<script setup lang="ts">
import type { editor } from 'monaco-editor'

import { nextTick, onMounted, ref, watch } from 'vue'
import * as monaco from 'monaco-editor'
import { IconButton } from '@repo/ui'

const props = withDefaults(
    defineProps<{
        allowFullscreen?: boolean
        autoToggleTheme?: boolean
        bordered?: boolean
        config?: editor.IStandaloneEditorConstructionOptions
        language?: string
        lineNumbers?: 'off' | 'on'
        modelValue?: any
        readOnly?: boolean
        theme?: 'hc-black' | 'vs-dark' | 'vs-light'
        valueFormat?: string
        height?: string
        appendTo?: string | HTMLElement
    }>(),
    {
        allowFullscreen: true,
        config: () => ({
            minimap: {
                enabled: false
            },
            selectOnLineNumbers: true
        }),
        autoToggleTheme: true,
        language: 'json',
        lineNumbers: 'on',
        readOnly: false,
        theme: 'vs-light',
        valueFormat: 'string',
        height: '120px'
    }
)

// 动态切换主题
const theme = ref('dark')

const emit = defineEmits(['update:modelValue', 'update:language'])

const isFullScreen = ref(false)

const fullScreenStyle = `position: fixed;
  top: 0;
  left: 0;      
  right: 0;
  bottom: 0;
  z-index: 2999;`

const editContainer = ref<HTMLElement | null>(null)

let monacoEditor: monaco.editor.IStandaloneCodeEditor | null = null

/**
 * 设置文本
 * @param text
 */
function setValue(text: string) {
    monacoEditor?.setValue(text || '')
}

/**
 * 光标处插入文本
 * @param text
 */
function insertText(text: string) {
    // 获取光标位置
    const position = monacoEditor?.getPosition()
    // 未获取到光标位置信息
    if (!position) {
        return
    }
    // 插入
    monacoEditor?.executeEdits('', [
        {
            range: new monaco.Range(
                position.lineNumber,
                position.column,
                position.lineNumber,
                position.column
            ),
            text
        }
    ])
    // 设置新的光标位置
    monacoEditor?.setPosition({
        ...position,
        column: position.column + text.length
    })
    // 重新聚焦
    monacoEditor?.focus()
}

onMounted(() => {
    monacoEditor = monaco.editor.create(editContainer.value as HTMLElement, {
        value: getValue(),
        ...props.config,
        automaticLayout: true,
        language: props.language,
        lineNumbers: props.lineNumbers,
        readOnly: props.readOnly,
        scrollBeyondLastLine: false,
        theme: props.theme
    })

    function handleToggleTheme() {
        if (theme.value === 'dark') {
            monaco.editor.setTheme('vs-dark')
        } else {
            monaco.editor.setTheme('vs-light')
        }
    }

    // 自动切换主题
    if (props.autoToggleTheme) {
        watch(
            () => theme,
            () => {
                nextTick(() => handleToggleTheme())
            },
            {
                immediate: true
            }
        )
    }

    // 获取值
    function getValue() {
        // valueFormat 为json 格式，需要转换处理
        if (props.valueFormat === 'json' && props.modelValue) {
            return JSON.stringify(props.modelValue, null, 2)
        }
        return props.modelValue ?? ''
    }

    // 监听值变化
    monacoEditor.onDidChangeModelContent(() => {
        const currenValue = monacoEditor?.getValue()

        // valueFormat 为json 格式，需要转换处理
        if (props.valueFormat === 'json' && currenValue) {
            emit('update:modelValue', JSON.parse(currenValue))
            return
        }

        emit('update:modelValue', currenValue ?? '')
    })
})

defineExpose({
    insertText,
    setValue
})
</script>
<template>
    <Teleport :disabled="!appendTo" :to="appendTo">
        <div ref="editContainer" :class="{ bordered: props.bordered }"
            :style="isFullScreen ? fullScreenStyle : { height: props.height }"
            class="code-editor w-full h-full relative">
            <div class="z-999 text-$epic-text-helper absolute right-4 top-2 cursor-pointer text-xl"
                @click="isFullScreen = !isFullScreen" v-if="props.allowFullscreen">
                <IconButton v-if="!isFullScreen" icon="lucide:fullscreen" link />
                <IconButton v-else icon="material-symbols-light:fullscreen-exit-rounded" link />
            </div>
        </div>
    </Teleport>
</template>
<style lang="less" scoped>
.code-editor {
    width: 100%;
    min-height: 150px;

    :deep(.monaco-editor) {
        height: 100%;
    }

    &.bordered {
        border: 1px solid var(--epic-border-color);
    }
}
</style>
