<script setup lang="ts">
import { nextTick, onMounted, reactive, ref, watch, onBeforeUnmount, computed } from 'vue'
import { useLocalEditorTheme } from "@/utils/useLocalEditorTheme"
import type { editor } from 'monaco-editor'
import { ElMessage } from "element-plus"
import * as monaco from 'monaco-editor'
import { IconButton } from '@repo/ui'
import { debounce } from "lodash-es"

interface CodeEditorType {
    // 是否展示工具栏
    tools?: boolean
    // 是否展示全屏工具
    allowFullscreen?: boolean
    // 是否展示复制工具
    copyCode?: boolean
    // 是否自动切换主题
    autoToggleTheme?: boolean
    // 挂载DOM
    appendTo?: string | HTMLElement
    // 内容
    modelValue?: any
    // 语法  
    language?: string
    // 主题
    theme?: 'vs-light' | 'vs-dark'
    // 是否只读
    readOnly?: boolean
    // 是否显示行号
    lineNumbers?: 'on' | 'off'
    // 设置代码模块高度，总高度多40px
    height?: number
    // 返回结果为string,也可转化为json
    formatValue?: 'string' | 'json'
    // 插件内部配置
    config?: editor.IStandaloneEditorConstructionOptions

}

const props = withDefaults(defineProps<CodeEditorType>(),
    {
        //默认配置
        tools: true,
        copyCode: true,
        readOnly: false,
        allowFullscreen: true,
        autoToggleTheme: true,
        language: 'javascript',
        lineNumbers: 'on',
        theme: 'vs-light',
        formatValue: 'string',
        height: 150,
        config: () => ({
            minimap: { enabled: false },
            selectOnLineNumbers: true
        }),
    }
)


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

let monacoEditor: monaco.editor.IStandaloneCodeEditor | null = null
let model: editor.ITextModel | null = null
const editContainer = ref<HTMLElement>()

const isFullScreen = ref(false)
const {
    monacoTheme,
    themeClass,
    toggleTheme
} = useLocalEditorTheme({
    defaultTheme: props.theme,
    autoToggleTheme: props.autoToggleTheme
})

let componentConfig = reactive({
    language: props.language
})

const languageSource = [
    { id: 'javascript', name: 'javascript' },
    { id: 'python', name: 'python' },
    { id: 'json', name: 'json' }
]

/**
 * @description: formatValue为json 格式，需要转换处理
 * @return 
 */
const formatValue = (value: any) => {
    if (props.formatValue === 'json') {
        return JSON.stringify(value ?? {}, null, 2)
    }
    return value ?? ''
}

onMounted(() => {
    // 处理代码转换
    model = monaco.editor.createModel(
        formatValue(props.modelValue),
        componentConfig.language
    )

    // 接入配置
    monacoEditor = monaco.editor.create(editContainer.value!, {
        model,
        wordWrap: 'on',
        automaticLayout: true,
        theme: monacoTheme.value,
        readOnly: props.readOnly,
        lineNumbers: props.lineNumbers,
        ...props.config,
    })

    //  添加空格
    monacoEditor.onKeyDown((e) => {
        if (e.keyCode === monaco.KeyCode.Space) {
            e.preventDefault();
            monacoEditor?.trigger('keyboard', 'type', { text: ' ' });
        }
    });

    // 监听代码输入
    monacoEditor.onDidChangeModelContent(updateModelValue)

})


// 读取传值数据
watch(() => props.modelValue, (value) => {
    nextTick(() => {
        if (!model) return
        const next = formatValue(value)
        if (model.getValue() !== next) {
            model.setValue(next)
        }
    })
}, { immediate: true })


// 切换语法，触发回调
watch(() => componentConfig.language, (lang) => {
    nextTick(() => {
        if (!model) return
        monaco.editor.setModelLanguage(model, lang)
        emit('update:language', lang)
    })
}, { immediate: true })

// 监听全屏, 重新计算layout
watch(isFullScreen, () => {
    nextTick(() => {
        monacoEditor?.layout()
    })
})




/**
 * @description: 发送回调(formatValue:json，需要转换处理)
 * @return {*}
 */
const updateModelValue = debounce(() => {
    if (!model) return
    const value = model.getValue()

    if (props.formatValue === 'json') {
        try {
            emit('update:modelValue', JSON.parse(value))
        } catch {
            return ElMessage.warning('JSON 语法错误')
        }
    } else {
        emit('update:modelValue', value)
    }
}, 1000)


/**
 * @description: 复制代码到粘贴板
 * @return {*}
 */
const copyCode = () => {
    const text = model?.getValue()
    if (!text) return
    try {
        navigator.clipboard.writeText(text).then(() => {
            ElMessage.success('复制成功！')
        })
    } catch {
        const textarea = document.createElement('textarea')
        textarea.value = text
        document.body.appendChild(textarea)
        textarea.select()
        document.execCommand('copy')
        textarea.remove()
        ElMessage.success('复制成功！')
    }
}


/**
 * @description: 设置全屏样式
 * @return {*}
 */
const fullScreenStyle = computed(() => {
    if (isFullScreen.value) return {}

    return {
        height: `${props.height + 40}px`
    }
})
/**
 * @description: 切换主题， 并设置对应样式
 * @return {*}
 */
