<template>
	<div
		ref="root"
		class="tui-markdown-editor diselect"
		:class="{
			'fold': isFold,
			'code': code
		}"
	>
		<div class="editor">
			<div class="header">
				<slot name="editorHeader">
					<h4 class="title">
						<span>源码</span>
						<span class="light-gray word-space">Markdown</span>
					</h4>
				</slot>
			</div>
			<slot name="editor">
				<textarea ref="textArea" class="text-area" v-model="_data" @keydown.tab.prevent="onTab"></textarea>
			</slot>
		</div>
		<div class="preview" :class="{ 'showing': showingPreview }">
			<div class="header">
				<div
					v-if="isFold"
					class="icon cur-pointer"
					v-text="showingPreview ? '>' : '<'"
					@click="showingPreview = !showingPreview"
				>
				</div>
				<slot name="previewHeader">
					<h4 class="title">预览</h4>
				</slot>
			</div>
			<div ref="previewContent" class="content">
				<markdown-view v-if="code" :code="`${code}:${_data}`" />
				<markdown-view v-else :content="_data" />
			</div>
		</div>
	</div>
</template>

<script lang="ts" setup>
import { MarkdownView } from "../markdown-view";
import calcHeight from "./CalcTextareaHeight";

defineOptions({
	name: "MarkdownEditor"
});

const props = withDefaults(defineProps<{
	code?: string;
	data?: string,
	minRows?: number,
	maxRows?: number
}>(), {
	data: "",
	minRows: 8,
	maxRows: 32
});
const { data, minRows, maxRows } = toRefs(props);

const _data = ref(data.value);
const textArea = ref<HTMLTextAreaElement>();
const textAreaHeight = ref(30);
const previewContent = ref<HTMLDivElement>();

const emit = defineEmits(["update:data"]);

watch(_data, () => {
	emit("update:data", _data.value);
	calcTextAreaHeight();
});
watch(data, () => {
	_data.value = data.value;
	calcTextAreaHeight();
});

// 自适应折叠预览
const root = ref<HTMLDivElement>();
const isFold = ref(false);
const showingPreview = ref(false);
onMounted(() => {
	if (root.value) {
		const foldObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
			if (entries && 0 < entries.length) {
				isFold.value = entries[0].contentRect.width < 650;
			}
		});
		foldObserver.observe(root.value);
	}
});

// 自适应高度
const calcTextAreaHeight = () => {
	if (textArea.value) {
		textAreaHeight.value = calcHeight(textArea.value, minRows.value, maxRows.value).height;
	}
};
onMounted(() => {
	calcTextAreaHeight();
	if (textArea.value) {
		const textareaHeightObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
			if (entries && 0 < entries.length) {
				if (previewContent.value) {
					previewContent.value.style.height = `calc(${entries[0].contentRect.height}px + 16px)`;
				}
			}
		});
		textareaHeightObserver.observe(textArea.value);
	}
});

// ---------- Tab 缩进 ----------

const TAB = "\t";

/** 处理 Tab / Shift+Tab 缩进 */
const onTab = (e: KeyboardEvent) => {
	const el = textArea.value!;
	const { selectionStart: start, selectionEnd: end, value } = el;
	const lines = value.split("\n");

	// 计算选区覆盖的行范围
	let charCount = 0;
	let startLine = 0;
	let endLine = 0;
	for (let i = 0; i < lines.length; i++) {
		const lineEnd = charCount + lines[i].length;
		if (charCount <= start && start <= lineEnd) startLine = i;
		if (charCount <= end && end <= lineEnd) endLine = i;
		charCount += lines[i].length + 1;
	}

	// 单行且无选区：直接插入 Tab
	if (!e.shiftKey && startLine === endLine && start === end) {
		_data.value = value.slice(0, start) + TAB + value.slice(end);
		nextTick(() => {
			el.selectionStart = el.selectionEnd = start + 1;
		});
		return;
	}

	// 多行或有选区：整行缩进 / 反缩进
	const newLines = lines.map((line, i) => {
		if (i < startLine || i > endLine) return line;
		if (e.shiftKey) return line.startsWith(TAB) ? line.slice(1) : line;
		return TAB + line;
	});

	// 计算新选区位置
	const delta = e.shiftKey ? -1 : 1;
	const affectedCount = endLine - startLine + 1;
	const newStart = Math.max(0, start + delta);
	const newEnd = Math.max(newStart, end + delta * affectedCount);

	_data.value = newLines.join("\n");
	nextTick(() => {
		el.selectionStart = newStart;
		el.selectionEnd = newEnd;
	});
};

defineExpose({
	textArea
});
</script>

<style lang="less" scoped>
.tui-markdown-editor {
	width: 100%;
	border: var(--tui-border);
	display: flex;
	overflow: hidden;
	position: relative;

	.header {
		display: flex;
		align-items: center;
		border-bottom: 1px solid var(--tui-dark-white);

		.icon {
			margin-left: .5rem;
		}
	}

	.title {
		margin: 0;
		height: 30px;
		display: flex;
		line-height: 30px;
		padding-left: 10px;
	}

	.editor {
		width: 50%;
		display: flex;
		border-right: var(--tui-border);
		flex-direction: column;

		.text-area {
			width: calc(100% - .5rem * 2);
			height: v-bind("textAreaHeight + 'px'");
			resize: none;
			border: none;
			outline: none;
			padding: .5rem;
			font-size: 14px;
			word-wrap: normal;
			white-space: nowrap;
		}
	}

	.preview {
		width: 50%;
		background: #FFF;

		.content {
			padding: .5rem 1rem;
			overflow-y: auto;
		}
	}

	&.fold {

		.editor {
			width: 100%;
		}

		.preview {
			left: calc(100% - 5rem);
			width: calc(100% - 2rem);
			height: 100%;
			z-index: 1;
			position: absolute;
			border-left: var(--tui-border);
			transition: left .5s var(--tui-bezier);
			box-shadow: -2px 0 0 var(--tui-shadow-color);

			&.showing {
				left: 2rem;
			}
		}
	}

	&.code {

		.preview {

			.content {
				padding: 0;

				:deep(pre[class*="language-"]) {
					margin: 0;
					border: none;
				}

			}
		}
	}
}
</style>
