"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import { Archive, ArchiveRestore, ChevronRight, Loader2 } from "lucide-react"
import { useTranslations } from "next-intl"
import { toast } from "sonner"
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog"
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible"
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"
import { ScrollArea } from "@/components/ui/scroll-area"
import {
  ResizableHandle,
  ResizablePanel,
  ResizablePanelGroup,
} from "@/components/ui/resizable"
import {
  FileTree,
  FileTreeFile,
  FileTreeFolder,
} from "@/components/ai-elements/file-tree"
import { DiffViewer } from "@/components/diff/diff-viewer"
import {
  gitStashList,
  gitStashShow,
  gitStashApply,
  gitStashDrop,
  gitShowFile,
} from "@/lib/api"
import { toErrorMessage } from "@/lib/app-error"
import { languageFromPath } from "@/lib/language-detect"
import type { GitStashEntry, GitStatusEntry } from "@/lib/types"
import { cn } from "@/lib/utils"

// --- File tree types & builder (same pattern as commit-dialog) ---

interface TreeFileNode {
  kind: "file"
  name: string
  path: string
  entry: GitStatusEntry
}

interface TreeDirNode {
  kind: "dir"
  name: string
  path: string
  children: TreeNode[]
}

type TreeNode = TreeFileNode | TreeDirNode

function buildFileTree(entries: GitStatusEntry[]): TreeNode[] {
  type BuildDir = {
    name: string
    path: string
    dirs: Map<string, BuildDir>
    files: TreeFileNode[]
  }

  const root: BuildDir = {
    name: "",
    path: "",
    dirs: new Map(),
    files: [],
  }

  for (const entry of entries) {
    const parts = entry.file.split("/").filter(Boolean)
    if (parts.length === 0) continue

    let current = root
    let currentPath = ""

    for (let i = 0; i < parts.length; i += 1) {
      const part = parts[i]
      const isLeaf = i === parts.length - 1
      currentPath = currentPath ? `${currentPath}/${part}` : part

      if (isLeaf) {
        current.files.push({
          kind: "file",
          name: part,
          path: currentPath,
          entry,
        })
      } else {
        const found = current.dirs.get(part)
        if (found) {
          current = found
        } else {
          const next: BuildDir = {
            name: part,
            path: currentPath,
            dirs: new Map(),
            files: [],
          }
          current.dirs.set(part, next)
          current = next
        }
      }
    }
  }

  function sortNodes(nodes: TreeNode[]) {
    return nodes.sort((a, b) => {
      if (a.kind !== b.kind) return a.kind === "dir" ? -1 : 1
      return a.name.localeCompare(b.name)
    })
  }

  function toNodes(dir: BuildDir): TreeNode[] {
    const dirs: TreeNode[] = Array.from(dir.dirs.values()).map((child) => ({
      kind: "dir",
      name: child.name,
      path: child.path,
      children: toNodes(child),
    }))
    return sortNodes([...dirs, ...dir.files])
  }

  return toNodes(root)
}

function collectDirPaths(entries: GitStatusEntry[]) {
  const paths = new Set<string>()
  for (const entry of entries) {
    const parts = entry.file.split("/").filter(Boolean)
    if (parts.length < 2) continue
    let p = ""
    for (let i = 0; i < parts.length - 1; i += 1) {
      p = p ? `${p}/${parts[i]}` : parts[i]
      paths.add(p)
    }
  }
  return paths
}

function statusColor(status: string) {
  switch (status.charAt(0).toUpperCase()) {
    case "A":
      return "text-green-500"
    case "D":
      return "text-red-500"
    case "M":
      return "text-blue-500"
    default:
      return "text-muted-foreground"
  }
}

// --- Main component ---

interface StashWorkspaceProps {
  folderPath: string
}