const toolTipClass = computed(() => {
    return `editor-tooltip editor-tooltip--${themeClass.value}`
})
const onToggleTheme = () => {
    toggleTheme()
}


/**
 * 设置文本
 * @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()
}

// 销毁model
onBeforeUnmount(() => {
    monacoEditor?.dispose()
    model?.dispose()
})

defineExpose({
    insertText,
    setValue
})
</script>
<template>
    <Teleport :disabled="!appendTo" :to="appendTo">
        <div class="monacoEditor !m-0" :class="[themeClass, { 'is-fullscreen': isFullScreen }]"
            :style="fullScreenStyle">

            <div class="tools  h-[33px] flex items-center justify-between gap-2" v-if="props.tools">
                <!-- 语法切换 -->
                <ElTooltip :effect="themeClass" :popper-class="toolTipClass" placement="top" content="切换语法">
                    <div class="w-1/3">

                        <ElSelect v-model="componentConfig.language">
                            <ElOption v-for="value in languageSource" :label="value.name" :value="value.id" />
                        </ElSelect>

                    </div>
                </ElTooltip>
                <div class="flex-1 flex items-center justify-end gap-1">
                    <!-- 放大/缩小 -->
                    <ElTooltip :effect="themeClass" placement="top" :popper-class="toolTipClass"
                        :content="!isFullScreen ? '放大' : '恢复正常'">
                        <div class="cursor-pointer text-xl text-center px-2" @click="isFullScreen = !isFullScreen"
                            v-if="props.allowFullscreen">
                            <IconButton :icon="isFullScreen ? 'lucide:minimize' : 'lucide:fullscreen'" link
                                class="fullscreen" />
                        </div>
                    </ElTooltip>

                    <!-- copy -->
                    <ElTooltip :effect="themeClass" :popper-class="toolTipClass" placement="top" content="复制">
                        <div class="copy text-center px-2" @click="copyCode" v-if="props.copyCode">
                            <IconButton icon="lucide:copy" link class="copyIcon"></IconButton>
                        </div>
                    </ElTooltip>

                    <!-- 主题 -->
                    <ElTooltip :effect="themeClass" :popper-class="toolTipClass" placement="top" content="主题">
                        <div class="copy text-center px-2" @click="onToggleTheme">
                            <IconButton :icon="themeClass === 'dark' ? 'lucide:moon' : 'lucide:sun'" link
                                class="themeIcon">
                            </IconButton>
                        </div>
                    </ElTooltip>
                </div>
            </div>
            <!-- 编辑器 -->
            <div class="editor-wrapper">
                <div ref="editContainer" class="code-editor"></div>
            </div>

        </div>
    </Teleport>
</template>
<style lang="less">
.editor-tooltip {
    border-radius: 6px;
    font-size: 12px;
    padding: 6px 10px;
}

// light
.editor-tooltip--light {
    background-color: #ffffff !important;
    color: #1e1e1e !important;
    border: 1px solid #e5e7eb;

    .el-popper__arrow::before {
        background: #ffffff !important;
        border: 1px solid #e5e7eb;
    }
}

// dark
.editor-tooltip--dark {
    background-color: #1e1e1e !important;
    color: #ffffff !important;

    .el-popper__arrow::before {
        background: #1e1e1e !important;
    }
}
</style>
<style lang="less" scoped>
.monacoEditor {
    border: 1px solid #eee;
    display: flex;
    flex-direction: column;
    transition: height 0.25s ease, inset 0.25s ease, background-color 0.2s;

    &.is-fullscreen {
        position: absolute;
        inset: 0;
        z-index: 1000;
    }

    .tools {
        border-bottom: 1px solid #eeeeee83;
        padding-bottom: 6px;

        ::v-deep(.el-select .el-select__wrapper) {
            border: none;
            box-shadow: none;
            background-color: #ffff;
        }

        ::v-deep(.el-select .el-select__placeholder) {
            color: #1e1e1e;
        }
    }

    .editor-wrapper {
        flex: 1;
        overflow: hidden;
    }

    .code-editor {
        width: 100%;
        height: 100%;

        ::v-deep(.monaco-editor) {
            height: 100%;
            background-color: #ffffff;
        }

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

}

.light {
    background-color: #ffffff;
    color: #1e1e1e;

    &:hover {
        color: #1e1e1e;
    }

    .tools {
        ::v-deep(.el-select .el-select__wrapper) {
            border: none;
            box-shadow: none;
            background-color: #ffffff;
        }

        ::v-deep(.el-select .el-select__placeholder) {
            color: #1e1e1e;
        }

        .fullscreen,
        .fullscreen-exit,
        .themeIcon,
        .copyIcon {
            color: #1e1e1e;
        }
    }
}

.dark {
    background-color: #1e1e1e;
    color: #ffffff;

    &:hover {
        color: #ffffff;
    }

    .tools {
        ::v-deep(.el-select .el-select__wrapper) {
            border: none;
            box-shadow: none;
            background-color: #1e1e1e;
        }

        ::v-deep(.el-select .el-select__placeholder) {
            color: #ffffff;
        }

        .fullscreen,
        .fullscreen-exit,
        .themeIcon,
        .copyIcon {
            color: #ffffff;
        }
    }
}
</style>
