import { createStore } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
import type {
  DominoState,
  SceneElement,
  Viewport,
  Snapshot,
  PlaceholderElement,
  Padding,
  GroupElement,
  ArtboardElement,
} from './domino'
import { getElementWorldRect } from './math'

export function createSnapshot(
  elements: Record<string, SceneElement>,
  elementOrder: string[],
): Snapshot {
  // We keep the full elementOrder to preserve placeholder positions
  const filteredElements = { ...elements }
  return { elements: filteredElements, elementOrder: [...elementOrder] }
}

export interface CanvasActions {
  setViewport: (viewport: Partial<Viewport>) => void
  addElement: (element: SceneElement) => void
  removeElement: (id: string) => void
  updateElement: (id: string, updates: Partial<SceneElement>) => void
  updateElementUIState: (
    id: string,
    updates: Partial<DominoState['elementUIStates'][string]>,
  ) => void
  setSelectedIds: (ids: string[]) => void
  moveViewport: (dx: number, dy: number) => void
  zoomViewport: (
    scaleMultiplier: number,
    centerX: number,
    centerY: number,
    minScale?: number,
    maxScale?: number,
  ) => void
  moveElements: (ids: string[], dx: number, dy: number) => void
  setSelectionBox: (box: DominoState['selectionBox']) => void
  setMode: (mode: 'select' | 'pan') => void
  clearElements: (initialViewport?: Partial<Viewport>) => void
  setFocusedElementId: (id: string | null) => void
  setHoveredElementId: (id: string | null) => void
  setSnapLines: (lines: DominoState['snapLines']) => void
  moveElementUp: (id: string) => void
  moveElementDown: (id: string) => void
  moveElementToTop: (id: string) => void
  moveElementToBottom: (id: string) => void
  setPadding: (padding: Partial<Padding>) => void

  undo: () => void
  redo: () => void
  takeSnapshot: () => void
  resetHistory: () => void
  addPlaceholder: (element: PlaceholderElement) => void
  removePlaceholder: (id: string) => void
  updatePlaceholder: (id: string, updates: Partial<PlaceholderElement>) => void
  setElementsData: (
    elements: Record<string, SceneElement>,
    order: string[],
  ) => void
  setReadOnly: (readOnly: boolean) => void
  moveElementToParent: (
    id: string,
    targetParentId: string | null,
    targetIndex?: number,
  ) => void
}

export type DominoStoreState = DominoState & CanvasActions
export type DominoStore = ReturnType<typeof createDominoStore>

