import { useState } from 'react'
import {
  ShoppingBasket,
  Plus,
  Target,
  ChevronDown,
  ChevronRight,
  FolderOutput,
  MoreHorizontal,
  Pencil,
  Copy,
  Trash2,
  Users,
  Download as DownloadIcon,
} from 'lucide-react'
import clsx from 'clsx'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useHeapsQuery, HEAPS_QUERY_KEY } from '../../hooks/useHeapsQuery'
import { useSharedHeapsQuery } from '../../hooks/useSharingQueries'
import { heaps as heapsApi, downloads, type Heap } from '../../services/api'
import { useFilterStore } from '../../store/filterStore'
import { toast } from '../ToastContainer'
import { PHOTO_DRAG_MIME } from '../timeline/PhotoThumbnail'
import { formatApiError } from '../../lib/apiError'
import { HeapConvertDialog } from './HeapConvertDialog'
import { ShareDialog } from '../sharing/ShareDialog'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'

/**
 * 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)
  const [sharingHeap, setSharingHeap] = useState<Heap | null>(null)
  const { data: sharedHeaps = [] } = useSharedHeapsQuery()
  // 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. Drives the trigger's
  // hover-visible state via DropdownMenu's open prop — Radix handles
  // outside-click + Escape dismissal internally.
  const [openMenuId, setOpenMenuId] = useState<string | 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', formatApiError(e)),
  })

  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', formatApiError(e)),
  })

  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', formatApiError(e)),
  })

  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', formatApiError(e)),
  })

  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', formatApiError(e)),
  })

  // 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', formatApiError(e))
    },
    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="h-6 flex-1 bg-bg px-2 text-[11px]"
              />
              <Button
                onClick={handleCreate}
                disabled={!newName.trim() || createMutation.isPending}
                size="sm"
                className="h-6 px-2 text-[11px]"
              >
                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 Select / 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="h-6 flex-1 bg-bg px-1 text-[13px]"
                  />
                ) : (
                  <>
                    <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 — Select (S) 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. */}
                <DropdownMenu
                  open={isMenuOpen}
                  onOpenChange={(o) => setOpenMenuId(o ? heap.id : null)}
                >
                  <div
                    className={clsx(
                      'relative flex-shrink-0',
                      isMenuOpen ? 'block' : 'hidden group-hover:block'
                    )}
                  >
                    <DropdownMenuTrigger asChild>
                      <button
                        onClick={(e) => e.stopPropagation()}
                        className="rounded p-0.5 text-text-muted hover:bg-surface-offset hover:text-text"
                        title="More actions"
                        aria-label="More heap actions"
                      >
                        <MoreHorizontal className="h-3.5 w-3.5" />
                      </button>
                    </DropdownMenuTrigger>
                  </div>
                  <DropdownMenuContent
                    align="end"
                    className="min-w-[160px]"
                    onClick={(e) => e.stopPropagation()}
                  >
                    <DropdownMenuItem
                      onClick={() => {
                        setRenamingId(heap.id)
                        setRenameDraft(heap.name)
                      }}
                    >
                      <Pencil className="h-3.5 w-3.5 text-text-muted" />
                      Rename
                    </DropdownMenuItem>
                    <DropdownMenuItem
                      onClick={() => duplicateMutation.mutate(heap.id)}
                    >
                      <Copy className="h-3.5 w-3.5 text-text-muted" />
                      Duplicate
                    </DropdownMenuItem>
                    <DropdownMenuItem onClick={() => setConvertingHeap(heap)}>
                      <FolderOutput className="h-3.5 w-3.5 text-text-muted" />
                      Move to folder…
                    </DropdownMenuItem>
                    <DropdownMenuItem onClick={() => setSharingHeap(heap)}>
                      <Users className="h-3.5 w-3.5 text-text-muted" />
                      Share…
                    </DropdownMenuItem>
                    <DropdownMenuItem
                      onClick={() =>
                        downloads.trigger(downloads.heapUrl(heap.id))
                      }
                    >
                      <DownloadIcon className="h-3.5 w-3.5 text-text-muted" />
                      Download as zip
                    </DropdownMenuItem>
                    <DropdownMenuSeparator />
                    <DropdownMenuItem
                      onClick={() => {
                        if (
                          confirm(
                            `Delete heap "${heap.name}"? Photos are not affected.`
                          )
                        ) {
                          deleteMutation.mutate(heap.id)
                        }
                      }}
                      className="text-reject focus:bg-reject/10 focus:text-reject"
                    >
                      <Trash2 className="h-3.5 w-3.5" />
                      Delete
                    </DropdownMenuItem>
                  </DropdownMenuContent>
                </DropdownMenu>
              </div>
            )
          })}
        </div>
      )}

      {/* Shared with me */}
      {sharedHeaps.length > 0 && expanded && (
        <div className="mt-1">
          <div className="px-3 py-0.5 text-[9px] font-semibold uppercase tracking-[0.14em] text-text-faint">
            Shared with me
          </div>
          {sharedHeaps.map((sh) => {
            const isFiltered = currentSection === `heap-${sh.id}`
            return (
              <div
                key={sh.id}
                className={clsx(
                  'group 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',
                )}
                style={{ paddingLeft: '20px' }}
                onClick={() =>
                  navigateToSection(`heap-${sh.id}`, { heapId: sh.id })
                }
              >
                <Users
                  className={clsx(
                    'h-3.5 w-3.5 flex-shrink-0',
                    isFiltered ? 'text-primary' : 'text-text-muted'
                  )}
                />
                <span className="truncate" title={sh.name}>
                  {sh.name}
                </span>
                <span className="ml-0.5 truncate text-[10px] text-text-faint">
                  {sh.owner_username}
                </span>
                <span className="ml-auto rounded bg-surface-offset px-1 text-[9px] uppercase text-text-faint">
                  {sh.permission}
                </span>
                {sh.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">
                    {sh.photo_count}
                  </span>
                )}
              </div>
            )
          })}
        </div>
      )}

      <HeapConvertDialog
        heap={convertingHeap}
        onClose={() => setConvertingHeap(null)}
      />
      <ShareDialog
        isOpen={!!sharingHeap}
        type="heap"
        targetId={sharingHeap?.id ?? ''}
        targetName={sharingHeap?.name ?? ''}
        onClose={() => setSharingHeap(null)}
      />
    </div>
  )
}

