"use client"

import React, { useCallback, useMemo, useState } from "react"
import {
  DndContext,
  pointerWithin,
  rectIntersection,
  CollisionDetection,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragOverlay,
  defaultDropAnimationSideEffects,
  DragStartEvent,
  DragOverEvent,
  DragEndEvent,
  DropAnimation,
  MeasuringStrategy,
} from "@dnd-kit/core"
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
  useSortable,
} from "@dnd-kit/sortable"
import { CSS } from "@dnd-kit/utilities"
import { Button } from "@/shared/components/ui/button"
import { Input } from "@/shared/components/ui/input"
import { Label } from "@/shared/components/ui/label"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/shared/components/ui/collapsible"
import { Trash2, GripVertical, ChevronDown, ChevronRight, Calculator } from "lucide-react"
import { cn } from "@/shared/lib/utils"
import { useTranslations } from "next-intl"
import type { ExamNode } from "./selected-question-list"

// --- Types ---

type StructureEditorProps = {
  items: ExamNode[]
  onChange: (items: ExamNode[]) => void
  onScoreChange: (id: string, score: number) => void
  onGroupTitleChange: (id: string, title: string) => void
  onRemove: (id: string) => void
  onAddGroup: () => void
  onAddSection?: () => void
}

function cloneExamNodes(nodes: ExamNode[]): ExamNode[] {
  return nodes.map((node) => {
    if (node.type === "group" || node.type === "section") {
      return { ...node, children: cloneExamNodes(node.children || []) }
    }
    return { ...node }
  })
}

// Safely extract a text preview from a question's content (which may be a string,
// object, or JSON string). Avoids `as` assertions by runtime narrowing.
const extractQuestionText = (raw: unknown): string => {
  if (!raw) return ""
  if (typeof raw === "string") {
    // Content might be a JSON string or plain text
    try {
      const parsed: unknown = JSON.parse(raw)
      if (parsed && typeof parsed === "object") {
        const obj = parsed as Record<string, unknown>
        return typeof obj.text === "string" ? obj.text : ""
      }
      return raw
    } catch {
      return raw
    }
  }
  if (typeof raw === "object") {
    const obj = raw as Record<string, unknown>
    return typeof obj.text === "string" ? obj.text : ""
  }
  return ""
}

// --- Components ---

function SortableItem({ 
  id, 
  item, 
  onRemove, 
  onScoreChange 
}: { 
  id: string
  item: ExamNode
  onRemove: () => void
  onScoreChange: (val: number) => void 
}) {
  const t = useTranslations("examHomework.exam.build")
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id })

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.5 : 1,
  }

  const rawContent = item.question?.content
  const parsedContent = (() => {
    if (rawContent && typeof rawContent === "object") return rawContent as { text?: string; options?: Array<{ id?: string; text?: string }> }
    if (typeof rawContent === "string") {
      try {
        const parsed = JSON.parse(rawContent) as unknown
        if (parsed && typeof parsed === "object") return parsed as { text?: string; options?: Array<{ id?: string; text?: string }> }
        return { text: rawContent }
      } catch {
        return { text: rawContent }
      }
    }
    return { text: "" }
  })()

  const options = Array.isArray(parsedContent.options) ? parsedContent.options : []

  return (
    <div ref={setNodeRef} style={style} className={cn("group flex flex-col gap-3 rounded-md border p-3 bg-card hover:border-primary/50 transition-colors", isDragging && "ring-2 ring-primary")}>
      <div className="flex items-start justify-between gap-3">
        <div className="flex gap-2 items-start flex-1">
          <button {...attributes} {...listeners} className="mt-1 cursor-grab active:cursor-grabbing text-muted-foreground hover:text-foreground">
            <GripVertical className="h-4 w-4" />
          </button>
          <p className="text-sm line-clamp-2 pt-0.5 select-none">
            {parsedContent.text || t("questionContent")}
          </p>
        </div>
        <Button 
          variant="ghost" 
          size="icon" 
          className="h-6 w-6 text-muted-foreground hover:text-destructive shrink-0"
          onClick={onRemove}
        >
          <Trash2 className="h-4 w-4" />
        </Button>
      </div>
      {(item.question?.type === "single_choice" || item.question?.type === "multiple_choice") && options.length > 0 && (
        <div className="grid grid-cols-1 md:grid-cols-2 gap-1 pl-8 text-xs text-muted-foreground">
          {options.map((opt, idx) => (
            <div key={opt.id ?? idx} className="flex gap-2">
              <span className="font-medium">{opt.id ?? String.fromCharCode(65 + idx)}.</span>
              <span>{opt.text ?? ""}</span>
            </div>
          ))}
        </div>
      )}
      
      <div className="flex items-center justify-end pl-8">
        <div className="flex items-center gap-2">
          <Label htmlFor={`score-${item.id}`} className="text-xs text-muted-foreground">
            {t("score")}
          </Label>
          <Input
            id={`score-${item.id}`}
            type="number"
            min={0}
            className="h-7 w-16 text-right"
            value={item.score}
            onChange={(e) => onScoreChange(parseInt(e.target.value, 10) || 0)}
          />
        </div>
      </div>
    </div>
  )
}

