<template>
  <div class="relative !h-full w-full" ref="el" />
</template>

<script lang="ts" setup>
  import { ref, onMounted, onUnmounted, watchEffect, watch, nextTick } from 'vue';
  import { useDebounceFn } from '@vueuse/core';
  import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';

  import { basicSetup, EditorView } from 'codemirror';
  import { EditorState, Compartment } from '@codemirror/state';
  import { java } from '@codemirror/lang-java';
  import { javascript } from '@codemirror/lang-javascript';

  const props = defineProps({
    mode: {
      type: String,
      default: 'javascript',
    },
    value: { type: String, default: '' },
    readonly: { type: Boolean, default: false },
  });

  const emit = defineEmits(['change']);

  const el = ref();
  let editor;
  let timer;

  const debounceRefresh = useDebounceFn(refresh, 100);

  watch(
    () => props.value,
    async value => {
      await nextTick();
      const oldValue = editor?.state.doc.toString();
      if (value !== oldValue && value != '[object Object]') {
        // editor?.setValue(value ? value : '');
        editor.dispatch({
          changes: { from: 0, insert: value },
        });
      }
    },
    { flush: 'post' },
  );

  watchEffect(() => {
    editor?.setOption('mode', props.mode);
  });

  // watch(
  //   () => appStore.getDarkMode,
  //   async () => {
  //     setTheme();
  //   },
  //   {
  //     immediate: true,
  //   },
  // );

  // function setTheme() {
  //   unref(editor)?.setOption(
  //     'theme',
  //     appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight',
  //   );
  // }

  function refresh() {
    editor?.refresh();
  }

  async function init() {
    const language = new Compartment();
    const tabSize = new Compartment();
    const state = EditorState.create({
      doc: '',
      extensions: [
        basicSetup,
        language.of(props.mode == 'javascript' ? javascript() : java()),
        tabSize.of(EditorState.tabSize.of(4)),
        EditorView.updateListener.of(v => {
          if (v.docChanged) {
            if (timer) clearTimeout(timer);
            timer = setTimeout(() => {
              try {
                const doc = v.state.toJSON()?.doc;
                const removeLine = doc.replace(/\n/g, '');
                const json = JSON.parse(removeLine || '{}');
                emit('change', json);
              } catch (error) {
                emit('change', v.state.toString());
              }
            }, 500);
          }
        }),
      ],
    });
    editor = new EditorView({
      state,
      parent: el.value!,
    });
    editor.dispatch({
      changes: { from: 0, insert: props.value },
    });
  }

  onMounted(async () => {
    await nextTick();
    init();
    useWindowSizeFn(debounceRefresh);
  });

  onUnmounted(() => {
    editor = null;
  });
</script>
