import React, { useEffect, useState } from 'react'
import {
  DndContext,
  DragOverlay,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragStartEvent,
  DragEndEvent,
} from '@dnd-kit/core'
import {
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable'
import { MenuItemComponent } from './MenuItemComponent'
import { MenuItem } from '@/@types/menu'
import { getPermissionsList } from '@/services/identity.service'
import { PermissionDefinitionRecord } from '@/proxy/admin/models'
import { SelectBoxOption } from '@/shared/types'

interface SortableMenuTreeProps {
  items: MenuItem[]
  onItemsChange: (items: MenuItem[]) => void
  isDesignMode: boolean
  refetch: () => void
}

export const SortableMenuTree: React.FC<SortableMenuTreeProps> = ({
  items,
  onItemsChange,
  isDesignMode,
  refetch,
}) => {
  const [permissions, setPermissions] = useState<SelectBoxOption[]>([])
  const [activeItem, setActiveItem] = React.useState<MenuItem | null>(null)

  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: {
        distance: 8,
      },
    }),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    }),
  )

  const flattenItems = (
    items: MenuItem[],
    parentPath: number[] = [],
  ): Array<{ item: MenuItem; path: number[] }> => {
    return items.flatMap((item, index) => {
      const path = [...parentPath, index]
      const self = { item, path }

      if (item.children?.length) {
        return [self, ...flattenItems(item.children, path)]
      }

      return [self]
    })
  }

  const findItemById = (items: MenuItem[], id: string): MenuItem | null => {
    for (const item of items) {
      if (item.id === id) return item
      if (item.children) {
        const found = findItemById(item.children, id)
        if (found) return found
      }
    }
    return null
  }

  const removeItemFromTree = (items: MenuItem[], id: string): MenuItem[] => {
    return items.reduce((acc: MenuItem[], item) => {
      if (item.id === id) return acc

      const newItem = { ...item }
      if (newItem.children && newItem.children.length > 0) {
        newItem.children = removeItemFromTree(newItem.children, id)
      }

      acc.push(newItem)
      return acc
    }, [])
  }

  const insertItemAtPath = (
    items: MenuItem[],
    item: MenuItem,
    targetPath: number[],
  ): MenuItem[] => {
    if (targetPath.length === 1) {
      const newItems = [...items]
      newItems.splice(targetPath[0], 0, item)
      return newItems
    }

    const [firstIndex, ...restPath] = targetPath
    const newItems = [...items]

    if (newItems[firstIndex]) {
      newItems[firstIndex] = {
        ...newItems[firstIndex],
        children: insertItemAtPath(newItems[firstIndex].children || [], item, restPath),
      }
    }

    return newItems
  }

  const getInsertionPath = (
    items: MenuItem[],
    activeId: string,
    overId: string,
  ): number[] | null => {
    const flat = flattenItems(items)
    const activeFlat = flat.find((f) => f.item.id === activeId)
    const overFlat = flat.find((f) => f.item.id === overId)

    if (!activeFlat || !overFlat) return null

    const isSameParent = activeFlat.path.slice(0, -1).join() === overFlat.path.slice(0, -1).join()

    const insertPath = [...overFlat.path]

    if (isSameParent) {
      const activeIndex = activeFlat.path[activeFlat.path.length - 2]
      const overIndex = overFlat.path[overFlat.path.length - 1]

      if (activeIndex < overIndex) {
        insertPath[insertPath.length - 1] += 1
      }
    }

    return insertPath
  }

  const updateOrderNumbers = (items: MenuItem[]): MenuItem[] => {
    return items.map((item, index) => ({
      ...item,
      order: index + 1,
      children: item.children ? updateOrderNumbers(item.children) : [],
    }))
  }

  const handleDragStart = (event: DragStartEvent) => {
    const { active } = event
    const activeItem = findItemById(items, active.id as string)
    setActiveItem(activeItem)
  }

  const handleDragEnd = (event: DragEndEvent) => {
    const { active, over } = event
    setActiveItem(null)

    if (!over || active.id === over.id || !isDesignMode) return

    const activeId = active.id as string
    const overId = over.id as string
    const activeItem = findItemById(items, activeId)
    if (!activeItem) return

    const insertionPath = getInsertionPath(items, activeId, overId)
    if (!insertionPath) return

    let newItems = removeItemFromTree(items, activeId)
    newItems = insertItemAtPath(newItems, activeItem, insertionPath)
    const finalItems = updateOrderNumbers(newItems)
    onItemsChange(finalItems)
  }

  useEffect(() => {
    const fetchPermissions = async () => {
      const response = await getPermissionsList()
      if (response.data) {
        setPermissions(
          response.data.map((p: PermissionDefinitionRecord) => ({
            value: p.name,
            label: p.name,
          })),
        )
      }
    }

    fetchPermissions()
  }, [])

  const renderMenuItem = (item: MenuItem, depth: number = 0): React.ReactNode => {
    return (
      <div key={item.id}>
        <MenuItemComponent item={item} isDesignMode={isDesignMode} depth={depth} refetch={refetch} permissions={permissions}>
          {Array.isArray(item.children) && item.children.length > 0 && (
            <SortableContext
              items={item.children
                .filter((child): child is MenuItem & { id: string } => !!child.id)
                .map((child) => child.id)}
              strategy={verticalListSortingStrategy}
            >
              <div className="ml-4">
                {item.children.map((child) => renderMenuItem(child, depth + 1))}
              </div>
            </SortableContext>
          )}
        </MenuItemComponent>
      </div>
    )
  }

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
    >
      <div className="space-y-1">{items.map((item) => renderMenuItem(item))}</div>

      <DragOverlay>
        {activeItem ? (
          <MenuItemComponent
            item={activeItem}
            isDesignMode={isDesignMode}
            depth={0}
            isDragOverlay={true}
            refetch={refetch}
            permissions={permissions}
          />
        ) : null}
      </DragOverlay>
    </DndContext>
  )
}
