import { useEffect, useRef, useState } from 'react'
import {
  ShoppingBasket,
  Plus,
  Target,
  ChevronDown,
  ChevronRight,
  FolderOutput,
  MoreHorizontal,
  Pencil,
  Copy,
  Trash2,
} from 'lucide-react'
import clsx from 'clsx'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useHeapsQuery, HEAPS_QUERY_KEY } from '../../hooks/useHeapsQuery'
import { heaps as heapsApi, type Heap } from '../../services/api'
import { useFilterStore } from '../../store/filterStore'
import { toast } from '../ToastContainer'
import { PHOTO_DRAG_MIME } from '../timeline/PhotoThumbnail'
import { HeapConvertDialog } from './HeapConvertDialog'

/**
 * Heaps panel for the left sidebar. Renders the list of heaps with the
 * basket icon, lets the user create a new heap, click one to filter the
 * timeline to its contents, set one as the "active" target for the T
 * shortcut, and delete heaps.
 *
 * Heap state:
 *  - filter heapId: which heap is currently filtered to (visual)
 *  - heap.is_active: which heap T adds to (server-side, single per row)
 */
export function HeapsPanel() {
  const { data: heaps = [] } = useHeapsQuery()
  const navigateToSection = useFilterStore((s) => s.navigateToSection)
  const currentSection = useFilterStore((s) => s.currentSection)
  const queryClient = useQueryClient()

  const [expanded, setExpanded] = useState(true)
  const [creating, setCreating] = useState(false)
  const [newName, setNewName] = useState('')
  // Which heap row is currently being hovered with a drag — used to render
  // the drop highlight ring. Only one heap can be the target at a time.
  const [dropTargetId, setDropTargetId] = useState<string | null>(null)
  const [convertingHeap, setConvertingHeap] = useState<Heap | null>(null)
  // Inline rename state for heap rows: stores the heap id being edited and
  // the draft name. Mirrors the folder rename pattern in LeftSidebar.
  const [renamingId, setRenamingId] = useState<string | null>(null)
  const [renameDraft, setRenameDraft] = useState('')
  // Which heap's burger menu is currently open. null when no menu is open.
  // The popover closes on outside click and Escape via the effect below.
  const [openMenuId, setOpenMenuId] = useState<string | null>(null)
  const menuRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (!openMenuId) return
    const onDown = (e: MouseEvent) => {
      if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
        setOpenMenuId(null)
      }
    }
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setOpenMenuId(null)
    }
    document.addEventListener('mousedown', onDown)
    document.addEventListener('keydown', onKey)
    return () => {
      document.removeEventListener('mousedown', onDown)
      document.removeEventListener('keydown', onKey)
    }
  }, [openMenuId])

  const invalidate = () => {
    queryClient.invalidateQueries({ queryKey: HEAPS_QUERY_KEY })
  }

  const createMutation = useMutation({
    mutationFn: (name: string) => heapsApi.create(name),
    onSuccess: () => {
      invalidate()
      setNewName('')
      setCreating(false)
    },
    onError: (e: any) =>
      toast.error('Failed to create heap', e.message || 'Unknown error'),
  })

  const setActiveMutation = useMutation({
    mutationFn: (heapId: string) =>
      heapsApi.update(heapId, { is_active: true }),
    onSuccess: (heap) => {
      invalidate()
      toast.success('Active heap', `Now adding to "${heap.name}" with T`)
    },
    onError: (e: any) =>
      toast.error('Failed to set active', e.message || 'Unknown error'),
  })

  const deleteMutation = useMutation({
    mutationFn: (heapId: string) => heapsApi.delete(heapId),
    onSuccess: (_, heapId) => {
      invalidate()
      // If we were viewing this heap, snap back to all-photos.
      if (currentSection === `heap-${heapId}`) {
        navigateToSection('all-photos', {})
      }
    },
    onError: (e: any) =>
      toast.error('Failed to delete heap', e.message || 'Unknown error'),
  })

  const renameMutation = useMutation({
    mutationFn: ({ heapId, name }: { heapId: string; name: string }) =>
      heapsApi.update(heapId, { name }),
    onSuccess: () => invalidate(),
    onError: (e: any) =>
      toast.error('Failed to rename heap', e.message || 'Unknown error'),
  })

  const duplicateMutation = useMutation({
    mutationFn: (heapId: string) => heapsApi.duplicate(heapId),
    onSuccess: (heap) => {
      invalidate()
      toast.success('Heap duplicated', heap.name)
    },
    onError: (e: any) =>
      toast.error('Failed to duplicate heap', e.message || 'Unknown error'),
  })

  // Drop handler: add the dragged photos to the target heap. Optimistically
  // updates the membership cache so the basket affordance flips immediately,
  // mirroring the keyboard P-toggle pattern.
  const dropMutation = useMutation({
    mutationFn: ({ heapId, photoIds }: { heapId: string; photoIds: string[] }) =>
      heapsApi.addPhotos(heapId, photoIds),
    onMutate: ({ heapId, photoIds }) => {
      const key = ['heap-photo-ids', heapId] as const
      const previous = queryClient.getQueryData<string[]>(key)
      const set = new Set(previous ?? [])
      photoIds.forEach((id) => set.add(id))
      queryClient.setQueryData<string[]>(key, Array.from(set))
      return { previous }
    },
    onError: (e: any, vars, ctx) => {
      if (ctx?.previous) {
        queryClient.setQueryData(['heap-photo-ids', vars.heapId], ctx.previous)
      }
      toast.error('Failed to add to heap', e.message || 'Unknown error')
    },
    onSuccess: (data, vars) => {
      const heap = heaps.find((h) => h.id === vars.heapId)
      const heapName = heap?.name ?? 'heap'
      const added = data?.added ?? 0
      const already = data?.already_present ?? 0
      if (added > 0) {
        toast.success(
          `Added to ${heapName}`,
          `${added} photo${added > 1 ? 's' : ''}${already > 0 ? ` (${already} already present)` : ''}`
        )
      } else if (already > 0) {
        toast.info(`Already in ${heapName}`, `${already} photo${already > 1 ? 's' : ''}`)
      }
    },
    onSettled: (_d, _e, vars) => {
      queryClient.invalidateQueries({ queryKey: HEAPS_QUERY_KEY })
      queryClient.invalidateQueries({ queryKey: ['heap-photo-ids', vars.heapId] })
      queryClient.invalidateQueries({ queryKey: ['photos'] })
    },
  })

  const handleCreate = () => {
    const name = newName.trim()
    if (!name) return
    createMutation.mutate(name)
  }

  return (
    <div>
      {/* Section header — eyebrow style to match the LeftSidebar
       *  library/folders headers. */}
      <div
        className="group mt-2 flex cursor-pointer items-center gap-1 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-text-muted hover:text-text"
        onClick={() => setExpanded((v) => !v)}
      >
        <button className="rounded p-0.5 hover:bg-surface-offset">
          {expanded ? (
            <ChevronDown className="h-3 w-3" />
          ) : (
            <ChevronRight className="h-3 w-3" />
          )}
        </button>
        <span className="flex-1 truncate">Heaps</span>
        <button
          onClick={(e) => {
            e.stopPropagation()
            setCreating(true)
            setExpanded(true)
          }}
          className="invisible rounded p-0.5 hover:bg-surface-offset hover:text-text group-hover:visible"
          title="New heap"
        >
          <Plus className="h-3 w-3" />
        </button>
      </div>

      {expanded && (
        <div>
          {/* Inline create form */}
          {creating && (
            <div
              className="flex items-center gap-1 px-2 py-0.5"
              style={{ paddingLeft: '20px' }}
            >
              <input
                autoFocus
                type="text"
                value={newName}
                onChange={(e) => setNewName(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter') handleCreate()
                  if (e.key === 'Escape') {
                    setCreating(false)
                    setNewName('')
                  }
                }}
                placeholder="Heap name"
                className="flex-1 rounded border border-border bg-bg px-2 py-0.5 text-[11px] text-text focus:border-primary focus:outline-none"
              />
              <button
                onClick={handleCreate}
                disabled={!newName.trim() || createMutation.isPending}
                className="rounded bg-primary px-2 py-0.5 text-[11px] text-white hover:bg-primary/80 disabled:opacity-50"
              >
                Add
              </button>
            </div>
          )}

          {heaps.length === 0 && !creating && (
            <div
              className="px-2 py-0.5 text-[11px] text-text-faint"
              style={{ paddingLeft: '20px' }}
            >
              No heaps yet
            </div>
          )}

          {heaps.map((heap) => {
            const isFiltered = currentSection === `heap-${heap.id}`
            const isActive = heap.is_active
            const isDropTarget = dropTargetId === heap.id
            const isRenaming = renamingId === heap.id
            const isMenuOpen = openMenuId === heap.id

            const commitRename = () => {
              const next = renameDraft.trim()
              if (next && next !== heap.name) {
                renameMutation.mutate({ heapId: heap.id, name: next })
              }
              setRenamingId(null)
            }

            return (
              <div
                key={heap.id}
                className={clsx(
                  'group relative flex h-[24px] cursor-pointer items-center gap-1 rounded px-2 text-[12px] leading-none',
                  isFiltered ? 'bg-primary/20 text-primary' : 'text-text hover:bg-surface-2',
                  // Active-heap row gets a soft primary wash so the
                  // user always knows where Pick / T will land, even
                  // when viewing a different section.
                  isActive && !isFiltered && 'bg-primary/8 text-text',
                  isDropTarget && 'ring-2 ring-primary bg-primary/10'
                )}
                style={{ paddingLeft: '20px' }}
                onClick={() => {
                  if (isRenaming) return
                  navigateToSection(`heap-${heap.id}`, { heapId: heap.id })
                }}
                onDoubleClick={(e) => {
                  e.stopPropagation()
                  setRenamingId(heap.id)
                  setRenameDraft(heap.name)
                }}
                onDragOver={(e) => {
                  if (e.dataTransfer.types.includes(PHOTO_DRAG_MIME)) {
                    e.preventDefault()
                    e.dataTransfer.dropEffect = 'copy'
                    if (dropTargetId !== heap.id) setDropTargetId(heap.id)
                  }
                }}
                onDragLeave={(e) => {
                  if (!e.currentTarget.contains(e.relatedTarget as Node)) {
                    if (dropTargetId === heap.id) setDropTargetId(null)
                  }
                }}
                onDrop={(e) => {
                  e.preventDefault()
                  setDropTargetId(null)
                  const raw = e.dataTransfer.getData(PHOTO_DRAG_MIME)
                  if (!raw) return
                  try {
                    const ids = JSON.parse(raw) as string[]
                    if (Array.isArray(ids) && ids.length > 0) {
                      dropMutation.mutate({ heapId: heap.id, photoIds: ids })
                    }
                  } catch {
                    // Bad payload — ignore.
                  }
                }}
              >
                <ShoppingBasket
                  className={clsx(
                    'h-3.5 w-3.5 flex-shrink-0',
                    isActive || isFiltered ? 'text-primary' : 'text-text-muted'
                  )}
                />

                {isRenaming ? (
                  <input
                    autoFocus
                    type="text"
                    value={renameDraft}
                    onChange={(e) => setRenameDraft(e.target.value)}
                    onClick={(e) => e.stopPropagation()}
                    onBlur={commitRename}
                    onKeyDown={(e) => {
                      if (e.key === 'Enter') {
                        e.currentTarget.blur()
                      } else if (e.key === 'Escape') {
                        setRenamingId(null)
                      }
                    }}
                    className="flex-1 rounded border border-border bg-bg px-1 py-0 text-[13px] text-text focus:border-primary focus:outline-none"
                  />
                ) : (
                  <>
                    <span
                      className={clsx('truncate', isActive && 'font-semibold')}
                      title={heap.name}
                    >
                      {heap.name}
                    </span>
                    {isActive && (
                      <span
                        className="ml-1 flex-shrink-0 rounded-full bg-primary/25 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-primary"
                        title="Active heap — Pick (P) and the basket badge on photos point here"
                      >
                        Active
                      </span>
                    )}
                    <span className="flex-1" />
                  </>
                )}

                {/* Right cluster. Count is the rightmost element in the
                 *  resting state — set-active and kebab use display:none
                 *  (not invisible) so they reserve no width until hover,
                 *  keeping the count column aligned with the rest of the
                 *  sidebar. The active heap is signaled by font-semibold
                 *  on the name above; the standalone Target indicator
                 *  was making heap counts sit left of the others. */}
                {heap.photo_count > 0 ? (
                  <span className="flex h-4 min-w-[20px] flex-shrink-0 items-center justify-center rounded bg-surface-offset px-1 text-[10px] tabular-nums text-text-muted">
                    {heap.photo_count}
                  </span>
                ) : (
                  <span className="h-4 min-w-[20px] flex-shrink-0" aria-hidden="true" />
                )}
                {!isActive && (
                  <button
                    onClick={(e) => {
                      e.stopPropagation()
                      setActiveMutation.mutate(heap.id)
                    }}
                    className="hidden flex-shrink-0 rounded p-0.5 text-text-muted hover:bg-surface-offset hover:text-text group-hover:block"
                    title="Set as active heap (T target)"
                    aria-label="Set as active heap"
                  >
                    <Target className="h-3 w-3" />
                  </button>
                )}

                {/* Kebab menu — collects rename / duplicate / convert /
                 *  delete so the row stays compact. */}
                <div
                  className={clsx(
                    'relative flex-shrink-0',
                    isMenuOpen ? 'block' : 'hidden group-hover:block'
                  )}
                >
                  <button
                    onClick={(e) => {
                      e.stopPropagation()
                      setOpenMenuId(isMenuOpen ? null : heap.id)
                    }}
                    className="rounded p-0.5 text-text-muted hover:bg-surface-offset hover:text-text"
                    title="More actions"
                    aria-label="More heap actions"
                    aria-haspopup="menu"
                    aria-expanded={isMenuOpen}
                  >
                    <MoreHorizontal className="h-3.5 w-3.5" />
                  </button>

                  {isMenuOpen && (
                    <div
                      ref={menuRef}
                      role="menu"
                      className="absolute right-0 top-full z-30 mt-1 min-w-[160px] overflow-hidden rounded-lg border border-border bg-surface py-1 text-sm shadow-xl"
                      onClick={(e) => e.stopPropagation()}
                    >
                      <MenuItem
                        icon={<Pencil className="h-3.5 w-3.5" />}
                        label="Rename"
                        onClick={() => {
                          setOpenMenuId(null)
                          setRenamingId(heap.id)
                          setRenameDraft(heap.name)
                        }}
                      />
                      <MenuItem
                        icon={<Copy className="h-3.5 w-3.5" />}
                        label="Duplicate"
                        onClick={() => {
                          setOpenMenuId(null)
                          duplicateMutation.mutate(heap.id)
                        }}
                      />
                      <MenuItem
                        icon={<FolderOutput className="h-3.5 w-3.5" />}
                        label="Move to folder…"
                        onClick={() => {
                          setOpenMenuId(null)
                          setConvertingHeap(heap)
                        }}
                      />
                      <div className="my-1 h-px bg-border" />
                      <MenuItem
                        icon={<Trash2 className="h-3.5 w-3.5" />}
                        label="Delete"
                        destructive
                        onClick={() => {
                          setOpenMenuId(null)
                          if (
                            confirm(
                              `Delete heap "${heap.name}"? Photos are not affected.`
                            )
                          ) {
                            deleteMutation.mutate(heap.id)
                          }
                        }}
                      />
                    </div>
                  )}
                </div>
              </div>
            )
          })}
        </div>
      )}

      <HeapConvertDialog
        heap={convertingHeap}
        onClose={() => setConvertingHeap(null)}
      />
    </div>
  )
}

function MenuItem({
  icon,
  label,
  onClick,
  destructive = false,
}: {
  icon: React.ReactNode
  label: string
  onClick: () => void
  destructive?: boolean
}) {
  return (
    <button
      role="menuitem"
      onClick={onClick}
      className={clsx(
        'flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors',
        destructive
          ? 'text-reject hover:bg-reject/10'
          : 'text-text hover:bg-surface-2'
      )}
    >
      <span className="text-text-muted">{icon}</span>
      {label}
    </button>
  )
}
