import { useState } from 'react'
import {
  ShoppingBasket,
  Plus,
  Target,
  X,
  ChevronDown,
  ChevronRight,
  FolderOutput,
} 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 filterHeapId = useFilterStore((s) => s.heapId)
  const setFilterHeapId = useFilterStore((s) => s.setHeapId)
  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)

  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 filtering by this heap, clear the filter
      if (filterHeapId === heapId) setFilterHeapId(null)
    },
    onError: (e: any) =>
      toast.error('Failed to delete 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 */}
      <div
        className="group flex cursor-pointer items-center gap-1 rounded px-2 py-1 text-sm text-text hover:bg-surface-2"
        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>
        <ShoppingBasket className="h-4 w-4 text-text-muted" />
        <span className="flex-1 truncate">Heaps</span>
        <button
          onClick={(e) => {
            e.stopPropagation()
            setCreating(true)
            setExpanded(true)
          }}
          className="invisible rounded p-0.5 text-text-muted 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-1"
              style={{ paddingLeft: '32px' }}
            >
              <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-xs 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-xs text-white hover:bg-primary/80 disabled:opacity-50"
              >
                Add
              </button>
            </div>
          )}

          {heaps.length === 0 && !creating && (
            <div
              className="px-2 py-1 text-xs text-text-faint"
              style={{ paddingLeft: '32px' }}
            >
              No heaps yet
            </div>
          )}

          {heaps.map((heap) => {
            const isFiltered = filterHeapId === heap.id
            const isActive = heap.is_active
            const isDropTarget = dropTargetId === heap.id
            return (
              <div
                key={heap.id}
                className={clsx(
                  'group flex cursor-pointer items-center gap-1 rounded px-2 py-1 text-[13px]',
                  isFiltered ? 'bg-primary/20 text-primary' : 'text-text hover:bg-surface-2',
                  isDropTarget && 'ring-2 ring-primary bg-primary/10'
                )}
                style={{ paddingLeft: '32px' }}
                onClick={() => setFilterHeapId(heap.id)}
                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) => {
                  // Only clear if we're actually leaving this row, not just
                  // moving over a child element.
                  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-4 w-4 flex-shrink-0',
                    isFiltered ? 'text-primary' : 'text-text-muted'
                  )}
                />
                <span
                  className={clsx(
                    'flex-1 truncate',
                    isActive && 'font-semibold'
                  )}
                  title={heap.name}
                >
                  {heap.name}
                </span>
                {isActive && (
                  <Target
                    className="h-3 w-3 text-primary"
                    aria-label="Active heap (T target)"
                  />
                )}
                {heap.photo_count > 0 && (
                  <span className="rounded bg-surface-offset px-1.5 py-0.5 text-xs text-text-muted">
                    {heap.photo_count}
                  </span>
                )}
                <button
                  onClick={(e) => {
                    e.stopPropagation()
                    if (!isActive) setActiveMutation.mutate(heap.id)
                  }}
                  className={clsx(
                    'rounded p-0.5 hover:bg-surface-offset hover:text-text',
                    isActive
                      ? 'invisible'
                      : 'invisible text-text-muted group-hover:visible'
                  )}
                  title="Set as active heap (T target)"
                >
                  <Target className="h-3 w-3" />
                </button>
                <button
                  onClick={(e) => {
                    e.stopPropagation()
                    setConvertingHeap(heap)
                  }}
                  className="invisible rounded p-0.5 text-text-muted hover:bg-surface-offset hover:text-text group-hover:visible"
                  title="Convert to folder…"
                >
                  <FolderOutput className="h-3 w-3" />
                </button>
                <button
                  onClick={(e) => {
                    e.stopPropagation()
                    if (confirm(`Delete heap "${heap.name}"? Photos are not affected.`)) {
                      deleteMutation.mutate(heap.id)
                    }
                  }}
                  className="invisible rounded p-0.5 text-text-muted hover:bg-surface-offset hover:text-reject group-hover:visible"
                  title="Delete heap"
                >
                  <X className="h-3 w-3" />
                </button>
              </div>
            )
          })}
        </div>
      )}

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