<template>
  <textarea ref="codeMirrorRef"></textarea>
</template>
<script>
import { ref, onMounted, watch, markRaw, nextTick } from 'vue'
import CodeMirror from 'codemirror'
import 'codemirror/lib/codemirror.css'
import 'codemirror/mode/javascript/javascript'
import 'codemirror/mode/xml/xml'
import 'codemirror/mode/sql/sql'
import 'codemirror/addon/hint/show-hint'
import 'codemirror/addon/hint/sql-hint'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/theme/dracula.css'
import 'codemirror/theme/3024-day.css'

export default {
  name: 'CipCodeMirror',
  inheritAttrs: false,
  props: {
    modelValue: [String, Object],
    readonly: {
      type: Boolean,
      default: false
    },
    type: {
      type: String,
      default: 'json'
    },
    height: {
      type: String,
      default: 'auto'
    },
    theme: { // 主题，需要额外引入
      type: String,
      default: 'dracula'
    }
  },
  emits: ['update:modelValue'],
  setup (props, { emit }) {
    const codeMirrorRef = ref()
    const codeMirror = ref()

    const getCodeMirrorInstance = () => {
      const instance = CodeMirror.fromTextArea(codeMirrorRef.value, {
        value: formatterValue(props.modelValue),
        mode: `text/${props.type === 'json' ? 'javascript' : props.type}`,
        theme: props.theme,
        indentUnit: 4,
        lineNumbers: true,
        lineWrapping: false,
        readOnly: props.readonly,
        singleCursorHeightPerLine: false,
        extraKeys: {}
      })
      instance.setSize(props.width ?? '100%', props.height ?? 'auto')
      instance.on('change', (instance, obj) => {
        const { origin } = obj
        const value = instance.getValue()
        codeMirror.value.save()
        if (origin !== 'setValue') { // 非setValue导致的更新才emit
          emit('update:modelValue', value)
        }
      })
      return instance
    }
    const formatterValue = (val) => {
      if (props.type === 'json' && val) {
        let data = val
        if (typeof val === 'string') {
          data = JSON.parse(data)
        }
        return JSON.stringify(data, null, 2) ?? ''
      } else {
        return val
      }
    }
    onMounted(() => {
      codeMirror.value = markRaw(getCodeMirrorInstance())
      watch(() => props.modelValue, (val, oldVal) => {
        const codeMirrorValue = codeMirror.value?.getValue()
        if (codeMirrorValue === val) return
        console.log(formatterValue(val))
        codeMirror.value.setValue(formatterValue(val))
        nextTick(() => {
          codeMirror.value.refresh()
        })
      }, {
        immediate: true
      })
    })

    return {
      codeMirrorRef
    }
  }
}
</script>