function SortableGroup({ 
  id, 
  item, 
  children,
  onRemove,
  onTitleChange
}: { 
  id: string
  item: ExamNode
  children: React.ReactNode
  onRemove: () => void
  onTitleChange: (val: string) => void
}) {
  const t = useTranslations("examHomework.exam.build")
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id })

  const [isOpen, setIsOpen] = useState(true)

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.5 : 1,
  }

  const childrenKey = JSON.stringify(item.children || [])
  const totalScore = useMemo(() => {
    const calc = (nodes: ExamNode[]): number => {
      return nodes.reduce((acc, node) => {
        if (node.type === 'question') return acc + (node.score || 0)
        if (node.type === 'group') return acc + calc(node.children || [])
        return acc
      }, 0)
    }
    return calc(item.children || [])
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [childrenKey])

  return (
    <Collapsible open={isOpen} onOpenChange={setIsOpen} ref={setNodeRef} style={style} className={cn("rounded-lg border bg-muted/10 p-3 space-y-2", isDragging && "ring-2 ring-primary")}>
      <div className="flex items-center gap-3">
        <button {...attributes} {...listeners} className="cursor-grab active:cursor-grabbing text-muted-foreground hover:text-foreground">
          <GripVertical className="h-5 w-5" />
        </button>
        
        <CollapsibleTrigger asChild>
          <Button variant="ghost" size="sm" className="p-0 h-6 w-6 hover:bg-transparent">
            {isOpen ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
          </Button>
        </CollapsibleTrigger>

        <Input 
          value={item.title || ""} 
          onChange={(e) => onTitleChange(e.target.value)}
          placeholder={t("groupTitlePlaceholder")}
          className="font-semibold h-9 bg-transparent border-transparent hover:border-input focus:bg-background flex-1"
        />
        
        <div className="flex items-center gap-1 text-muted-foreground text-xs bg-background/50 px-2 py-1 rounded">
          <Calculator className="h-3 w-3" />
          <span>{totalScore} {t("pts")}</span>
        </div>

        <Button variant="ghost" size="icon" className="h-8 w-8 text-destructive" onClick={onRemove}>
          <Trash2 className="h-4 w-4" />
        </Button>
      </div>
      
      <CollapsibleContent className="pl-4 border-l-2 border-muted space-y-3 min-h-[50px] animate-in slide-in-from-top-2 fade-in duration-200">
        {children}
      </CollapsibleContent>
    </Collapsible>
  )
}

function SortableSection({ 
  id, 
  item, 
  children,
  onRemove,
  onTitleChange
}: { 
  id: string
  item: ExamNode
  children: React.ReactNode
  onRemove: () => void
  onTitleChange: (val: string) => void
}) {
  const t = useTranslations("examHomework.exam.build")
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id })

  const [isOpen, setIsOpen] = useState(true)

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.5 : 1,
  }

  const childrenKey = JSON.stringify(item.children || [])
  const totalScore = useMemo(() => {
    const calc = (nodes: ExamNode[]): number => {
      return nodes.reduce((acc, node) => {
        if (node.type === 'question') return acc + (node.score || 0)
        if (node.type === 'group' || node.type === 'section') return acc + calc(node.children || [])
        return acc
      }, 0)
    }
    return calc(item.children || [])
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [childrenKey])

  return (
    <Collapsible open={isOpen} onOpenChange={setIsOpen} ref={setNodeRef} style={style} className={cn("rounded-lg border-2 border-primary/20 bg-primary/5 p-3 space-y-2", isDragging && "ring-2 ring-primary")}>
      <div className="flex items-center gap-3">
        <button {...attributes} {...listeners} className="cursor-grab active:cursor-grabbing text-muted-foreground hover:text-foreground">
          <GripVertical className="h-5 w-5" />
        </button>
        
        <CollapsibleTrigger asChild>
          <Button variant="ghost" size="sm" className="p-0 h-6 w-6 hover:bg-transparent">
            {isOpen ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
          </Button>
        </CollapsibleTrigger>

        <Input 
          value={item.title || ""} 
          onChange={(e) => onTitleChange(e.target.value)}
          placeholder={t("sectionTitlePlaceholder")}
          className="font-bold h-9 bg-transparent border-transparent hover:border-input focus:bg-background flex-1"
        />
        
        <div className="flex items-center gap-1 text-muted-foreground text-xs bg-background/50 px-2 py-1 rounded">
          <Calculator className="h-3 w-3" />
          <span>{totalScore} {t("pts")}</span>
        </div>

        <Button variant="ghost" size="icon" className="h-8 w-8 text-destructive" onClick={onRemove}>
          <Trash2 className="h-4 w-4" />
        </Button>
      </div>
      
      <CollapsibleContent className="pl-4 border-l-2 border-primary/30 space-y-3 min-h-[50px] animate-in slide-in-from-top-2 fade-in duration-200">
        {children}
      </CollapsibleContent>
    </Collapsible>
  )
}

function StructureRenderer({ nodes, ...props }: {
  nodes: ExamNode[]
  onRemove: (id: string) => void
  onScoreChange: (id: string, score: number) => void
  onGroupTitleChange: (id: string, title: string) => void
}) {
  const t = useTranslations("examHomework.exam.build")
  // Deduplicate nodes to prevent React key errors
  const nodesKey = JSON.stringify(nodes.map(n => n.id))
  const uniqueNodes = useMemo(() => {
    const seen = new Set()
    return nodes.filter(n => {
      if (seen.has(n.id)) return false
      seen.add(n.id)
      return true
    })
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [nodesKey])

  return (
    <SortableContext items={uniqueNodes.map(n => n.id)} strategy={verticalListSortingStrategy}>
      {uniqueNodes.map(node => (
        <div key={node.id}>
           {node.type === 'section' ? (
             <SortableSection 
               id={node.id} 
               item={node} 
               onRemove={() => props.onRemove(node.id)} 
               onTitleChange={(val) => props.onGroupTitleChange(node.id, val)}
             >
               <StructureRenderer 
                 nodes={node.children || []} 
                 onRemove={props.onRemove}
                 onScoreChange={props.onScoreChange}
                 onGroupTitleChange={props.onGroupTitleChange}
               />
               {(!node.children || node.children.length === 0) && (
                 <div className="text-xs text-muted-foreground italic py-2 text-center border-2 border-dashed border-muted/50 rounded">
                   {t("dragItemsHere")}
                 </div>
               )}
             </SortableSection>
           ) : node.type === 'group' ? (
             <SortableGroup 
               id={node.id} 
               item={node} 
               onRemove={() => props.onRemove(node.id)} 
               onTitleChange={(val) => props.onGroupTitleChange(node.id, val)}
             >
               <StructureRenderer 
                 nodes={node.children || []} 
                 onRemove={props.onRemove}
                 onScoreChange={props.onScoreChange}
                 onGroupTitleChange={props.onGroupTitleChange}
               />
               {(!node.children || node.children.length === 0) && (
                 <div className="text-xs text-muted-foreground italic py-2 text-center border-2 border-dashed border-muted/50 rounded">
                   {t("dragItemsHere")}
                 </div>
               )}
             </SortableGroup>
           ) : (
             <SortableItem 
               id={node.id} 
               item={node} 
               onRemove={() => props.onRemove(node.id)} 
               onScoreChange={(val) => props.onScoreChange(node.id, val)} 
             />
           )}
        </div>
      ))}
    </SortableContext>
  )
}

// --- Main Component ---

const dropAnimation: DropAnimation = {
  sideEffects: defaultDropAnimationSideEffects({
    styles: {
      active: {
        opacity: '0.5',
      },
    },
  }),
}

export function StructureEditor({ items, onChange, onScoreChange, onGroupTitleChange, onRemove, onAddGroup, onAddSection }: StructureEditorProps) {
  const t = useTranslations("examHomework.exam.build")
  const [activeId, setActiveId] = useState<string | null>(null)

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

  // Recursively find item
  const findItem = useCallback((id: string, nodes: ExamNode[] = items): ExamNode | null => {
    const walk = (list: ExamNode[]): ExamNode | null => {
      for (const node of list) {
        if (node.id === id) return node
        if (node.children) {
          const found = walk(node.children)
          if (found) return found
        }
      }
      return null
    }
    return walk(nodes)
  }, [items])

  const activeItem = activeId ? findItem(activeId) : null

  // DND Handlers

  const handleDragStart = useCallback((event: DragStartEvent) => {
    setActiveId(event.active.id as string)
  }, [])

  // Custom collision detection for nested sortables
  const customCollisionDetection: CollisionDetection = useCallback((args) => {
    // 1. First check pointer within for precise container detection
    const pointerCollisions = pointerWithin(args)

    // If we have pointer collisions, prioritize the most specific one (usually the smallest/innermost container)
    if (pointerCollisions.length > 0) {
      return pointerCollisions
    }

    // 2. Fallback to rect intersection for smoother sortable reordering when not directly over a container
    return rectIntersection(args)
  }, [])

  function handleDragOver(event: DragOverEvent) {
    const { active, over } = event
    if (!over) return

    const activeId = active.id as string
    const overId = over.id as string

    if (activeId === overId) return

    // Find if we are moving over a Group container
    // "overId" could be a SortableItem (Question) OR a SortableGroup (Group)
    
    const activeNode = findItem(activeId)
    const overNode = findItem(overId)

    if (!activeNode || !overNode) return

    // CRITICAL FIX: Prevent dragging a node onto its own descendant
    // This happens when dragging a group and hovering over its own children.
    // If we proceed, we would remove the group (and its children) and then fail to find the child to insert next to.
    const isDescendantOfActive = (childId: string): boolean => {
      const check = (node: ExamNode): boolean => {
        if (!node.children) return false
        return node.children.some(c => c.id === childId || check(c))
      }
      return check(activeNode)
    }

    if (isDescendantOfActive(overId)) return

    // Find which list the `over` item belongs to
    const findContainerId = (id: string, list: ExamNode[], parentId: string = 'root'): string | undefined => {
      if (list.some(i => i.id === id)) return parentId
      for (const node of list) {
        if (node.children) {
          const res = findContainerId(id, node.children, node.id)
          if (res) return res
        }
      }
      return undefined
    }

    const activeContainerId = findContainerId(activeId, items)
    const overContainerId = findContainerId(overId, items)

    // Scenario 1: Moving item into a Group/Section by hovering over the Group/Section itself
    // If overNode is a Group or Section, we might want to move INTO it
    if (overNode.type === 'group' || overNode.type === 'section') {
      // Logic: If active item is NOT in this container already
      // AND we are not trying to move a container into its own descendant (circular check)
      
      const isDescendant = (parent: ExamNode, childId: string): boolean => {
         if (!parent.children) return false
         for (const c of parent.children) {
           if (c.id === childId) return true
           if (isDescendant(c, childId)) return true
         }
         return false
      }

      // If moving a container, check if overNode is a descendant of activeNode
      if ((activeNode.type === 'group' || activeNode.type === 'section') && isDescendant(activeNode, overNode.id)) {
        return
      }

      if (activeContainerId !== overNode.id) {
          // ... implementation continues ...
          
          const newItems = cloneExamNodes(items)
          
          // Remove active from old location
          const removeRecursive = (list: ExamNode[]): ExamNode | null => {
            const idx = list.findIndex(i => i.id === activeId)
            if (idx !== -1) return list.splice(idx, 1)[0]
            for (const node of list) {
              if (node.children) {
                const res = removeRecursive(node.children)
                if (res) return res
              }
            }
            return null
          }
          
          const movedItem = removeRecursive(newItems)
          if (!movedItem) return

          // Insert into new Group (overNode)
          // We need to find the overNode in the NEW structure (since we cloned it)
          const findGroupAndInsert = (list: ExamNode[]) => {
             for (const node of list) {
               if (node.id === overId) {
                 if (!node.children) node.children = []
                 // Extra safety: Check if movedItem.id is already in children
                 if (!node.children.some(c => c.id === movedItem.id)) {
                    node.children.push(movedItem)
                 }
                 return true
               }
               if (node.children) {
                 if (findGroupAndInsert(node.children)) return true
               }
             }
             return false
          }
          
          findGroupAndInsert(newItems)
          onChange(newItems)
          return
       }
    }

    // Scenario 2: Moving between different lists (e.g. from Root to Group A, or Group A to Group B)
    if (activeContainerId !== overContainerId) {
      // FIX: If we are already inside the group we are hovering (i.e. activeContainerId IS overId), 
      // do not try to move "next to" the group (which would move us out).
      if (activeContainerId === overId) return

      // Standard Sortable Move
      const newItems = cloneExamNodes(items)
      
      const removeRecursive = (list: ExamNode[]): ExamNode | null => {
        const idx = list.findIndex(i => i.id === activeId)
        if (idx !== -1) return list.splice(idx, 1)[0]
        for (const node of list) {
          if (node.children) {
             const res = removeRecursive(node.children)
             if (res) return res
          }
        }
        return null
      }

      const movedItem = removeRecursive(newItems)
      if (!movedItem) return

      // Insert into destination list at specific index
      // We need to find the destination list array and the index of `overId`
      const insertRecursive = (list: ExamNode[]): boolean => {
         const idx = list.findIndex(i => i.id === overId)
         if (idx !== -1) {
           // Insert before or after based on direction? 
           // Usually dnd-kit handles order if we are in same container, but cross-container we need to pick a spot.
           // We'll insert at the index of `overId`.
           
           // However, if we insert AT the index, dnd-kit might get confused if we are dragging DOWN vs UP.
           // But since we are changing containers, just inserting at the target index is usually fine.
           // The issue "swapping positions is not smooth" might be because we insert *at* index, displacing the target.
           // Let's try to determine if we are "below" or "above" the target? 
           // For cross-container, simpler is better. Inserting at index is standard.
           
           list.splice(idx, 0, movedItem)
           return true
         }
         for (const node of list) {
           if (node.children) {
             if (insertRecursive(node.children)) return true
           }
         }
         return false
      }

      insertRecursive(newItems)
      onChange(newItems)
    }
  }

  function handleDragEnd(event: DragEndEvent) {
    const { active, over } = event
    setActiveId(null)

    if (!over) return
    
    const activeId = active.id as string
    const overId = over.id as string
    
    if (activeId === overId) return

    // Re-find positions in the potentially updated state
    // Note: Since we mutate in DragOver, the item might already be in the new container.
    // So activeContainerId might equal overContainerId now!
    
    const findContainerId = (id: string, list: ExamNode[], parentId: string = 'root'): string | undefined => {
      if (list.some(i => i.id === id)) return parentId
      for (const node of list) {
        if (node.children) {
          const res = findContainerId(id, node.children, node.id)
          if (res) return res
        }
      }
      return undefined
    }

    const activeContainerId = findContainerId(activeId, items)
    const overContainerId = findContainerId(overId, items)

    if (activeContainerId === overContainerId) {
      // Same container reorder
      const newItems = cloneExamNodes(items)
      
      const getMutableList = (groupId?: string): ExamNode[] => {
        if (groupId === 'root') return newItems
        // Need recursive find
        const findGroup = (list: ExamNode[]): ExamNode | null => {
          for (const node of list) {
            if (node.id === groupId) return node
            if (node.children) {
              const res = findGroup(node.children)
              if (res) return res
            }
          }
          return null
        }
        return findGroup(newItems)?.children || []
      }

      const list = getMutableList(activeContainerId)
      const oldIndex = list.findIndex(i => i.id === activeId)
      const newIndex = list.findIndex(i => i.id === overId)

      if (oldIndex !== -1 && newIndex !== -1 && oldIndex !== newIndex) {
        const moved = arrayMove(list, oldIndex, newIndex)

        // Update the list reference in parent
        if (activeContainerId === 'root') {
          onChange(moved)
        } else if (activeContainerId) {
           // list is already a reference to children array if we did it right?
           // getMutableList returned `group.children`. Modifying `list` directly via arrayMove returns NEW array.
           // So we need to re-assign.
           const group = findItem(activeContainerId, newItems)
           if (group) group.children = moved
           onChange(newItems)
        }
      }
    }
  }

  return (
    <DndContext 
      id="structure-editor-dnd"
      sensors={sensors} 
      collisionDetection={customCollisionDetection} 
      onDragStart={handleDragStart}
      onDragOver={handleDragOver}
      onDragEnd={handleDragEnd}
      measuring={{ droppable: { strategy: MeasuringStrategy.Always } }}
    >
      <div className="space-y-4">
        <StructureRenderer 
          nodes={items}
          onRemove={onRemove}
          onScoreChange={onScoreChange}
          onGroupTitleChange={onGroupTitleChange}
        />

        <div className="flex justify-center gap-2 pt-2">
          <Button variant="outline" size="sm" onClick={onAddGroup} className="border-dashed">
            {t("addGroup")}
          </Button>
          {onAddSection && (
            <Button variant="outline" size="sm" onClick={onAddSection} className="border-dashed">
              {t("addSection")}
            </Button>
          )}
        </div>
      </div>

      <DragOverlay dropAnimation={dropAnimation}>
        {activeItem ? (
           activeItem.type === 'section' ? (
             <div className="rounded-lg border-2 border-primary/20 bg-primary/5 p-4 shadow-lg opacity-80 w-[300px]">
               <div className="flex items-center gap-3">
                 <GripVertical className="h-5 w-5" />
                 <span className="font-bold">{activeItem.title || t("section")}</span>
               </div>
             </div>
           ) : activeItem.type === 'group' ? (
             <div className="rounded-lg border bg-background p-4 shadow-lg opacity-80 w-[300px]">
               <div className="flex items-center gap-3">
                 <GripVertical className="h-5 w-5" />
                 <span className="font-semibold">{activeItem.title || t("group")}</span>
               </div>
             </div>
           ) : (
             <div className="rounded-md border bg-background p-3 shadow-lg opacity-80 w-[300px] flex items-center gap-3">
               <GripVertical className="h-4 w-4" />
               <p className="text-sm line-clamp-1">
                 {extractQuestionText(activeItem.question?.content) || t("question")}
               </p>
             </div>
           )
        ) : null}
      </DragOverlay>
    </DndContext>
  )
}
