<template>
  <div class="code-editor-container">
    <div ref="editorContainer" class="editor-container"></div>
    <div v-if="showMinimap" class="minimap"></div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import * as monaco from 'monaco-editor'
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'

// 配置 Monaco Worker
self.MonacoEnvironment = {
  getWorker(_, label) {
    if (label === 'json') return new jsonWorker()
    if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker()
    if (label === 'html' || label === 'handlebars' || label === 'razor') return new htmlWorker()
    if (label === 'typescript' || label === 'javascript') return new tsWorker()
    return new editorWorker()
  }
}

interface Props {
  modelValue: string
  language: 'csharp' | 'java' | 'cpp'
  readOnly?: boolean
  theme?: 'vs' | 'vs-dark' | 'hc-black'
  showMinimap?: boolean
  showLineNumbers?: boolean
  automaticLayout?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  readOnly: false,
  theme: 'vs-dark',
  showMinimap: true,
  showLineNumbers: true,
  automaticLayout: true
})

const emit = defineEmits<{
  (e: 'update:modelValue', value: string): void
  (e: 'change', value: string): void
  (e: 'cursorChange', position: { lineNumber: number; column: number }): void
}>()

const editorContainer = ref<HTMLElement | null>(null)
let editor: monaco.editor.IStandaloneCodeEditor | null = null

const languageMap: Record<string, string> = {
  csharp: 'csharp',
  java: 'java',
  cpp: 'cpp'
}

onMounted(() => {
  if (!editorContainer.value) return
  
  editor = monaco.editor.create(editorContainer.value, {
    value: props.modelValue,
    language: languageMap[props.language] || 'plaintext',
    theme: props.theme,
    readOnly: props.readOnly,
    minimap: { enabled: props.showMinimap },
    lineNumbers: props.showLineNumbers ? 'on' : 'off',
    automaticLayout: props.automaticLayout,
    fontSize: 14,
    wordWrap: 'on',
    scrollBeyondLastLine: false,
    renderWhitespace: 'selection',
    suggestOnTriggerCharacters: true,
    quickSuggestions: true,
    tabSize: 4,
    insertSpaces: true,
    formatOnPaste: true,
    formatOnType: true,
    autoClosingBrackets: 'always',
    autoClosingQuotes: 'always',
    bracketPairColorization: { enabled: true },
    glyphMargin: true,
    folding: true,
    foldingStrategy: 'indentation'
  })
  
  editor.onDidChangeModelContent(() => {
    const value = editor?.getValue() || ''
    emit('update:modelValue', value)
    emit('change', value)
  })
  
  editor.onDidChangeCursorPosition((e) => {
    emit('cursorChange', {
      lineNumber: e.position.lineNumber,
      column: e.position.column
    })
  })
  
  registerCustomLanguages()
})

onBeforeUnmount(() => {
  editor?.dispose()
})

watch(() => props.modelValue, (newValue) => {
  if (editor && newValue !== editor.getValue()) {
    editor.setValue(newValue)
  }
})

watch(() => props.language, (newLang) => {
  if (editor) {
    const model = editor.getModel()
    if (model) {
      monaco.editor.setModelLanguage(model, languageMap[newLang] || 'plaintext')
    }
  }
})

watch(() => props.theme, (newTheme) => {
  if (editor) {
    monaco.editor.setTheme(newTheme)
  }
})

const registerCustomLanguages = () => {
  monaco.languages.registerCompletionItemProvider('java', {
    provideCompletionItems: (model, position) => {
      const word = model.getWordUntilPosition(position)
      const range = {
        startLineNumber: position.lineNumber,
        endLineNumber: position.lineNumber,
        startColumn: word.startColumn,
        endColumn: word.endColumn
      }
      
      return {
        suggestions: [
          {
            label: 'public static void main',
            kind: monaco.languages.CompletionItemKind.Keyword,
            insertText: 'public static void main(String[] args) {\n\t$0\n}',
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range
          },
          {
            label: 'System.out.println',
            kind: monaco.languages.CompletionItemKind.Function,
            insertText: 'System.out.println($0);',
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range
          }
        ]
      }
    }
  })
  
  monaco.languages.registerCompletionItemProvider('csharp', {
    provideCompletionItems: (model, position) => {
      const word = model.getWordUntilPosition(position)
      const range = {
        startLineNumber: position.lineNumber,
        endLineNumber: position.lineNumber,
        startColumn: word.startColumn,
        endColumn: word.endColumn
      }
      
      return {
        suggestions: [
          {
            label: 'Console.WriteLine',
            kind: monaco.languages.CompletionItemKind.Function,
            insertText: 'Console.WriteLine($0);',
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range
          }
        ]
      }
    }
  })
}

const setDecorations = (decorations: monaco.editor.IModelDeltaDecoration[]) => {
  if (!editor) return
  const model = editor.getModel()
  if (!model) return
  editor.createDecorationsCollection(decorations)
}

const setValue = (value: string) => {
  editor?.setValue(value)
}

const getValue = (): string => {
  return editor?.getValue() || ''
}

const focus = () => {
  editor?.focus()
}

const layout = () => {
  editor?.layout()
}

defineExpose({
  setDecorations,
  setValue,
  getValue,
  focus,
  layout
})
</script>

<style scoped>
.code-editor-container {
  width: 100%;
  height: 100%;
  display: flex;
  position: relative;
}

.editor-container {
  flex: 1;
  height: 100%;
  min-height: 300px;
}

.minimap {
  width: 50px;
  background: rgba(0, 0, 0, 0.1);
}
</style>
