import { computed, defineComponent, PropType, provide, ref, watch } from 'vue'
import deepcopy from 'deepcopy'
import FormBlock from './components/FormBlock'
import { registerConfig } from './utils/edit-config'
import { useMenuDragger } from './hooks/useMenuDragger'
import { useFocus } from './hooks/useFocus'
import { useBlockDragger } from './hooks/useBlockDragger'
import { useCommand } from './hooks/useCommand'
import { $dialog } from './components/Dialog'
import { $dropdown, DropdownItem } from './components/Dropdown'
import { ElButton } from 'element-plus'
import EditorOperator from './components/EditorOperator'
import { isNumber } from 'skzz-utils'

export interface IDfOpts {
  container: {
    width: string | number
    height: string | number
  }
  blocks: Array<IBlock>
}

export interface IBlock {
  top: number
  left: number
  zIndex: number
  key: string
  focus: boolean
  props: IBlockProps | null
}

export interface IBlockProps{
  text: string
  color: string
  size: string
}

export default defineComponent({
  name: 'SkDynamicForm',
  components: { FormBlock, EditorOperator },
  emits: ['update:modelValue'],
  props: {
    modelValue: { type: Object as PropType<IDfOpts> },
    formData: { type: Object },
    edit: {
      type: Boolean,
      default: true,
    },
  },
  setup(props, ctx) {

    const config = registerConfig
    provide('config', config)
    const data = computed<IDfOpts>({
      get() {
        return props.modelValue
      },
      set (newValue) {
        ctx.emit('update:modelValue', deepcopy(newValue))
      },
    })

    const containerStyles = computed(() => {
      const isWNumber = isNumber(Number(data.value.container.width))
      const isHNumber = isNumber(Number(data.value.container.height))
      return {
        width: isWNumber ? data.value.container.width + 'px' : data.value.container.width,
        height: isHNumber ? data.value.container.height + 'px' : data.value.container.height,
      }
    })

    const previewRef = ref(false)
    const containerRef = ref(null)
    const editorRef = ref(true)
    watch(() => props.edit, v => editorRef.value = v)
    // 1.实现菜单的拖拽功能
    const { dragstart, dragend } = useMenuDragger(containerRef, data)
    // 2.实现获取焦点 选中后可能直接就进行拖拽了
    const {
      blockMousedown,
      focusData,
      containerMousedown,
      lastSelectBlock,
      clearBlockFocus,
    } = useFocus(data, previewRef, e => {
      // 获取焦点后进行拖拽
      mousedown(e)
    })
    // 2.实现组件拖拽
    const { mousedown, markLine } = useBlockDragger(focusData, lastSelectBlock, data, containerRef)


    const { commands } = useCommand(data, focusData)
    const buttons = [
      { label: '撤销', icon: 'skicon-back', handler: () => commands.undo() },
      { label: '重做', icon: 'skicon-forward', handler: () => commands.redo() },
      {
        label: '导出', icon: 'skicon-export', handler: () => {
          $dialog({
            title: '导出json使用',
            content: JSON.stringify(data.value),
          })
        },
      },
      {
        label: '导入', icon: 'skicon-import', handler: () => {
          $dialog({
            title: '导入json使用',
            content: '',
            footer: true,
            onConfirm(text) {
              //data.value = JSON.parse(text); // 这样去更改无法保留历史记录
              commands.updateContainer(JSON.parse(text))
            },
          })
        },
      },
      { label: '置顶', icon: 'skicon-place-top', handler: () => commands.placeTop() },
      { label: '置底', icon: 'skicon-place-bottom', handler: () => commands.placeBottom() },
      { label: '删除', icon: 'skicon-delete', handler: () => commands.delete() },
      {
        label: () => previewRef.value ? '编辑' : '预览',
        icon: () => previewRef.value ? 'skicon-edit' : 'skicon-browse',
        handler: () => {
          previewRef.value = !previewRef.value
          clearBlockFocus()
        },
      },
      {
        label: '关闭', icon: 'skicon-close', handler: () => {
          editorRef.value = false
          clearBlockFocus()
        },
      },
    ]
    const onContextMenuBlock = (e, block) => {
      e.preventDefault()
      $dropdown({
        el: e.target, // 以哪个元素为准产生一个dropdown
        content: () => {
          return <>
            <DropdownItem label="删除" icon="skicon-delete" onClick={() => commands.delete()}></DropdownItem>
            <DropdownItem label="置顶" icon="skicon-place-top" onClick={() => commands.placeTop()}></DropdownItem>
            <DropdownItem label="置底" icon="skicon-place-bottom" onClick={() => commands.placeBottom()}></DropdownItem>
            <DropdownItem label="查看" icon="skicon-browse" onClick={() => {
              $dialog({
                title: '查看节点数据',
                content: JSON.stringify(block),
              })
            }}></DropdownItem>
            <DropdownItem label="导入" icon="skicon-import" onClick={() => {
              $dialog({
                title: '导入节点数据',
                content: '',
                footer: true,
                onConfirm(text) {
                  text = JSON.parse(text)
                  commands.updateBlock(text, block)
                },
              })
            }}></DropdownItem>
          </>
        },
      })
    }

    return () => !editorRef.value ?
      <>
        <div
          class="sk-editor-container-canvas__content"
          style={{ ...containerStyles.value, margin: 0 }}
        >
          {
            (data.value.blocks.map((block, index) => (
              <FormBlock
                class='sk-editor-block-preview'
                block={block}
                formData={props.formData}
              ></FormBlock>
            )))
          }
        </div>
        <div>
          <ElButton type="primary" onClick={() => editorRef.value = true}>继续编辑</ElButton>
          {JSON.stringify(props.formData)}
        </div>
      </>
      :
      <div class="sk-editor">
        <div class="sk-editor-top">
          {
            buttons.map((btn, index) => {
              const icon = typeof btn.icon == 'function' ? btn.icon() : btn.icon
              const label = typeof btn.label == 'function' ? btn.label() : btn.label
              return <div class="sk-editor-top-button" onClick={btn.handler}>
                <i class={icon}></i>
                <span>{label}</span>
              </div>
            })
          }
        </div>
        <div class="sk-editor-left">
          {/* 根据注册列表 渲染对应的内容  可以实现h5的拖拽*/}
          {config.componentList.map(component => (
            <div
              class="sk-editor-left-item"
              draggable
              onDragstart={e => dragstart(e, component)}
              onDragend={dragend}
            >
              <span>{component.label}</span>
              <div>{component.preview()}</div>
            </div>
          ))}
        </div>
        <div class="sk-editor-right">
          <EditorOperator
            block={lastSelectBlock.value}
            data={data.value}
            updateContainer={commands.updateContainer}
            updateBlock={commands.updateBlock}
          ></EditorOperator>
        </div>
        <div class="sk-editor-container">
          {/*  负责产生滚动条 */}
          <div class="sk-editor-container-canvas">
            {/* 产生内容区域 */}
            <div
              class="sk-editor-container-canvas__content"
              style={containerStyles.value}
              ref={containerRef}
              onMousedown={containerMousedown}
            >
              {
                (data.value.blocks.map((block, index) => (
                  <FormBlock
                    block={block}
                    class={{ 'sk-editor-block-focus': block.focus, 'sk-editor-block-preview': previewRef.value }}
                    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                    // @ts-ignore
                    onMousedown={e => blockMousedown(e, block, index)}
                    onContextmenu={e => onContextMenuBlock(e, block)}
                    formData={props.formData}
                  ></FormBlock>
                )))
              }
              {markLine.x !== null && <div class="sk-line-x" style={{ left: markLine.x + 'px' }}></div>}
              {markLine.y !== null && <div class="sk-line-y" style={{ top: markLine.y + 'px' }}></div>}
            </div>
          </div>
        </div>
      </div>
  },
})
