<template>
  <div>
    <textarea ref="codeMirrorRef"></textarea>
  </div>
</template>
<script>
import { ref, onMounted, watch, markRaw, nextTick, computed } from 'vue'
import { getUsingConfig, isJson } from '@cip/utils/util'
import { generateProps, generateEmits } from '../helper/component-util'
import { componentScheme } from './component.scheme'
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/javascript-hint'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/edit/closebrackets'
import 'codemirror/theme/dracula.css'
import 'codemirror/theme/3024-day.css'

export default {
  name: 'CipCodeMirror',
  props: generateProps(componentScheme),
  emits: generateEmits(componentScheme),
  setup (props, { emit }) {
    const codeMirrorRef = ref()
    const codeMirror = ref()
    const _mode = computed(() => getUsingConfig(
      props.mode,
      props.type === 'json' ? 'application/json' : `text/${props.type}`))
    const getCodeMirrorInstance = () => {
      const instance = CodeMirror.fromTextArea(codeMirrorRef.value, {
        value: formatterValue(props.modelValue),
        mode: _mode.value,
        theme: props.theme,
        indentUnit: props.indentUnit,
        lineNumbers: props.lineNumbers,
        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)
        }
      })
      instance.on('inputRead', function () {
        instance.showHint({
          completeSingle: false
        })
      })
      return instance
    }
    const formatterValue = (val) => {
      if (props.type === 'json' && val) {
        let data = val
        if (typeof val === 'string' && isJson(data)) {
          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
        codeMirror.value.setValue(formatterValue(val) ?? '')
        nextTick(() => {
          codeMirror.value.refresh()
        })
      }, {
        immediate: true
      })
    })

    return {
      codeMirrorRef
    }
  }
}
</script>