export function createDominoStore(initialState?: Partial<DominoState>) {
  return createStore<DominoStoreState>()(
    subscribeWithSelector(
      immer(set => ({
        elements: {},
        placeholders: {},
        elementUIStates: {},
        elementOrder: [],
        selectedIds: [],
        focusedElementId: null,
        hoveredElementId: null,
        selectionBox: null,
        metadata: {},
        mode: 'select',
        viewport: { x: 0, y: 0, scale: 0.5 },
        snapLines: null,
        padding: { top: 0, right: 0, bottom: 0, left: 0 },
        readOnly: false,
        past: [],
        future: [],
        ...initialState,

        setViewport: viewport =>
          set(state => {
            const hasChange = Object.entries(viewport).some(
              ([key, value]) => state.viewport[key as keyof Viewport] !== value,
            )
            if (!hasChange) return
            state.viewport = { ...state.viewport, ...viewport }
          }),

        setElementsData: (
          elements: Record<string, SceneElement>,
          order: string[],
        ) =>
          set(state => {
            state.elements = elements
            state.elementOrder = order
          }),

        moveViewport: (dx, dy) =>
          set(state => {
            state.viewport.x += dx
            state.viewport.y += dy
          }),

        zoomViewport: (
          multiplier,
          centerX,
          centerY,
          minScale = 0.1,
          maxScale = 20,
        ) =>
          set(state => {
            const oldScale = state.viewport.scale
            const newScale = Math.max(
              minScale,
              Math.min(maxScale, oldScale * multiplier),
            )

            state.viewport.x =
              centerX - (centerX - state.viewport.x) * (newScale / oldScale)
            state.viewport.y =
              centerY - (centerY - state.viewport.y) * (newScale / oldScale)
            state.viewport.scale = newScale
          }),

        takeSnapshot: () =>
          set(state => {
            if (state.readOnly) return
            state.past.push(createSnapshot(state.elements, state.elementOrder))
            if (state.past.length > 100) {
              state.past.shift()
            }
            state.future = []
          }),

        undo: () =>
          set(state => {
            if (state.readOnly || state.past.length === 0) return
            const previous = state.past.pop()
            if (previous) {
              state.future.push(
                createSnapshot(state.elements, state.elementOrder),
              )

              // Revert elements
              state.elements = previous.elements

              // Smart merge elementOrder:
              // We want to keep placeholders that are currently active
              const currentPlaceholders = { ...state.placeholders }
              state.elementOrder = previous.elementOrder.filter(
                id => state.elements[id] || currentPlaceholders[id],
              )

              // Add back any active placeholders that weren't in the snapshot's order
              Object.keys(currentPlaceholders).forEach(pid => {
                if (!state.elementOrder.includes(pid)) {
                  state.elementOrder.push(pid)
                }
              })
            }
          }),

        redo: () =>
          set(state => {
            if (state.readOnly) return
            const next = state.future.pop()
            if (next) {
              state.past.push(
                createSnapshot(state.elements, state.elementOrder),
              )

              // Revert elements
              state.elements = next.elements

              // Smart merge elementOrder to preserve active placeholders
              const currentPlaceholders = { ...state.placeholders }
              state.elementOrder = next.elementOrder.filter(
                id => state.elements[id] || currentPlaceholders[id],
              )

              // Ensure current placeholders are still in the order
              Object.keys(currentPlaceholders).forEach(pid => {
                if (!state.elementOrder.includes(pid)) {
                  state.elementOrder.push(pid)
                }
              })
            }
          }),

        resetHistory: () =>
          set(state => {
            state.past = []
            state.future = []
          }),

        addElement: element =>
          set(state => {
            if (state.readOnly || state.elements[element.id]) return

            // Record history before change
            state.past.push(createSnapshot(state.elements, state.elementOrder))
            state.future = []
            if (state.past.length > 100) state.past.shift()

            state.elements[element.id] = element

            if (!element.parentId) {
              if (!state.elementOrder.includes(element.id)) {
                state.elementOrder.push(element.id)
              }
            } else {
              const parent = state.elements[element.parentId]
              if (
                parent &&
                (parent.type === 'artboard' || parent.type === 'group')
              ) {
                if (!parent.childrenIds.includes(element.id)) {
                  parent.childrenIds.push(element.id)
                }
              }
            }
          }),

        addPlaceholder: element =>
          set(state => {
            if (state.elements[element.id] || state.placeholders[element.id])
              return

            state.placeholders[element.id] = element

            if (!element.parentId) {
              if (!state.elementOrder.includes(element.id)) {
                state.elementOrder.push(element.id)
              }
            } else {
              const parent =
                state.elements[element.parentId] ||
                state.placeholders[element.parentId]
              if (
                parent &&
                (parent.type === 'artboard' || parent.type === 'group')
              ) {
                if (!parent.childrenIds.includes(element.id)) {
                  parent.childrenIds.push(element.id)
                }
              }
            }
          }),

        removeElement: id =>
          set(state => {
            if (state.readOnly) return
            const element = state.elements[id]
            if (!element) return

            // Record history
            state.past.push(createSnapshot(state.elements, state.elementOrder))
            state.future = []
            if (state.past.length > 100) state.past.shift()

            const { parentId } = element
            delete state.elements[id]
            state.elementOrder = state.elementOrder.filter(oid => oid !== id)

            if (parentId) {
              const parent = state.elements[parentId]
              if (
                parent &&
                (parent.type === 'artboard' || parent.type === 'group')
              ) {
                parent.childrenIds = parent.childrenIds.filter(
                  (cid: string) => cid !== id,
                )
              }
            }
            if (state.selectedIds.includes(id)) {
              state.selectedIds = state.selectedIds.filter(sid => sid !== id)
            }
            if (state.focusedElementId === id) {
              state.focusedElementId = null
            }
            delete state.elementUIStates[id]
          }),

        removePlaceholder: id =>
          set(state => {
            const element = state.placeholders[id]
            if (!element) return

            const { parentId } = element
            delete state.placeholders[id]
            state.elementOrder = state.elementOrder.filter(oid => oid !== id)

            if (parentId) {
              const parent =
                state.elements[parentId] || state.placeholders[parentId]
              if (
                parent &&
                (parent.type === 'artboard' || parent.type === 'group')
              ) {
                parent.childrenIds = parent.childrenIds.filter(
                  (cid: string) => cid !== id,
                )
              }
            }
            if (state.selectedIds.includes(id)) {
              state.selectedIds = state.selectedIds.filter(sid => sid !== id)
            }
            if (state.focusedElementId === id) {
              state.focusedElementId = null
            }
            delete state.elementUIStates[id]
          }),

        updateElement: (id, updates) =>
          set(state => {
            if (state.readOnly) return
            const element = state.elements[id]
            if (element) {
              state.elements[id] = {
                ...element,
                ...updates,
              } as SceneElement
            }
          }),

        updatePlaceholder: (id, updates) =>
          set(state => {
            if (state.placeholders[id]) {
              state.placeholders[id] = {
                ...state.placeholders[id],
                ...updates,
              }
            }
          }),

        updateElementUIState: (id, updates) =>
          set(state => {
            if (!state.elementUIStates[id]) {
              state.elementUIStates[id] = { status: 'idle' }
            }
            state.elementUIStates[id] = {
              ...state.elementUIStates[id],
              ...updates,
            }
          }),

        setSelectedIds: ids =>
          set(state => {
            state.selectedIds = ids
          }),

        moveElements: (ids, dx, dy) =>
          set(state => {
            if (state.readOnly) return
            ids.forEach(id => {
              const el = state.elements[id]
              if (el) {
                el.x += dx
                el.y += dy
              }
            })
          }),

        setSelectionBox: box =>
          set(state => {
            state.selectionBox = box
          }),

        setMode: mode =>
          set(state => {
            state.mode = mode
          }),

        clearElements: (initialViewport?: Partial<Viewport>) =>
          set(state => {
            // Record history if not already empty
            if (state.elementOrder.length > 0) {
              state.past.push(
                createSnapshot(state.elements, state.elementOrder),
              )
              state.future = []
              if (state.past.length > 100) state.past.shift()
            }

            state.elements = {}
            state.placeholders = {}
            state.elementUIStates = {}
            state.elementOrder = []
            state.selectedIds = []
            state.viewport = { x: 0, y: 0, scale: 0.5, ...initialViewport }
            state.mode = 'select'
            state.focusedElementId = null
            state.hoveredElementId = null
          }),

        setFocusedElementId: (id: string | null) =>
          set(state => {
            state.focusedElementId = id
          }),

        setHoveredElementId: (id: string | null) =>
          set(state => {
            state.hoveredElementId = id
          }),
        setSnapLines: lines =>
          set(state => {
            state.snapLines = lines
          }),
        moveElementUp: (id: string) =>
          set(state => {
            if (state.readOnly) return
            const element = state.elements[id]
            if (!element) return

            const orderArray = element.parentId
              ? (
                  state.elements[element.parentId] as
                    | ArtboardElement
                    | GroupElement
                )?.childrenIds
              : state.elementOrder

            if (!orderArray) return

            const index = orderArray.indexOf(id)
            if (index > -1 && index < orderArray.length - 1) {
              // Record history
              state.past.push(
                createSnapshot(state.elements, state.elementOrder),
              )
              state.future = []
              if (state.past.length > 100) state.past.shift()

              const temp = orderArray[index]
              orderArray[index] = orderArray[index + 1]
              orderArray[index + 1] = temp
            }
          }),
        moveElementDown: (id: string) =>
          set(state => {
            if (state.readOnly) return
            const element = state.elements[id]
            if (!element) return

            const orderArray = element.parentId
              ? (
                  state.elements[element.parentId] as
                    | ArtboardElement
                    | GroupElement
                )?.childrenIds
              : state.elementOrder

            if (!orderArray) return

            const index = orderArray.indexOf(id)
            if (index > 0) {
              // Record history
              state.past.push(
                createSnapshot(state.elements, state.elementOrder),
              )
              state.future = []
              if (state.past.length > 100) state.past.shift()

              const temp = orderArray[index]
              orderArray[index] = orderArray[index - 1]
              orderArray[index - 1] = temp
            }
          }),
        moveElementToTop: (id: string) =>
          set(state => {
            if (state.readOnly) return
            const element = state.elements[id]
            if (!element) return

            const orderArray = element.parentId
              ? (
                  state.elements[element.parentId] as
                    | ArtboardElement
                    | GroupElement
                )?.childrenIds
              : state.elementOrder

            if (!orderArray) return

            const index = orderArray.indexOf(id)
            if (index > -1 && index < orderArray.length - 1) {
              // Record history
              state.past.push(
                createSnapshot(state.elements, state.elementOrder),
              )
              state.future = []
              if (state.past.length > 100) state.past.shift()

              orderArray.splice(index, 1)
              orderArray.push(id)
            }
          }),
        moveElementToBottom: (id: string) =>
          set(state => {
            if (state.readOnly) return
            const element = state.elements[id]
            if (!element) return

            const orderArray = element.parentId
              ? (
                  state.elements[element.parentId] as
                    | ArtboardElement
                    | GroupElement
                )?.childrenIds
              : state.elementOrder

            if (!orderArray) return

            const index = orderArray.indexOf(id)
            if (index > 0) {
              // Record history
              state.past.push(
                createSnapshot(state.elements, state.elementOrder),
              )
              state.future = []
              if (state.past.length > 100) state.past.shift()

              orderArray.splice(index, 1)
              orderArray.unshift(id)
            }
          }),

        setPadding: padding =>
          set(state => {
            const hasChange = Object.entries(padding).some(
              ([key, value]) => state.padding[key as keyof Padding] !== value,
            )
            if (!hasChange) return
            state.padding = { ...state.padding, ...padding }
          }),
        setReadOnly: readOnly =>
          set(state => {
            if (state.readOnly === readOnly) return
            state.readOnly = readOnly
          }),

        moveElementToParent: (id, targetParentId, targetIndex) =>
          set(state => {
            if (state.readOnly) return
            const element = state.elements[id]
            if (!element || element.parentId === targetParentId) return

            // 记录历史快照
            state.past.push(createSnapshot(state.elements, state.elementOrder))
            state.future = []
            if (state.past.length > 100) state.past.shift()

            // 1. 从当前父级中移除
            if (!element.parentId) {
              state.elementOrder = state.elementOrder.filter(oid => oid !== id)
            } else {
              const oldParent = state.elements[element.parentId] as
                | ArtboardElement
                | GroupElement
              if (oldParent && oldParent.childrenIds) {
                oldParent.childrenIds = oldParent.childrenIds.filter(
                  (cid: string) => cid !== id,
                )
              }
            }

            // 2. 计算新坐标以维持世界坐标位置不变
            const worldRect = getElementWorldRect(element, state.elements)
            let newX = worldRect.x
            let newY = worldRect.y

            if (targetParentId) {
              const newParent = state.elements[targetParentId]
              if (newParent) {
                const parentWorld = getElementWorldRect(
                  newParent,
                  state.elements,
                )
                newX -= parentWorld.x
                newY -= parentWorld.y
              }
            }

            // 3. 更新父级关联和局部坐标
            element.parentId = targetParentId || undefined
            element.x = newX
            element.y = newY

            // 4. 将其添加到新父级的子元素列表或顶层列表中
            if (!targetParentId) {
              if (!state.elementOrder.includes(id)) {
                if (typeof targetIndex === 'number') {
                  state.elementOrder.splice(targetIndex, 0, id)
                } else {
                  state.elementOrder.push(id)
                }
              }
            } else {
              const newParent = state.elements[targetParentId] as
                | ArtboardElement
                | GroupElement
              if (newParent && newParent.childrenIds) {
                if (!newParent.childrenIds.includes(id)) {
                  if (typeof targetIndex === 'number') {
                    newParent.childrenIds.splice(targetIndex, 0, id)
                  } else {
                    newParent.childrenIds.push(id)
                  }
                }
              }
            }
          }),
      })),
    ),
  )
}
