<template>
  <plugin-panel
    id="source-code"
    title="页面 Schema"
    class="plugin-schema"
    :fixed-name="PLUGIN_NAME.Schema"
    :fixedPanels="fixedPanels"
    @close="close"
  >
    <template #header>
      <div class="schema-scroll-switch">
        <tiny-checkbox v-model="enableSchemaScroll" @change="schemaScrollControl"></tiny-checkbox>
        <span>跟随画布</span>
      </div>
      <span class="icon-wrap">
        <i v-show="!showRed" class="red"></i>
        <tiny-button type="primary" @click="saveSchema">保存</tiny-button>
      </span>
      <tiny-popover v-show="false" placement="bottom" trigger="hover" append-to-body content="导入 Schema">
        <template #reference>
          <span class="icon-wrap">
            <icon-download-link></icon-download-link>
          </span>
        </template>
      </tiny-popover>
    </template>

    <template #content>
      <div class="source-code-content">
        <monaco-editor
          ref="container"
          class="code-edit-content"
          :value="state.pageData"
          :options="options"
          @change="editorChange"
          @shortcutSave="saveSchema"
        ></monaco-editor>
      </div>
      <div class="source-code-footer">
        <button>导入 Schema</button>
      </div>
    </template>
  </plugin-panel>
</template>

<script lang="tsx">
/* metaService: engine.plugins.schema.Main */
import { nextTick, reactive, getCurrentInstance, onActivated, ref, onDeactivated, provide, watch } from 'vue'
import type { Component } from 'vue'
import { Popover, Button, Checkbox as TinyCheckbox } from '@opentiny/vue'
import { VueMonaco, PluginPanel } from '@opentiny/tiny-engine-common'
import { useCanvas, useModal, useNotify, useMessage, useLayout } from '@opentiny/tiny-engine-meta-register'
import { utils } from '@opentiny/tiny-engine-utils'
import { iconDownloadLink } from '@opentiny/vue-icon'
import { useThrottleFn } from '@vueuse/core'

const { reactiveObj2String: obj2String, string2Obj } = utils

