/**
 * Logos sidebar: one-level page/subpage hierarchy. Create, rename, delete, reorder.
 */

import React, { useCallback, useEffect, useRef, useState } from 'react'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Button } from '@/components/ui/button'
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog'
import {
  ChevronDown,
  ChevronRight,
  FileText,
  MoreHorizontal,
  Pencil,
  Plus,
  Trash2,
  ArrowUp,
  ArrowDown,
} from 'lucide-react'
import type { LogosPageId, LogosPageMeta } from '../state/recollectionStore'
import { cn } from '@/lib/utils'

const DEFAULT_PAGE_TITLE = 'Untitled'

function generatePageId(): LogosPageId {
  return `page-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
}

function sortByPosition(a: LogosPageMeta, b: LogosPageMeta) {
  return a.position - b.position
}

export type LogosSidebarProps = {
  recollectionId: string
  tree: LogosPageMeta[]
  onTreeChange: (tree: LogosPageMeta[]) => void
  activePageId: LogosPageId | null
  onSelectPage: (id: LogosPageId) => void
  onDeletePage?: (pageId: LogosPageId) => void
}

export function LogosSidebar({
  recollectionId,
  tree,
  onTreeChange,
  activePageId,
  onSelectPage,
  onDeletePage,
}: LogosSidebarProps) {
  const [expandedPages, setExpandedPages] = useState<Set<LogosPageId>>(new Set())
  const expandedInitializedRef = useRef(false)
  const [editingId, setEditingId] = useState<LogosPageId | null>(null)
  const [editTitle, setEditTitle] = useState('')
  const [deleteTarget, setDeleteTarget] = useState<LogosPageMeta | null>(null)

  const pages = tree.filter((p) => p.parentId === null).sort(sortByPosition)

  // Default: expand parent pages that have subpages so children are visible on first load.
  useEffect(() => {
    if (tree.length === 0 || expandedInitializedRef.current) return
    expandedInitializedRef.current = true
    const parentIdsWithChildren = tree
      .filter((p) => p.parentId === null && tree.some((s) => s.parentId === p.id))
      .map((p) => p.id)
    if (parentIdsWithChildren.length > 0) {
      setExpandedPages((prev) => {
        const next = new Set(prev)
        parentIdsWithChildren.forEach((id) => next.add(id))
        return next
      })
    }
  }, [tree])

  const toggleExpanded = useCallback((id: LogosPageId) => {
    setExpandedPages((prev) => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })
  }, [])

  const updatePage = useCallback(
    (id: LogosPageId, patch: Partial<Pick<LogosPageMeta, 'title' | 'position'>>) => {
      const next = tree.map((p) => (p.id === id ? { ...p, ...patch } : p))
      onTreeChange(next)
    },
    [tree, onTreeChange]
  )

  const addPage = useCallback(() => {
    const maxPos = Math.max(0, ...pages.map((p) => p.position), -1)
    const newPage: LogosPageMeta = {
      id: generatePageId(),
      title: DEFAULT_PAGE_TITLE,
      parentId: null,
      position: maxPos + 1,
    }
    onTreeChange([...tree, newPage])
    setEditingId(newPage.id)
    setEditTitle(newPage.title)
    onSelectPage(newPage.id)
  }, [tree, pages, onTreeChange, onSelectPage])

  const addSubpage = useCallback(
    (parentId: LogosPageId) => {
      const siblings = tree.filter((p) => p.parentId === parentId).sort(sortByPosition)
      const maxPos = siblings.length === 0 ? 0 : Math.max(...siblings.map((p) => p.position)) + 1
      const newPage: LogosPageMeta = {
        id: generatePageId(),
        title: DEFAULT_PAGE_TITLE,
        parentId,
        position: maxPos,
      }
      onTreeChange([...tree, newPage])
      setExpandedPages((prev) => new Set(prev).add(parentId))
      setEditingId(newPage.id)
      setEditTitle(newPage.title)
      onSelectPage(newPage.id)
    },
    [tree, onTreeChange, onSelectPage]
  )

  const startRename = useCallback((page: LogosPageMeta) => {
    setEditingId(page.id)
    setEditTitle(page.title)
  }, [])

  const commitRename = useCallback(() => {
    if (editingId && editTitle.trim()) {
      updatePage(editingId, { title: editTitle.trim() })
    }
    setEditingId(null)
    setEditTitle('')
  }, [editingId, editTitle, updatePage])

  const movePage = useCallback(
    (id: LogosPageId, delta: number) => {
      const page = tree.find((p) => p.id === id)
      if (!page) return
      const siblings = tree
        .filter((p) => p.parentId === page.parentId)
        .sort(sortByPosition)
      const idx = siblings.findIndex((p) => p.id === id)
      if (idx < 0) return
      const newIdx = Math.max(0, Math.min(siblings.length - 1, idx + delta))
      if (newIdx === idx) return
      const reordered = siblings.slice()
      const [removed] = reordered.splice(idx, 1)
      reordered.splice(newIdx, 0, removed)
      const withNewPositions = tree.map((p) => {
        const i = reordered.findIndex((r) => r.id === p.id)
        return i >= 0 ? { ...p, position: i } : p
      })
      onTreeChange(withNewPositions)
    },
    [tree, onTreeChange]
  )

  const removePage = useCallback(
    (page: LogosPageMeta) => {
      const toRemove = [page.id, ...tree.filter((p) => p.parentId === page.id).map((p) => p.id)]
      onTreeChange(tree.filter((p) => !toRemove.includes(p.id)))
      toRemove.forEach((id) => onDeletePage?.(id))
      if (activePageId && toRemove.includes(activePageId)) {
        const remaining = tree.filter((p) => !toRemove.includes(p.id)).sort(sortByPosition)
        const first = remaining[0]
        if (first) onSelectPage(first.id)
      }
      setDeleteTarget(null)
    },
    [tree, activePageId, onTreeChange, onDeletePage, onSelectPage]
  )

  const subpages = (parentId: LogosPageId) =>
    tree.filter((p) => p.parentId === parentId).sort(sortByPosition)

  const isPage = (p: LogosPageMeta) => p.parentId === null

  return (
    <div className="flex h-full w-64 shrink-0 flex-col border-r border-border bg-muted/30">
      <div className="flex shrink-0 items-center justify-between border-b border-border/60 px-2 py-2">
        <span className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
          Pages
        </span>
        <Button
          type="button"
          variant="ghost"
          size="sm"
          className="h-7 gap-1 px-2 text-xs"
          onClick={addPage}
        >
          <Plus className="size-3.5" />
          New page
        </Button>
      </div>
      <div className="flex-1 overflow-y-auto py-1">
        {pages.length === 0 ? (
          <div className="px-3 py-4 text-center text-xs text-muted-foreground">
            No pages yet.
            <br />
            <Button
              type="button"
              variant="link"
              className="h-auto p-0 text-xs"
              onClick={addPage}
            >
              Create your first page
            </Button>
          </div>
        ) : (
          <ul className="flex flex-col gap-0.5 px-1">
            {pages.map((page) => {
              const subs = subpages(page.id)
              const expanded = expandedPages.has(page.id)
              const isActive = activePageId === page.id
              return (
                <li key={page.id} className="flex flex-col gap-0.5">
                  <PageRow
                    page={page}
                    isSubpage={false}
                    isActive={isActive}
                    isEditing={editingId === page.id}
                    editTitle={editTitle}
                    onEditTitleChange={setEditTitle}
                    onCommitRename={commitRename}
                    onStartRename={() => startRename(page)}
                    onSelect={() => onSelectPage(page.id)}
                    onExpandToggle={() => toggleExpanded(page.id)}
                    expanded={expanded}
                    hasSubpages={subs.length > 0}
                    onAddSubpage={() => addSubpage(page.id)}
                    onMoveUp={() => movePage(page.id, -1)}
                    onMoveDown={() => movePage(page.id, 1)}
                    onDelete={() => setDeleteTarget(page)}
                    canMoveUp={pages.indexOf(page) > 0}
                    canMoveDown={pages.indexOf(page) < pages.length - 1}
                  />
                  {expanded && (
                    <ul className="ml-3 flex flex-col gap-0.5 border-l border-border/60 pl-2">
                      {subs.map((sub, i) => {
                        const subActive = activePageId === sub.id
                        const subEditing = editingId === sub.id
                        return (
                          <li key={sub.id}>
                            <PageRow
                              page={sub}
                              isSubpage
                              isActive={subActive}
                              isEditing={subEditing}
                              editTitle={editTitle}
                              onEditTitleChange={setEditTitle}
                              onCommitRename={commitRename}
                              onStartRename={() => startRename(sub)}
                              onSelect={() => onSelectPage(sub.id)}
                              onAddSubpage={undefined}
                              onMoveUp={() => movePage(sub.id, -1)}
                              onMoveDown={() => movePage(sub.id, 1)}
                              onDelete={() => setDeleteTarget(sub)}
                              canMoveUp={i > 0}
                              canMoveDown={i < subs.length - 1}
                            />
                          </li>
                        )
                      })}
                    </ul>
                  )}
                </li>
              )
            })}
          </ul>
        )}
      </div>

      <Dialog open={!!deleteTarget} onOpenChange={(open) => !open && setDeleteTarget(null)}>
        <DialogContent className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>
              {deleteTarget && isPage(deleteTarget)
                ? 'Delete page and subpages?'
                : 'Delete subpage?'}
            </DialogTitle>
          </DialogHeader>
          {deleteTarget && (
            <p className="text-sm text-muted-foreground">
              {isPage(deleteTarget) && subpages(deleteTarget.id).length > 0 ? (
                <>
                  &quot;{deleteTarget.title}&quot; and its {subpages(deleteTarget.id).length}{' '}
                  subpage(s) will be permanently removed.
                </>
              ) : (
                <> &quot;{deleteTarget.title}&quot; will be permanently removed.</>
              )}
            </p>
          )}
          <DialogFooter className="gap-2 sm:gap-0">
            <Button type="button" variant="outline" onClick={() => setDeleteTarget(null)}>
              Cancel
            </Button>
            <Button
              type="button"
              variant="destructive"
              onClick={() => deleteTarget && removePage(deleteTarget)}
            >
              Delete
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  )
}

type PageRowProps = {
  page: LogosPageMeta
  isSubpage: boolean
  isActive: boolean
  isEditing: boolean
  editTitle: string
  onEditTitleChange: (v: string) => void
  onCommitRename: () => void
  onStartRename: () => void
  onSelect: () => void
  onExpandToggle?: () => void
  expanded?: boolean
  hasSubpages?: boolean
  onAddSubpage?: () => void
  onMoveUp: () => void
  onMoveDown: () => void
  onDelete: () => void
  canMoveUp: boolean
  canMoveDown: boolean
}

function PageRow({
  page,
  isSubpage,
  isActive,
  isEditing,
  editTitle,
  onEditTitleChange,
  onCommitRename,
  onStartRename,
  onSelect,
  onExpandToggle,
  expanded,
  hasSubpages,
  onAddSubpage,
  onMoveUp,
  onMoveDown,
  onDelete,
  canMoveUp,
  canMoveDown,
}: PageRowProps) {
  const isParentRow = onExpandToggle != null

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter') onCommitRename()
    if (e.key === 'Escape') {
      onEditTitleChange(page.title)
      onCommitRename()
    }
  }

  return (
    <div
      className={cn(
        'group flex items-center gap-0.5 rounded-md pr-1',
        isActive && 'bg-accent text-accent-foreground'
      )}
    >
      {isParentRow ? (
        // VSCode-like "twisty gutter" so labels align nicely.
        <span className="flex h-8 w-4 shrink-0 items-center justify-center">
          {hasSubpages ? (
            <button
              type="button"
              className="flex size-4 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1"
              onClick={(e) => {
                e.stopPropagation()
                e.preventDefault()
                onExpandToggle?.()
              }}
              aria-expanded={expanded}
              aria-label={expanded ? 'Collapse subpages' : 'Expand subpages'}
            >
              {expanded ? (
                <ChevronDown className="size-3.5 shrink-0" />
              ) : (
                <ChevronRight className="size-3.5 shrink-0" />
              )}
            </button>
          ) : (
            <span className="inline-block size-4" aria-hidden />
          )}
        </span>
      ) : isSubpage ? (
        <span className="flex h-8 w-4 shrink-0 items-center justify-center">
          <span className="inline-block size-4" aria-hidden />
        </span>
      ) : null}
      {isSubpage && <span className="w-2 shrink-0" />}
      {isEditing ? (
        <input
          type="text"
          className="h-7 min-w-0 flex-1 rounded border border-input bg-background px-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
          value={editTitle}
          onChange={(e) => onEditTitleChange(e.target.value)}
          onBlur={onCommitRename}
          onKeyDown={handleKeyDown}
          autoFocus
          aria-label="Page title"
        />
      ) : (
        <button
          type="button"
          className="flex min-w-0 flex-1 items-center gap-1.5 rounded px-1.5 py-1.5 text-left text-sm hover:bg-accent/80"
          onClick={onSelect}
        >
          <FileText className="size-3.5 shrink-0 text-muted-foreground" />
          <span className="truncate">{page.title || DEFAULT_PAGE_TITLE}</span>
        </button>
      )}
      {!isEditing && (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button
              type="button"
              variant="ghost"
              size="icon"
              className="h-6 w-6 shrink-0 opacity-0 group-hover:opacity-100"
              onClick={(e) => e.stopPropagation()}
            >
              <MoreHorizontal className="size-3.5" />
              <span className="sr-only">Actions</span>
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end" className="w-48" onClick={(e) => e.stopPropagation()}>
            <DropdownMenuItem onClick={() => { onStartRename(); onSelect(); }}>
              <Pencil className="mr-2 size-3.5" />
              Rename
            </DropdownMenuItem>
            {onAddSubpage != null && (
              <DropdownMenuItem onClick={onAddSubpage}>
                <Plus className="mr-2 size-3.5" />
                New subpage
              </DropdownMenuItem>
            )}
            <DropdownMenuSeparator />
            <DropdownMenuItem onClick={onMoveUp} disabled={!canMoveUp}>
              <ArrowUp className="mr-2 size-3.5" />
              Move up
            </DropdownMenuItem>
            <DropdownMenuItem onClick={onMoveDown} disabled={!canMoveDown}>
              <ArrowDown className="mr-2 size-3.5" />
              Move down
            </DropdownMenuItem>
            <DropdownMenuSeparator />
            <DropdownMenuItem onClick={onDelete} className="text-destructive focus:text-destructive">
              <Trash2 className="mr-2 size-3.5" />
              Delete
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      )}
    </div>
  )
}