export function StashWorkspace({ folderPath }: StashWorkspaceProps) {
  const t = useTranslations("Folder.branchDropdown.unstashDialog")

  const [stashes, setStashes] = useState<GitStashEntry[]>([])
  const [expandedStash, setExpandedStash] = useState<string | null>(null)
  const [stashFiles, setStashFiles] = useState<
    Record<string, GitStatusEntry[]>
  >({})
  const [filesLoading, setFilesLoading] = useState<string | null>(null)

  const [selectedFile, setSelectedFile] = useState<string | null>(null)
  const [selectedStashRef, setSelectedStashRef] = useState<string | null>(null)
  const [originalContent, setOriginalContent] = useState("")
  const [modifiedContent, setModifiedContent] = useState("")

  const [listLoading, setListLoading] = useState(false)
  const [diffLoading, setDiffLoading] = useState(false)
  const [actionLoading, setActionLoading] = useState(false)

  const loadStashes = useCallback(async () => {
    setListLoading(true)
    try {
      const list = await gitStashList(folderPath)
      setStashes(list)
    } catch (err) {
      toast.error(toErrorMessage(err))
    } finally {
      setListLoading(false)
    }
  }, [folderPath])

  useEffect(() => {
    loadStashes()
  }, [loadStashes])

  async function handleToggleStash(stashRef: string) {
    if (expandedStash === stashRef) {
      setExpandedStash(null)
      return
    }
    setExpandedStash(stashRef)

    if (!stashFiles[stashRef]) {
      setFilesLoading(stashRef)
      try {
        const fileList = await gitStashShow(folderPath, stashRef)
        setStashFiles((prev) => ({ ...prev, [stashRef]: fileList }))
      } catch (err) {
        toast.error(toErrorMessage(err))
      } finally {
        setFilesLoading(null)
      }
    }
  }

  async function handleSelectFile(stashRef: string, file: string) {
    setSelectedFile(file)
    setSelectedStashRef(stashRef)
    setDiffLoading(true)
    try {
      const [orig, mod] = await Promise.all([
        gitShowFile(folderPath, file, stashRef + "^").catch(() => ""),
        gitShowFile(folderPath, file, stashRef).catch(() => ""),
      ])
      setOriginalContent(orig)
      setModifiedContent(mod)
    } catch {
      setOriginalContent("")
      setModifiedContent("")
    } finally {
      setDiffLoading(false)
    }
  }

  async function handleApply(stashRef: string) {
    setActionLoading(true)
    try {
      await gitStashApply(folderPath, stashRef)
      toast.success(t("applySuccess"))
    } catch (err) {
      toast.error(toErrorMessage(err))
    } finally {
      setActionLoading(false)
    }
  }

  async function handleDrop(stashRef: string) {
    setActionLoading(true)
    try {
      await gitStashDrop(folderPath, stashRef)
      toast.success(t("dropSuccess"))
      if (expandedStash === stashRef) {
        setExpandedStash(null)
      }
      if (selectedStashRef === stashRef) {
        setSelectedFile(null)
        setSelectedStashRef(null)
        setOriginalContent("")
        setModifiedContent("")
      }
      setStashFiles((prev) => {
        const next = { ...prev }
        delete next[stashRef]
        return next
      })
      await loadStashes()
    } catch (err) {
      toast.error(toErrorMessage(err))
    } finally {
      setActionLoading(false)
    }
  }

  // Render file tree nodes
  function renderNode(node: TreeNode, stashRef: string): React.ReactNode {
    if (node.kind === "dir") {
      return (
        <FileTreeFolder key={node.path} name={node.name} path={node.path}>
          {node.children.map((child) => renderNode(child, stashRef))}
        </FileTreeFolder>
      )
    }

    return (
      <ContextMenu key={node.path}>
        <ContextMenuTrigger>
          <FileTreeFile
            name={node.name}
            path={node.path}
            className="gap-1 px-1.5 py-1"
          >
            <span className="flex-1 truncate text-left" title={node.path}>
              {node.name}
            </span>
            <span
              className={cn(
                "w-5 shrink-0 text-right text-xs font-bold",
                statusColor(node.entry.status)
              )}
            >
              {node.entry.status.charAt(0)}
            </span>
          </FileTreeFile>
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem
            onClick={() => handleSelectFile(stashRef, node.path)}
          >
            {t("viewDiff")}
          </ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
    )
  }

  return (
    <ResizablePanelGroup direction="horizontal" className="h-full">
      {/* Left panel: stash cards */}
      <ResizablePanel defaultSize={35} minSize={25}>
        <ScrollArea className="h-full">
          {listLoading ? (
            <div className="flex items-center justify-center py-12">
              <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
            </div>
          ) : stashes.length === 0 ? (
            <div className="flex items-center justify-center py-12 text-sm text-muted-foreground">
              {t("noStashes")}
            </div>
          ) : (
            <div className="flex flex-col gap-2 p-2">
              {stashes.map((stash) => (
                <StashCard
                  key={stash.ref_name}
                  stash={stash}
                  isExpanded={expandedStash === stash.ref_name}
                  isLoadingFiles={filesLoading === stash.ref_name}
                  actionLoading={actionLoading}
                  files={stashFiles[stash.ref_name]}
                  selectedFile={
                    selectedStashRef === stash.ref_name ? selectedFile : null
                  }
                  onToggle={() => handleToggleStash(stash.ref_name)}
                  onApply={() => handleApply(stash.ref_name)}
                  onDrop={() => handleDrop(stash.ref_name)}
                  onSelectFile={(file) =>
                    handleSelectFile(stash.ref_name, file)
                  }
                  renderNode={(node) => renderNode(node, stash.ref_name)}
                />
              ))}
            </div>
          )}
        </ScrollArea>
      </ResizablePanel>

      <ResizableHandle />

      {/* Right panel: diff viewer */}
      <ResizablePanel defaultSize={65} minSize={40}>
        {diffLoading ? (
          <div className="flex h-full items-center justify-center">
            <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
          </div>
        ) : selectedFile && selectedStashRef ? (
          <DiffViewer
            original={originalContent}
            modified={modifiedContent}
            originalLabel={`${selectedStashRef}^ (${t("original")})`}
            modifiedLabel={`${selectedStashRef} (${t("modified")})`}
            language={languageFromPath(selectedFile)}
            className="h-full"
          />
        ) : (
          <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
            {t("selectFile")}
          </div>
        )}
      </ResizablePanel>
    </ResizablePanelGroup>
  )
}

