<script setup lang="ts">
import type { CodeEditorProps } from './CodeTypes'
import { onMounted, ref, computed, watch } from 'vue'
import { useHighlight } from './useHighlight'

// Props with default values
const props = withDefaults(defineProps<CodeEditorProps>(), {
	language: 'html',
	readonly: false,
	modelValue: '',
	autodetect: true,
	ignoreIllegals: true,
	label: '',
	height: '240px'
})

const emit = defineEmits(['update:modelValue'])
// State
const code = ref(props.modelValue || '')
const editorRef = ref<HTMLDivElement>()
const { loaded, loadHighlight, highlightCode } = useHighlight()

// Computed
const maxHeight = computed(() => {
	const h = props.height ?? '240px'
	return h.match(/^\d+$/) ? `${h}px` : h
})

const formattedCode = computed(() => {
	return highlightCode(
		code.value,
		props.language,
		props.autodetect,
		props.ignoreIllegals
	)
})

function handleInput(e: Event) {
	const target = e.target as HTMLTextAreaElement
	code.value = target.value
	emit('update:modelValue', code.value)
}

function handleTab(event: KeyboardEvent) {
	if (event.key !== 'Tab') return

	event.preventDefault()
	const target = event.target as HTMLTextAreaElement
	const start = target.selectionStart
	const end = target.selectionEnd

	// Add tab or indent selected text
	const newValue = `${code.value.substring(0, start)}  ${code.value.substring(end)}`
	code.value = newValue
	emit('update:modelValue', code.value)

	// Move cursor position after the inserted tab
	setTimeout(() => {
		target.selectionStart = target.selectionEnd = start + 2
	}, 0)
}

// Lifecycle
onMounted(async () => {
	await loadHighlight()
})

// Watch for external modelValue changes
watch(() => props.modelValue, (newVal) => {
	if (newVal !== undefined && newVal !== code.value) {
		code.value = newVal
	}
}, { immediate: true })
</script>

<template>
	<div class="code-editor-container ltr" :style="{ maxHeight }">
		<label v-if="label" class="label">{{ label }}</label>
		<div
			v-if="loaded"
			ref="editorRef"
			class="code-editor-grandpa"
		>
			<div class="editor-content-papa relative">
				<pre class="code-display" wrap><code v-html="formattedCode" /></pre>
				<textarea
					v-if="!readonly"
					:value="code"
					class="code-input"
					spellcheck="false"
					autocomplete="off"
					autocorrect="off"
					autocapitalize="off"
					@input="handleInput"
					@keydown="handleTab"
				/>
			</div>
		</div>
	</div>
</template>

<style scoped>
.code-editor-container {
	margin-bottom: 0.5rem;
	height: 100%;
}

.label {
	display: block;
	text-align: left;
	margin-bottom: 0.25rem;
}

.code-editor-grandpa {
	background: #22252A;
	border-radius: 0.25rem;
	width: 100%;
	height: 100%;
	overflow: auto;
	padding: 1ch;
	padding-inline-start: 2ch;
}

.code-editor-grandpa:focus-within {
	outline: solid 1px var(--border-color, #4f575f);
	box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.13);
}

.editor-content-papa {
	position: relative;
	width: 100%;
	padding-bottom: calc(100% - 5lh);
}
.code-display,
.code-input {
	inset: 0;
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
	overflow: auto;
	font-family: monospace;
	font-size: 1em;
	line-height: 1.5;
	tab-size: 2;
	word-break: keep-all;
	text-align: left;
}

.code-display {
	position: relative;
	color:  #fff;
	pointer-events: none;
	z-index: 1;
}

.code-display code {
	display: block;
	background: transparent !important;
	padding: 0 !important;
}

.code-input {
	position: absolute;
	background: transparent;
	color: transparent;
	caret-color:  #fff;
	border: none;
	resize: none;
	outline: none;
	z-index: 2;
}

.code-input::selection {
	background-color: rgba(36, 102, 188, 0.3);
	color: transparent;
}
</style>

<style>
/* Global styles */
pre code.hljs {
	padding: 0 !important;
	background: transparent !important;
}
</style>
