import { ref } from 'vue'
import { defineStore } from 'pinia'

import { bfsWalk } from 'simple-mind-map/src/utils'
import { moveToPosition } from '@/utils'
import { klona } from 'klona'
import { v4 } from 'uuid'

import { useKeyPress } from 'vue-hooks-plus'
import { useProjectStore } from '@/store/modules/project'
import { useAppStore } from './app'
import { debounce } from 'lodash-es'

import type { BaseWidget } from '@/types/baseWidget'
import type { Page } from '@/types/page'

type AlignType =
  | 'left'
  | 'right'
  | 'top'
  | 'bottom'
  | 'vcenter'
  | 'hcenter'
  | 'vspace'
  | 'hcenter'
  | 'vspace'
  | 'hspace'
export const useActionStore = defineStore('action', () => {
  const projectStore = useProjectStore()
  const appStore = useAppStore()
  const clipboard = ref<BaseWidget[]>()

  const isLockedWidget = (widget?: BaseWidget | Page) => {
    return !!(widget && 'locked' in widget && widget.locked)
  }

  const hasLockedDescendant = (widget?: BaseWidget | Page): boolean => {
    return !!widget?.children?.some((child) => isLockedWidget(child) || hasLockedDescendant(child))
  }

  const isWidgetTreeLocked = (widget?: BaseWidget | Page) => {
    return isLockedWidget(widget) || hasLockedDescendant(widget)
  }

  const getEditableWidgets = (widgets = projectStore.activeWidgets) => {
    return widgets.filter((widget) => !isWidgetTreeLocked(widget))
  }

  const findWidgetLocation = (
    parent: Page | BaseWidget,
    widgetId: string,
    ancestors: Array<Page | BaseWidget> = [parent]
  ) => {
    const children = parent.children || []
    for (let index = 0; index < children.length; index++) {
      const widget = children[index]
      if (widget.id === widgetId) {
        return {
          widget,
          parent,
          list: children,
          index,
          ancestors
        }
      }

      if (widget.children?.length) {
        const found = findWidgetLocation(widget, widgetId, [...ancestors, widget])
        if (found) {
          return found
        }
      }
    }
  }

  const findWidgetLocationInProject = (widgetId: string) => {
    for (const screen of projectStore.project?.screens || []) {
      for (const page of screen.pages) {
        const location = findWidgetLocation(page, widgetId)
        if (location) {
          return {
            ...location,
            page,
            screen
          }
        }
      }
    }
  }

  const getUnbindOffset = (ancestors: Array<Page | BaseWidget>) => {
    return ancestors.reduce(
      (acc, item) => {
        if ('props' in item) {
          acc.x += Number(item.props?.x || 0)
          acc.y += Number(item.props?.y || 0)
        }
        return acc
      },
      {
        x: 0,
        y: 0
      }
    )
  }

  const isWidgetBoundById = (widgetId: string) => {
    const location = findWidgetLocationInProject(widgetId)
    return !!location && location.parent.type !== 'page'
  }

  /**
   * 对齐组件
   * @param type 对齐类型
   */
  const onAlign = (type: AlignType) => {
    const widgets = getEditableWidgets()
    if (!widgets.length) return
    const screen = projectStore.activeScreen
    switch (type) {
      case 'left': {
        let minX = Math.min(...widgets.map((widget) => widget.props.x))
        if (widgets.length === 1) minX = 0
        widgets.forEach((widget) => {
          widget.props.x = minX
        })
        break
      }

      case 'right': {
        let maxX = Math.max(...widgets.map((widget) => widget.props.x + widget.props.width))
        if (widgets.length === 1) maxX = screen?.width ?? maxX
        widgets.forEach((widget) => {
          widget.props.x = maxX - widget.props.width
        })
        break
      }

      case 'top': {
        let minY = Math.min(...widgets.map((widget) => widget.props.y))
        if (widgets.length === 1) minY = 0
        widgets.forEach((widget) => {
          widget.props.y = minY
        })
        break
      }

      case 'bottom': {
        let maxY = Math.max(...widgets.map((widget) => widget.props.y + widget.props.height))
        if (widgets.length === 1) maxY = screen?.height ?? maxY
        widgets.forEach((widget) => {
          widget.props.y = maxY - widget.props.height
        })
        break
      }

      case 'vcenter': {
        const maxY = Math.max(...widgets.map((widget) => widget.props.y + widget.props.height))
        const minY = Math.min(...widgets.map((widget) => widget.props.y))
        let centerY = minY + (maxY - minY) / 2
        if (widgets.length === 1) centerY = screen?.height ? screen.height / 2 : centerY
        widgets.forEach((widget) => {
          widget.props.y = centerY - widget.props.height / 2
        })
        break
      }

      case 'hcenter': {
        const maxX = Math.max(...widgets.map((widget) => widget.props.x + widget.props.width))
        const minX = Math.min(...widgets.map((widget) => widget.props.x))
        let centerX = minX + (maxX - minX) / 2
        if (widgets.length === 1) centerX = screen?.width ? screen.width / 2 : centerX
        widgets.forEach((widget) => {
          widget.props.x = centerX - widget.props.width / 2
        })
        break
      }
      // 水平平均分布
      case 'hspace': {
        const maxX = Math.max(...widgets.map((widget) => widget.props.x + widget.props.width))
        const minX = Math.min(...widgets.map((widget) => widget.props.x))

        // 排序
        const list = widgets.sort((a, b) => a.props.x - b.props.x)
        // 计算gap
        const gap = (maxX - minX - list.reduce((a, b) => a + b.props.width, 0)) / (list.length - 1)
        // flag
        let start = minX

        list.forEach((widget) => {
          widget.props.x = start
          start += widget.props.width + gap
        })
        break
      }

      // 垂直平均分布
      case 'vspace': {
        const maxY = Math.max(...widgets.map((widget) => widget.props.y + widget.props.height))
        const minY = Math.min(...widgets.map((widget) => widget.props.y))

        // 排序
        const list = widgets.sort((a, b) => a.props.y - b.props.y)
        // 计算gap
        const gap = (maxY - minY - list.reduce((a, b) => a + b.props.height, 0)) / (list.length - 1)
        // flag
        let start = minY

        list.forEach((widget) => {
          widget.props.y = start
          start += widget.props.height + gap
        })
        break
      }
    }
  }

  /**
   * 匹配宽高
   * @param type 宽高匹配类型
   */
  const onMatchSize = (type: 'width' | 'height' | 'both') => {
    const widgets = getEditableWidgets()
    if (!widgets.length) return
    switch (type) {
      case 'width': {
        const width = Math.max(...widgets.map((widget) => widget.props.width))
        widgets.forEach((widget) => {
          widget.props.width = width
        })
        break
      }

      case 'height': {
        const height = Math.max(...widgets.map((widget) => widget.props.height))
        widgets.forEach((widget) => {
          widget.props.height = height
        })
        break
      }

      case 'both': {
        const width = Math.max(...widgets.map((widget) => widget.props.width))
        const height = Math.max(...widgets.map((widget) => widget.props.height))
        widgets.forEach((widget) => {
          widget.props.width = width
          widget.props.height = height
        })
        break
      }
    }
  }

  /**
   * 层级调整
   * @param type 层级调整类型
   */
  const onLevel = (type: 'up' | 'down' | 'top' | 'bottom') => {
    let widgetIds = getEditableWidgets().map((widget) => widget.id)
    if (!widgetIds.length) return

    bfsWalk(projectStore.activePage, (child) => {
      // 对子节点遍历排序
      child?.children?.forEach((item, index) => {
        if (widgetIds.includes(item.id)) {
          switch (type) {
            case 'up': {
              if (index > 0) {
                moveToPosition(child.children, index, index - 1)
              }
              break
            }
            case 'down': {
              if (index < child.children.length - 1) {
                moveToPosition(child.children, index, index + 1)
              }
              break
            }
            case 'top': {
              if (index > 0) {
                moveToPosition(child.children, index, 0)
              }
              break
            }
            case 'bottom': {
              if (index < child.children.length - 1) {
                moveToPosition(child.children, index, child.children.length - index)
              }
              break
            }
          }
          // 跑完一轮 隐藏位置调整的
          widgetIds = widgetIds.filter((id) => id !== item.id)
        }
      })
    })
  }

  /**
   * 删除页面
   * @param pageId 页面ID
   */
  const onDeletePage = (pageId: string) => {
    projectStore.project?.screens.forEach((screen) => {
      screen.pages = screen.pages.filter((page) => page.id !== pageId)
    })
  }

  /**
   * 根据控件ID删除控件
   * @param widgetId 控件ID
   */
  const onDeleteById = (widgetId: string) => {
    projectStore.project?.screens.forEach((screen) => {
      screen.pages.forEach((page) => {
        bfsWalk(page, (child) => {
          const index = child?.children?.findIndex((item) => item.id === widgetId) ?? -1
          if (index !== -1 && !isWidgetTreeLocked(child.children[index])) {
            child.children.splice(index, 1)
          }
        })
      })
    })
  }

  /**
   * 删除控件
   */
  const onDelete = () => {
    const widgetIds = getEditableWidgets().map((widget) => widget.id)
    if (!widgetIds.length) return

    projectStore.project?.screens.forEach((screen) => {
      screen.pages.forEach((page) => {
        bfsWalk(page, (child) => {
          const indexs =
            child?.children?.map((item, index) => {
              if (widgetIds.includes(item.id)) return index
            }) ?? []
          if (indexs.length) {
            child.children = child.children.filter((_item, index) => !indexs.includes(index))
          }
        })
      })
    })
    projectStore.activeWidgets = projectStore.activeWidgets.filter((widget) => widget.locked)
  }

  /**
   * 复制
   *
   */
  const onCopy = () => {
    clipboard.value = klona(projectStore.activeWidgets)
  }

  /**
   * 复用
   */
  const onCopyFrom = () => {
    const widgetIds = getEditableWidgets().map((widget) => widget.id)
    if (!widgetIds.length) return

    bfsWalk(projectStore.activePage, (child) => {
      const obj = child?.children?.find((item) => widgetIds.includes(item.id))
      if (obj) {
        const newWidget = klona(obj)
        newWidget.id = v4()
        if (!newWidget.isCopy) {
          newWidget.isCopy = true
          projectStore.project?.widgets?.push(newWidget)
        }
        obj.copyFrom = newWidget.id
        obj.isCopy = true
        // 复制一份复用的
        child.children.push({
          ...newWidget,
          id: v4()
        })
      }
    })
  }

  /**
   * 锁定/解锁
   */
  const onLock = (lock: boolean) => {
    const widgetIds = projectStore.activeWidgets.map((widget) => widget.id)
    if (!widgetIds.length) return

    bfsWalk(projectStore.activePage, (child) => {
      const obj = child?.children?.find((item) => widgetIds.includes(item.id))
      if (obj) {
        obj.locked = lock
      }
    })
  }

  /**
   * 粘贴
   */
  const onPaste = () => {
    if (!clipboard.value?.length) return

    const target = projectStore.activeWidgets?.[0]
    const list = !isWidgetTreeLocked(target)
      ? target?.children || projectStore.activePage?.children || []
      : projectStore.activePage?.children || []
    const newArr: BaseWidget[] = []
    clipboard.value.forEach((obj) => {
      obj.props.x += 10
      obj.props.y += 10
      const newWidget = klona({
        ...obj,
        id: v4(),
        events: []
      })
      list.push(newWidget)
      newArr.push(newWidget)
    })
    projectStore.setSelectWidgets(newArr)
  }

  /**
   * 剪切
   */
  const onCut = () => {
    const widgetIds = getEditableWidgets().map((widget) => widget.id)
    if (!widgetIds.length) return
    clipboard.value = []
    bfsWalk(projectStore.activePage, (child) => {
      const index = child?.children?.findIndex((item) => widgetIds.includes(item.id)) ?? -1
      if (index != -1) {
        clipboard.value?.push(child.children[index])
        child.children.splice(index, 1)
      }
    })
    projectStore.activeWidgets = projectStore.activeWidgets.filter((widget) => widget.locked)
  }

  /**
   * 隐藏/显示
   */
  const onHidden = (hidden: boolean) => {
    const widgetIds = getEditableWidgets().map((widget) => widget.id)
    if (!widgetIds.length) return

    bfsWalk(projectStore.activePage, (child) => {
      const obj = child?.children?.find((item) => widgetIds.includes(item.id))
      if (obj) {
        obj.hidden = hidden
      }
    })
  }

  /**
   * 全选
   */
  const onSelectAll = () => {
    projectStore.setSelectWidgets(projectStore.activePage?.children || [])
  }

  /**
   * 解绑控件，将其从容器内部释放到页面根节点末尾
   */
  const onUnbind = (widgetIds = projectStore.activeWidgets.map((widget) => widget.id)) => {
    if (!widgetIds.length) return

    const movedWidgets: BaseWidget[] = []
    let targetPageId = ''
    let targetScreenId = ''

    widgetIds.forEach((widgetId) => {
      const location = findWidgetLocationInProject(widgetId)
      if (!location || location.parent.type === 'page') return
      if (isWidgetTreeLocked(location.widget)) return

      const { widget, list, index, ancestors, page, screen } = location
      const offset = getUnbindOffset(ancestors.slice(1))

      list.splice(index, 1)
      widget.props.x = Number(widget.props?.x || 0) + offset.x
      widget.props.y = Number(widget.props?.y || 0) + offset.y
      page.children.push(widget)
      movedWidgets.push(widget)
      targetPageId = page.id
      targetScreenId = screen.id
    })

    if (movedWidgets.length) {
      if (targetPageId) {
        projectStore.activePageId = targetPageId
      }
      if (targetScreenId && targetPageId) {
        const screenIndex =
          projectStore.project?.screens.findIndex((screen) => screen.id === targetScreenId) ?? -1
        if (screenIndex !== -1) {
          projectStore.openPageIds[screenIndex] = targetPageId
        }
      }
      projectStore.setSelectWidgets(movedWidgets)
    }
  }

  /**
   * 显示事件
   */
  const onShowEvent = () => {
    appStore.showComposite = true
    appStore.compositeTabAcitve = 'event'
  }

  // 键盘事件 复制
  useKeyPress(
    ['ctrl.c'],
    debounce((e: KeyboardEvent) => {
      // 忽略打开弹窗
      // 忽略输入框
      if (
        (e.target as HTMLElement)?.className.includes('el-') ||
        (e.target as HTMLElement)?.tagName === 'INPUT' ||
        (e.target as HTMLElement)?.tagName === 'TEXTAREA'
      )
        return
      onCopy()
    }, 100)
  )
  // 键盘事件 粘贴
  useKeyPress(
    ['ctrl.v'],
    debounce((e: KeyboardEvent) => {
      // 忽略输入框
      if (
        (e.target as HTMLElement)?.className.includes('el-') ||
        (e.target as HTMLElement)?.tagName === 'INPUT' ||
        (e.target as HTMLElement)?.tagName === 'TEXTAREA'
      )
        return
      onPaste()
    }, 100)
  )
  // 键盘事件 剪切
  useKeyPress(
    ['ctrl.x'],
    debounce((e: KeyboardEvent) => {
      // 忽略输入框
      if (
        (e.target as HTMLElement)?.className.includes('el-') ||
        (e.target as HTMLElement)?.tagName === 'INPUT' ||
        (e.target as HTMLElement)?.tagName === 'TEXTAREA'
      )
        return
      onCut()
    }, 100)
  )
  // 键盘事件 复用
  useKeyPress(
    ['ctrl.d'],
    debounce((e: KeyboardEvent) => {
      // 忽略输入框
      if (
        (e.target as HTMLElement)?.className.includes('el-') ||
        (e.target as HTMLElement)?.tagName === 'INPUT' ||
        (e.target as HTMLElement)?.tagName === 'TEXTAREA'
      )
        return
      onCopyFrom()
    }, 100)
  )
  // 键盘事件 删除
  useKeyPress(
    ['delete'],
    debounce((e: KeyboardEvent) => {
      // 忽略输入框
      if (
        (e.target as HTMLElement)?.className.includes('el-') ||
        (e.target as HTMLElement)?.tagName === 'INPUT' ||
        (e.target as HTMLElement)?.tagName === 'TEXTAREA'
      )
        return
      onDelete()
    }, 100)
  )
  // 键盘事件 撤销
  useKeyPress(
    ['ctrl.z'],
    debounce((e: KeyboardEvent) => {
      // 忽略输入框
      if (
        (e.target as HTMLElement)?.className.includes('el-') ||
        (e.target as HTMLElement)?.tagName === 'INPUT' ||
        (e.target as HTMLElement)?.tagName === 'TEXTAREA'
      )
        return
      // 阻止ctrl.shift.z同时触发
      if (e.ctrlKey && e.shiftKey) return
      projectStore.undo()
    }, 100)
  )
  // 键盘事件 重做
  useKeyPress(
    ['ctrl.shift.z'],
    debounce(() => {
      projectStore.redo()
    }, 100)
  )
  // 键盘事件 隐藏
  useKeyPress(
    ['ctrl.h'],
    debounce(() => {
      onHidden(true)
    }, 100)
  )
  // 键盘事件 锁定
  useKeyPress(
    ['ctrl.l'],
    debounce((e: KeyboardEvent) => {
      // 阻止ctrl.z同时触发
      if (e.ctrlKey && e.shiftKey) return
      onLock(true)
    }, 100)
  )
  // 键盘事件 锁定
  useKeyPress(
    ['ctrl.shift.l'],
    debounce(() => {
      onLock(false)
    }, 100)
  )
  // 键盘事件 上移
  useKeyPress(
    ['ctrl.up'],
    debounce((e: KeyboardEvent) => {
      // 阻止ctrl.z同时触发
      if (e.ctrlKey && e.shiftKey) return
      onLevel('up')
    }, 100)
  )
  // 键盘事件 下移
  useKeyPress(
    ['ctrl.down'],
    debounce((e: KeyboardEvent) => {
      // 阻止ctrl.z同时触发
      if (e.ctrlKey && e.shiftKey) return
      onLevel('down')
    }, 100)
  )
  // 键盘事件 置顶
  useKeyPress(
    ['ctrl.shift.up'],
    debounce(() => {
      onLevel('top')
    }, 100)
  )
  // 键盘事件 置底
  useKeyPress(
    ['ctrl.shift.down'],
    debounce(() => {
      onLevel('bottom')
    }, 100)
  )
  // 键盘事件 全选
  useKeyPress(
    ['ctrl.a'],
    debounce((e: KeyboardEvent) => {
      // 忽略输入框
      if (
        (e.target as HTMLElement)?.className.includes('el-') ||
        (e.target as HTMLElement)?.tagName === 'INPUT' ||
        (e.target as HTMLElement)?.tagName === 'TEXTAREA'
      )
        return

      onSelectAll()
    }, 100)
  )
  // 保存
  useKeyPress(
    ['ctrl.s'],
    debounce(() => {
      if (projectStore.project) {
        projectStore.saveProject()
      }
    }, 100)
  )

  return {
    onAlign,
    onMatchSize,
    onLevel,
    onDeletePage,
    onDelete,
    onDeleteById,
    onCopy,
    onCopyFrom,
    onLock,
    onPaste,
    onCut,
    onUnbind,
    onHidden,
    onShowEvent,
    onSelectAll,
    isWidgetBoundById
  }
})