// --- Stash Card Component ---

interface StashCardProps {
  stash: GitStashEntry
  isExpanded: boolean
  isLoadingFiles: boolean
  actionLoading: boolean
  files?: GitStatusEntry[]
  selectedFile: string | null
  onToggle: () => void
  onApply: () => void
  onDrop: () => void
  onSelectFile: (file: string) => void
  renderNode: (node: TreeNode) => React.ReactNode
}

function StashCard({
  stash,
  isExpanded,
  isLoadingFiles,
  actionLoading,
  files,
  selectedFile,
  onToggle,
  onApply,
  onDrop,
  onSelectFile,
  renderNode,
}: StashCardProps) {
  const t = useTranslations("Folder.branchDropdown.unstashDialog")
  const [confirmApplyOpen, setConfirmApplyOpen] = useState(false)
  const tree = useMemo(() => (files ? buildFileTree(files) : []), [files])

  const defaultExpanded = useMemo(
    () => (files ? collectDirPaths(files) : new Set<string>()),
    [files]
  )

  return (
    <>
      <ContextMenu>
        <ContextMenuTrigger>
          <Collapsible open={isExpanded} onOpenChange={onToggle}>
            <div className="group rounded-lg border bg-card">
              <div className="relative flex items-center">
                <CollapsibleTrigger asChild>
                  <button
                    type="button"
                    className="flex w-full items-center gap-2 rounded-t-lg px-3 py-2 text-left text-sm transition-colors group-hover:bg-muted/50"
                    disabled={actionLoading}
                  >
                    <ChevronRight
                      className={cn(
                        "h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform",
                        isExpanded && "rotate-90"
                      )}
                    />
                    <Archive className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
                    <div className="min-w-0 flex-1">
                      <div className="flex items-baseline gap-2">
                        <span className="text-xs font-medium text-muted-foreground">
                          {stash.ref_name}
                        </span>
                        <span className="truncate font-medium">
                          {stash.message}
                        </span>
                      </div>
                      <div className="flex gap-2 text-[10px] text-muted-foreground/70">
                        <span>{stash.branch}</span>
                        <span>{stash.date}</span>
                      </div>
                    </div>
                  </button>
                </CollapsibleTrigger>
                <button
                  type="button"
                  className="absolute right-1.5 top-1/2 flex h-6 w-6 -translate-y-1/2 items-center justify-center rounded-md opacity-0 transition-opacity hover:bg-accent group-hover:opacity-100"
                  title={t("apply") as string}
                  onClick={() => setConfirmApplyOpen(true)}
                  disabled={actionLoading}
                >
                  <ArchiveRestore className="h-3.5 w-3.5 text-muted-foreground" />
                </button>
              </div>

              <CollapsibleContent>
                <div className="border-t">
                  {/* File tree */}
                  {isLoadingFiles ? (
                    <div className="flex items-center justify-center py-4">
                      <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
                    </div>
                  ) : tree.length > 0 ? (
                    <div className="px-2 pb-2">
                      <FileTree
                        defaultExpanded={defaultExpanded}
                        selectedPath={selectedFile ?? undefined}
                        onSelect={onSelectFile}
                        className="border-0 bg-transparent"
                      >
                        {tree.map(renderNode)}
                      </FileTree>
                    </div>
                  ) : null}
                </div>
              </CollapsibleContent>
            </div>
          </Collapsible>
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem onClick={() => setConfirmApplyOpen(true)}>
            {t("apply")}
          </ContextMenuItem>
          <ContextMenuItem variant="destructive" onClick={onDrop}>
            {t("drop")}
          </ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>

      <AlertDialog open={confirmApplyOpen} onOpenChange={setConfirmApplyOpen}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>{t("apply")}</AlertDialogTitle>
            <AlertDialogDescription>
              {t("confirmApply", { ref: stash.ref_name })}
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
            <AlertDialogAction
              onClick={() => {
                setConfirmApplyOpen(false)
                onApply()
              }}
            >
              {t("apply")}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </>
  )
}