export default {
  components: {
    MonacoEditor: VueMonaco,
    TinyPopover: Popover as Component,
    TinyButton: Button as Component,
    TinyCheckbox: TinyCheckbox as Component,
    PluginPanel,
    IconDownloadLink: iconDownloadLink()
  },
  inheritAttrs: false,
  props: {
    fixedPanels: {
      type: Array
    }
  },
  setup(props, { emit }) {
    const app = getCurrentInstance()
    const { pageState } = useCanvas()
    const { confirm } = useModal()
    const state = reactive({
      pageData: obj2String(pageState.pageSchema)
    })
    const { subscribe, unsubscribe } = useMessage()

    const { PLUGIN_NAME } = useLayout()

    const panelState = reactive({
      emitEvent: emit
    })
    provide('panelState', panelState)

    const isEdit = false
    const showRed = ref(true)

    const enableSchemaScroll = ref(false)
    const isSchemaScrollActive = ref(false)
    const highlightField = ref([])

    const close = () => {
      const strs = app.refs.container.getEditor().getValue()
      const isChanged = state.pageData === strs

      if (!isChanged) {
        confirm({
          title: '提示',
          message: '有改动未保存,您确定关闭吗?',
          exec: () => emit('close')
        })
      } else {
        emit('close')
      }
    }
    const editorChange = (val) => {
      showRed.value = val === obj2String(state.pageData)
    }

    const saveSchema = () => {
      const editorValue = string2Obj(app.refs.container.getEditor().getValue())
      if (!editorValue) {
        // schema 解析不正确,作废此次保存
        useNotify({
          type: 'error',
          title: 'schema 保存失败',
          message: 'schema 解析异常,请确保格式正确'
        })

        return
      }

      // 不允许修改 componentName,因为修改 componentName 等同于修改页面类型
      const value = {
        ...editorValue,
        componentName: pageState.pageSchema.componentName
      }

      const { importSchema, setSaved } = useCanvas()

      importSchema(value)
      setSaved(false)

      // TODO: 历史堆栈
      // useHistory().addHistory()
      state.pageData = ''

      nextTick(() => {
        state.pageData = obj2String(value)
        emit('close')
      })
    }

    const throttleUpdateData = useThrottleFn(
      () => {
        state.pageData = obj2String(pageState.pageSchema)
      },
      100,
      true
    )

    const getCurrentSchemaLine = () => {
      const currentSchema = pageState.currentSchema
      if (state.pageData && currentSchema) {
        // 获取currentSchema的id行到首行的距离
        const currentSchemaString = obj2String(currentSchema)
        const index = currentSchemaString!.indexOf(`"id": "${currentSchema.id}"`)
        const currentSchemaIdLineNumber = currentSchemaString!.substring(0, index).match(/\n/g)?.length
        // 获取页面schema首行到currentSchema的id行的距离
        const currentSchemaIndex = state.pageData.indexOf(`"id": "${currentSchema.id}"`)
        const schemaIdLineNumber = state.pageData.substring(0, currentSchemaIndex).match(/\n/g)?.length
        return schemaIdLineNumber && currentSchemaIdLineNumber
          ? schemaIdLineNumber - currentSchemaIdLineNumber + 1
          : null
      }
      return null
    }

    const clearHighlight = () => {
      highlightField.value = app.refs.container.getEditor().deltaDecorations(highlightField.value, [])
    }

    const highlightFirstLine = () => {
      if (!isSchemaScrollActive.value || !enableSchemaScroll.value) {
        clearHighlight()
        return
      }
      const startLine = getCurrentSchemaLine()
      if (!startLine) {
        return
      }
      const editor = app.refs.container.getEditor()
      const monaco = app.refs.container.getMonaco()
      // 清除上次的高亮行
      clearHighlight()
      highlightField.value = editor.deltaDecorations(highlightField.value, [
        {
          range: new monaco.Range(startLine + 1, 1, startLine + 1, editor.getModel().getLineMaxColumn(startLine + 1)),
          options: {
            inlineClassName: 'current-code-highlight'
          }
        }
      ])
      editor.revealLineInCenter(startLine)
    }

    const schemaScrollControl = () => {
      if (enableSchemaScroll.value) {
        highlightFirstLine()
      } else {
        clearHighlight()
      }
    }

    onActivated(() => {
      state.pageData = obj2String(pageState.pageSchema)
      isSchemaScrollActive.value = true
      nextTick(() => {
        window.dispatchEvent(new Event('resize'))
        showRed.value = state.pageData === app.refs.container.getEditor().getValue()
        // 高亮当前行
        highlightFirstLine()
      })

      subscribe({
        topic: 'schemaChange',
        subscriber: 'schema-plugin',
        callback: throttleUpdateData
      })
    })

    onDeactivated(() => {
      isSchemaScrollActive.value = false
      clearHighlight()
      unsubscribe({
        topic: 'schemaChange',
        subscriber: 'schema-plugin'
      })
    })

    watch(
      () => pageState.currentSchema,
      () => {
        highlightFirstLine()
      }
    )

    return {
      PLUGIN_NAME,
      state,
      isEdit,
      enableSchemaScroll,
      schemaScrollControl,
      saveSchema,
      editorChange,
      close,
      showRed,
      options: {
        language: 'json',
        // readOnly: !pageState.isLock,  暂时放开schema录入功能,等画布功能完善后,再打开此注释
        readOnly: false,
        minimap: {
          enabled: false
        }
      }
    }
  }
}
</script>

<style lang="less" scoped>
#source-code {
  border-right: none;
  box-shadow: 6px 0px 3px 0px var(--te-schema-panel-shadow-color);
  z-index: 1000;
  .schema-scroll-switch {
    display: flex;
    align-items: center;
    margin-right: 12px;
    font-weight: normal;
  }

  .icon-wrap {
    position: relative;
    margin-right: 6px;

    .tiny-button {
      min-width: 40px;
      margin-right: 2px;
      height: 24px;
      line-height: 24px;
    }

    .red {
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background-color: var(--te-schema-dot-color);
      display: block;
      z-index: 100;
      position: absolute;
      top: -3px;
      right: -1px;
    }
  }

  .source-code-content {
    height: calc(100% - 12px);
    border: 1px solid var(--te-schema-common-border-color);
    border-radius: 4px;
    margin: 0 12px;
  }
  .code-edit-content {
    height: 100%;
  }
  .source-code-footer {
    display: none;
    justify-content: flex-end;
    padding: 12px 0;
    button {
      padding: 12px;
      border: none;
      border-radius: 4px;
      color: var(--te-schema-btn-color);
      background: var(--te-schema-btn-bg-color);
      cursor: pointer;
    }
  }
}
:deep(.current-code-highlight) {
  background: var(--te-common-bg-info);
}
</style>