"use client"

import Image from "next/image"
import { useEffect, useState, useCallback, useRef, useSyncExternalStore } from "react"
import {
  FolderOpen,
  Loader2,
  AlertCircle,
  Layers,
  Sparkles,
  ArrowUpCircle,
  GitBranch,
  CheckCircle2,
  FolderRoot,
  Plus,
  ArrowRight,
  X,
  ChevronRight,
  Folder,
  CornerLeftUp,
  Search,
  Clock,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { useProjectStoreManager } from "@/lib/project-store-manager"
import {
  useSFWorkspaceState,
  getLiveWorkspaceIndex,
  getLiveAutoDashboard,
  formatCost,
  getCurrentSlice,
} from "@/lib/sf-workspace-store"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
} from "@/components/ui/sheet"
import { ScrollArea } from "@/components/ui/scroll-area"
import { authFetch } from "@/lib/auth"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog"

// ─── Types (mirroring server-side ProjectMetadata) ─────────────────────────

type ProjectDetectionKind = "active-sf" | "empty-sf" | "v1-legacy" | "brownfield" | "blank"

interface ProjectDetectionSignals {
  hasSfFolder: boolean
  hasPlanningFolder: boolean
  hasGitRepo: boolean
  hasPackageJson: boolean
  fileCount: number
  hasMilestones?: boolean
  hasCargo?: boolean
  hasGoMod?: boolean
  hasPyproject?: boolean
  isMonorepo?: boolean
}

interface ProjectProgressInfo {
  activeMilestone: string | null
  activeSlice: string | null
  phase: string | null
  milestonesCompleted: number
  milestonesTotal: number
}

interface ProjectMetadata {
  name: string
  path: string
  kind: ProjectDetectionKind
  signals: ProjectDetectionSignals
  lastModified: number
  progress?: ProjectProgressInfo | null
}

// ─── Kind style config ─────────────────────────────────────────────────

const KIND_STYLE: Record<ProjectDetectionKind, { label: string; color: string; bgClass: string; icon: typeof Layers }> = {
  "active-sf": {
    label: "Active",
    color: "text-success",
    bgClass: "bg-success/10",
    icon: Layers,
  },
  "empty-sf": {
    label: "Initialized",
    color: "text-info",
    bgClass: "bg-info/10",
    icon: FolderOpen,
  },
  brownfield: {
    label: "Existing",
    color: "text-warning",
    bgClass: "bg-warning/10",
    icon: GitBranch,
  },
  "v1-legacy": {
    label: "Legacy",
    color: "text-warning",
    bgClass: "bg-warning/10",
    icon: ArrowUpCircle,
  },
  blank: {
    label: "New",
    color: "text-muted-foreground",
    bgClass: "bg-foreground/[0.04]",
    icon: Sparkles,
  },
}

function techStack(signals: ProjectDetectionSignals): string[] {
  const tags: string[] = []
  if (signals.isMonorepo) tags.push("Monorepo")
  if (signals.hasGitRepo) tags.push("Git")
  if (signals.hasPackageJson) tags.push("Node.js")
  if (signals.hasCargo) tags.push("Rust")
  if (signals.hasGoMod) tags.push("Go")
  if (signals.hasPyproject) tags.push("Python")
  return tags
}

function progressLabel(p: ProjectProgressInfo): string | null {
  if (p.milestonesTotal === 0) return null
  const parts: string[] = []
  if (p.activeMilestone) parts.push(p.activeMilestone)
  if (p.activeSlice) parts.push(p.activeSlice)
  if (p.phase) parts.push(p.phase)
  return parts.join(" · ") || null
}

function relativeTime(timestamp: number): string {
  const now = Date.now()
  const diffMs = now - timestamp
  if (diffMs < 60_000) return "just now"
  const minutes = Math.floor(diffMs / 60_000)
  if (minutes < 60) return `${minutes}m ago`
  const hours = Math.floor(minutes / 60)
  if (hours < 24) return `${hours}h ago`
  const days = Math.floor(hours / 24)
  if (days < 30) return `${days}d ago`
  return new Date(timestamp).toLocaleDateString(undefined, { month: "short", day: "numeric" })
}

// ─── Shared project card component ─────────────────────────────────────

function ProjectCard({
  project,
  isActive = false,
  onClick,
  disabled = false,
}: {
  project: ProjectMetadata
  isActive?: boolean
  onClick: () => void
  disabled?: boolean
}) {
  const style = KIND_STYLE[project.kind]
  const KindIcon = style.icon
  const stack = techStack(project.signals)
  const progress = project.progress ? progressLabel(project.progress) : null
  const milestoneCount = project.progress
    ? `${project.progress.milestonesCompleted}/${project.progress.milestonesTotal}`
    : null

  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      className={cn(
        "group flex w-full items-start gap-3.5 rounded-xl border px-4 py-3.5 text-left transition-all duration-200",
        "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
        "active:scale-[0.98]",
        isActive
          ? "border-primary/30 bg-primary/[0.08]"
          : "border-border/50 bg-card/50 hover:border-foreground/15 hover:bg-card/50",
        disabled && "opacity-40 pointer-events-none",
      )}
    >
      {/* Icon */}
      <div
        className={cn(
          "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg mt-0.5",
          isActive ? "bg-primary/15" : style.bgClass,
        )}
      >
        {isActive ? (
          <CheckCircle2 className="h-4 w-4 text-primary" />
        ) : (
          <KindIcon className={cn("h-4 w-4", style.color)} />
        )}
      </div>

      {/* Content */}
      <div className="min-w-0 flex-1">
        {/* Row 1: name + kind badge */}
        <div className="flex items-center gap-2">
          <span className="text-sm font-semibold text-foreground truncate">{project.name}</span>
          <span className={cn("text-[10px] font-medium shrink-0", isActive ? "text-primary" : style.color)}>
            {isActive ? "Current" : style.label}
          </span>
        </div>

        {/* Row 2: tech stack tags */}
        {stack.length > 0 && (
          <div className="mt-1 flex items-center gap-1.5">
            {stack.map((tag) => (
              <span
                key={tag}
                className="rounded bg-foreground/[0.06] px-1.5 py-0.5 text-[10px] text-muted-foreground"
              >
                {tag}
              </span>
            ))}
          </div>
        )}

        {/* Row 3: progress info */}
        {progress && (
          <div className="mt-1.5 text-[11px] text-muted-foreground">{progress}</div>
        )}

        {/* Row 4: milestone progress bar */}
        {project.progress && project.progress.milestonesTotal > 0 && (
          <div className="mt-2 flex items-center gap-2">
            <div className="h-1 flex-1 overflow-hidden rounded-full bg-foreground/[0.08]">
              <div
                className="h-full rounded-full bg-success/70 transition-all"
                style={{
                  width: `${Math.round(
                    (project.progress.milestonesCompleted / project.progress.milestonesTotal) * 100,
                  )}%`,
                }}
              />
            </div>
            <span className="text-[10px] tabular-nums text-muted-foreground">{milestoneCount}</span>
          </div>
        )}
      </div>

      {/* Arrow */}
      <ArrowRight className="mt-1 h-4 w-4 shrink-0 text-muted-foreground/50 transition-all group-hover:text-muted-foreground group-hover:translate-x-0.5" />
    </button>
  )
}

// ─── ProjectsPanel (slide-out sheet from sidebar) ──────────────────────

export function ProjectsPanel({
  open,
  onOpenChange,
}: {
  open: boolean
  onOpenChange: (open: boolean) => void
}) {
  const manager = useProjectStoreManager()
  const activeProjectCwd = useSyncExternalStore(manager.subscribe, manager.getSnapshot, manager.getSnapshot)

  const [projects, setProjects] = useState<ProjectMetadata[]>([])
  const [devRoot, setDevRoot] = useState<string | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)

  const loadProjects = useCallback(async (root: string) => {
    const projRes = await authFetch(`/api/projects?root=${encodeURIComponent(root)}&detail=true`)
    if (!projRes.ok) throw new Error(`Failed to discover projects: ${projRes.status}`)
    return (await projRes.json()) as ProjectMetadata[]
  }, [])

  // Load projects when panel opens
  useEffect(() => {
    if (!open) return
    let cancelled = false

    async function load() {
      setLoading(true)
      setError(null)
      try {
        const prefsRes = await authFetch("/api/preferences")
        if (!prefsRes.ok) throw new Error(`Failed to load preferences: ${prefsRes.status}`)
        const prefs = await prefsRes.json()

        if (!prefs.devRoot) {
          setDevRoot(null)
          setProjects([])
          setLoading(false)
          return
        }

        setDevRoot(prefs.devRoot)
        const discovered = await loadProjects(prefs.devRoot)
        if (!cancelled) setProjects(discovered)
      } catch (err) {
        if (!cancelled) {
          setError(err instanceof Error ? err.message : "Unknown error")
        }
      } finally {
        if (!cancelled) setLoading(false)
      }
    }

    load()
    return () => {
      cancelled = true
    }
  }, [open, loadProjects])

  const handleDevRootSaved = useCallback(
    async (newRoot: string) => {
      setLoading(true)
      setError(null)
      try {
        // Validate path and persist in a single call
        const res = await authFetch("/api/switch-root", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ devRoot: newRoot }),
        })

        if (!res.ok) {
          const body = await res.json().catch(() => ({}))
          throw new Error((body as { error?: string }).error ?? `Request failed (${res.status})`)
        }

        const data = await res.json() as { devRoot: string; projects: ProjectMetadata[] }
        setDevRoot(data.devRoot)
        setProjects(data.projects)
      } catch (err) {
        setError(err instanceof Error ? err.message : "Failed to switch project root")
      } finally {
        setLoading(false)
      }
    },
    [],
  )

  const [newProjectOpen, setNewProjectOpen] = useState(false)
  const [changeRootOpen, setChangeRootOpen] = useState(false)
  const workspaceState = useSFWorkspaceState()

  const handleProjectCreated = useCallback(
    (newProject: ProjectMetadata) => {
      setProjects((prev) => [...prev, newProject].sort((a, b) => a.name.localeCompare(b.name)))
      setNewProjectOpen(false)
      handleSelectProject(newProject)
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [],
  )

  function handleSelectProject(project: ProjectMetadata) {
    // Already active — just close the panel
    if (activeProjectCwd === project.path) {
      onOpenChange(false)
      return
    }

    // Close panel immediately — boot happens in the background with a
    // loading toast managed by WorkspaceChrome
    onOpenChange(false)
    manager.switchProject(project.path)
  }

  // Sort: active-sf first, then by name
  const sortedProjects = [...projects].sort((a, b) => {
    const kindOrder: Record<ProjectDetectionKind, number> = {
      "active-sf": 0,
      "empty-sf": 1,
      brownfield: 2,
      "v1-legacy": 3,
      blank: 4,
    }
    const ka = kindOrder[a.kind] ?? 5
    const kb = kindOrder[b.kind] ?? 5
    if (ka !== kb) return ka - kb
    return a.name.localeCompare(b.name)
  })

  // ─── Content for the various states ──────────────────────────────

  let content: React.ReactNode

  if (loading) {
    content = (
      <div className="flex items-center justify-center gap-2 py-16 text-xs text-muted-foreground">
        <Loader2 className="h-4 w-4 animate-spin" />
        Discovering projects…
      </div>
    )
  } else if (error) {
    content = (
      <div className="flex flex-col items-center gap-3 px-5 py-16 text-center">
        <AlertCircle className="h-8 w-8 text-destructive" />
        <p className="text-sm text-destructive">{error}</p>
      </div>
    )
  } else if (!devRoot) {
    content = <DevRootSetup onSaved={handleDevRootSaved} />
  } else if (sortedProjects.length === 0) {
    content = (
      <div className="flex flex-col items-center gap-4 px-5 py-16 text-center">
        <div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-muted">
          <FolderOpen className="h-7 w-7 text-muted-foreground" />
        </div>
        <div className="space-y-2">
          <h3 className="text-base font-semibold text-foreground">No projects found</h3>
          <p className="text-sm text-muted-foreground leading-relaxed">
            No project directories discovered in{" "}
            <code className="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">
              {devRoot}
            </code>
          </p>
        </div>
      </div>
    )
  } else {
    content = (
      <div className="space-y-2">
        {/* Project cards */}
        {sortedProjects.map((project) => (
          <ProjectCard
            key={project.path}
            project={project}
            isActive={activeProjectCwd === project.path}
            onClick={() => handleSelectProject(project)}
          />
        ))}

        {/* Create new project button */}
        <button
          type="button"
          onClick={() => setNewProjectOpen(true)}
          className={cn(
            "flex w-full items-center gap-3.5 rounded-xl border border-dashed px-4 py-3.5 text-left transition-all duration-200",
            "border-border/50 text-muted-foreground hover:border-foreground/15 hover:text-foreground",
            "active:scale-[0.98]",
          )}
        >
          <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-foreground/[0.04]">
            <Plus className="h-4 w-4" />
          </div>
          <div>
            <span className="text-sm font-medium">Create new project</span>
            <p className="mt-0.5 text-[11px] text-muted-foreground">Initialize a new directory with Git</p>
          </div>
        </button>

        {/* New project dialog */}
        <NewProjectDialog
          open={newProjectOpen}
          onOpenChange={setNewProjectOpen}
          devRoot={devRoot}
          existingNames={projects.map((p) => p.name)}
          onCreated={handleProjectCreated}
        />
      </div>
    )
  }

  return (
    <Sheet open={open} onOpenChange={onOpenChange}>
      <SheetContent side="left" className="flex h-full w-full flex-col p-0 sm:max-w-[420px]" data-testid="projects-panel">
        <SheetHeader className="sr-only">
          <SheetTitle>Projects</SheetTitle>
          <SheetDescription>Switch between projects or create a new one</SheetDescription>
        </SheetHeader>

        {/* Visible header */}
        <div className="flex items-center justify-between border-b border-border/50 px-5 py-4">
          <div>
            <h2 className="text-base font-semibold text-foreground">Projects</h2>
            {devRoot && !loading && (
              <div className="mt-0.5 flex items-center gap-1.5 text-xs text-muted-foreground">
                <code className="rounded bg-muted px-1 py-0.5 font-mono text-[10px] truncate max-w-[200px]">{devRoot}</code>
                <button
                  type="button"
                  onClick={() => setChangeRootOpen(true)}
                  className="shrink-0 text-[10px] text-primary hover:text-primary/80 transition-colors font-medium"
                  data-testid="projects-panel-change-root"
                >
                  Change
                </button>
                <span className="text-muted-foreground">·</span>
                <span>{projects.length} project{projects.length !== 1 ? "s" : ""}</span>
              </div>
            )}
          </div>
          <Button variant="ghost" size="icon" className="h-8 w-8 shrink-0" onClick={() => onOpenChange(false)}>
            <X className="h-4 w-4" />
          </Button>
        </div>

        {/* Scrollable project list */}
        <ScrollArea className="min-h-0 flex-1">
          <div className="px-5 py-4">{content}</div>
        </ScrollArea>

        {/* Folder picker for changing dev root */}
        <FolderPickerDialog
          open={changeRootOpen}
          onOpenChange={setChangeRootOpen}
          onSelect={(path) => void handleDevRootSaved(path)}
          initialPath={devRoot}
        />
      </SheetContent>
    </Sheet>
  )
}

// ─── Active project inline summary (compact for panel card) ────────────

function ActiveProjectSummary({ workspaceState }: { workspaceState: ReturnType<typeof useSFWorkspaceState> }) {
  const workspace = getLiveWorkspaceIndex(workspaceState)
  const dashboard = getLiveAutoDashboard(workspaceState)
  const currentSlice = getCurrentSlice(workspace)

  if (!workspace) return null

  const activeMilestone = workspace.milestones.find((m) => m.id === workspace.active.milestoneId)
  const cost = dashboard?.totalCost ?? 0

  const parts: string[] = []
  if (activeMilestone) parts.push(activeMilestone.id)
  if (currentSlice) parts.push(currentSlice.id)
  if (cost > 0) parts.push(formatCost(cost))

  if (parts.length === 0) return null

  return <div className="mt-1.5 text-[11px] text-muted-foreground">{parts.join(" · ")}</div>
}

// ─── New Project Dialog ────────────────────────────────────────────────

function NewProjectDialog({
  open,
  onOpenChange,
  devRoot,
  existingNames,
  onCreated,
}: {
  open: boolean
  onOpenChange: (open: boolean) => void
  devRoot: string
  existingNames: string[]
  onCreated: (project: ProjectMetadata) => void
}) {
  const [name, setName] = useState("")
  const [creating, setCreating] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const inputRef = useRef<HTMLInputElement>(null)

  useEffect(() => {
    if (open) {
      setName("")
      setError(null)
      setCreating(false)
      const t = setTimeout(() => inputRef.current?.focus(), 100)
      return () => clearTimeout(t)
    }
  }, [open])

  const nameValid = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)
  const nameConflict = existingNames.includes(name)
  const canSubmit = name.length > 0 && nameValid && !nameConflict && !creating

  const validationHint = (() => {
    if (!name) return null
    if (nameConflict) return "A project with this name already exists"
    if (!nameValid) return "Use letters, numbers, hyphens, underscores, dots. Must start with a letter or number."
    return null
  })()

  async function handleCreate() {
    if (!canSubmit) return
    setCreating(true)
    setError(null)
    try {
      const res = await authFetch("/api/projects", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ devRoot, name }),
      })
      if (!res.ok) {
        const body = await res.json().catch(() => ({}))
        throw new Error((body as { error?: string }).error ?? `Failed (${res.status})`)
      }
      const project = (await res.json()) as ProjectMetadata
      onCreated(project)
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to create project")
      setCreating(false)
    }
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-md">
        <DialogHeader>
          <DialogTitle>New Project</DialogTitle>
          <DialogDescription>
            Create a new project directory in{" "}
            <code className="rounded bg-muted px-1 py-0.5 text-xs font-mono">{devRoot}</code>
          </DialogDescription>
        </DialogHeader>

        <form
          onSubmit={(e) => {
            e.preventDefault()
            void handleCreate()
          }}
          className="space-y-4 py-2"
        >
          <div className="space-y-2">
            <Label htmlFor="project-name">Project name</Label>
            <Input
              ref={inputRef}
              id="project-name"
              placeholder="my-project"
              value={name}
              onChange={(e) => {
                setName(e.target.value)
                setError(null)
              }}
              autoComplete="off"
              aria-invalid={!!validationHint}
            />
            {validationHint && <p className="text-xs text-destructive">{validationHint}</p>}
            {error && <p className="text-xs text-destructive">{error}</p>}
            {name && nameValid && !nameConflict && (
              <p className="text-xs text-muted-foreground font-mono">
                {devRoot}/{name}
              </p>
            )}
          </div>
        </form>

        <DialogFooter>
          <Button variant="ghost" size="sm" onClick={() => onOpenChange(false)} disabled={creating}>
            Cancel
          </Button>
          <Button size="sm" onClick={() => void handleCreate()} disabled={!canSubmit} className="gap-1.5">
            {creating ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Plus className="h-3.5 w-3.5" />}
            Create
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

// ─── Folder Picker Dialog ───────────────────────────────────────────────

interface BrowseEntry {
  name: string
  path: string
}

interface BrowseResult {
  current: string
  parent: string | null
  entries: BrowseEntry[]
}

function FolderPickerDialog({
  open,
  onOpenChange,
  onSelect,
  initialPath,
}: {
  open: boolean
  onOpenChange: (open: boolean) => void
  onSelect: (path: string) => void
  initialPath?: string | null
}) {
  const [currentPath, setCurrentPath] = useState<string>("")
  const [parentPath, setParentPath] = useState<string | null>(null)
  const [entries, setEntries] = useState<BrowseEntry[]>([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const browse = useCallback(async (targetPath?: string) => {
    setLoading(true)
    setError(null)
    try {
      const param = targetPath ? `?path=${encodeURIComponent(targetPath)}` : ""
      const res = await authFetch(`/api/browse-directories${param}`)
      if (!res.ok) {
        const body = await res.json().catch(() => ({}))
        throw new Error((body as { error?: string }).error ?? `${res.status}`)
      }
      const data: BrowseResult = await res.json()
      setCurrentPath(data.current)
      setParentPath(data.parent)
      setEntries(data.entries)
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to browse")
    } finally {
      setLoading(false)
    }
  }, [])

  useEffect(() => {
    if (open) {
      void browse(initialPath ?? undefined)
    }
  }, [open, initialPath, browse])

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-lg gap-0 p-0 overflow-hidden">
        <DialogHeader className="px-5 pt-5 pb-3">
          <DialogTitle className="text-base">Choose Folder</DialogTitle>
          <DialogDescription className="text-xs">
            Navigate to the folder that contains your project directories.
          </DialogDescription>
        </DialogHeader>

        <div className="border-y border-border/50 bg-muted/50 px-5 py-2">
          <p className="font-mono text-xs text-muted-foreground truncate" title={currentPath}>
            {currentPath}
          </p>
        </div>

        <ScrollArea className="h-[320px]">
          <div className="px-2 py-1">
            {loading && (
              <div className="flex items-center justify-center py-12">
                <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
              </div>
            )}

            {error && <div className="px-3 py-4 text-center text-xs text-destructive">{error}</div>}

            {!loading && !error && (
              <>
                {parentPath && (
                  <button
                    onClick={() => void browse(parentPath)}
                    className="flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm transition-colors hover:bg-accent/50"
                  >
                    <CornerLeftUp className="h-4 w-4 text-muted-foreground shrink-0" />
                    <span className="text-muted-foreground">..</span>
                  </button>
                )}

                {entries.map((entry) => (
                  <button
                    key={entry.path}
                    onClick={() => void browse(entry.path)}
                    className="flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm transition-colors hover:bg-accent/50 group"
                  >
                    <Folder className="h-4 w-4 text-muted-foreground shrink-0" />
                    <span className="text-foreground truncate flex-1">{entry.name}</span>
                    <ChevronRight className="h-3.5 w-3.5 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0" />
                  </button>
                ))}

                {!parentPath && entries.length === 0 && (
                  <div className="px-3 py-8 text-center text-xs text-muted-foreground">No subdirectories</div>
                )}
              </>
            )}
          </div>
        </ScrollArea>

        <DialogFooter className="border-t border-border/50 px-5 py-3">
          <Button variant="ghost" size="sm" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            size="sm"
            onClick={() => {
              onSelect(currentPath)
              onOpenChange(false)
            }}
            disabled={!currentPath}
            className="gap-1.5"
          >
            <FolderOpen className="h-3.5 w-3.5" />
            Select This Folder
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

// ─── Dev Root Setup Component ───────────────────────────────────────────

function DevRootSetup({
  onSaved,
  currentRoot,
}: {
  onSaved: (root: string) => void
  currentRoot?: string | null
}) {
  const [saving, setSaving] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [success, setSuccess] = useState(false)
  const [pickerOpen, setPickerOpen] = useState(false)

  const handleSave = useCallback(
    async (selectedPath: string) => {
      setSaving(true)
      setError(null)
      setSuccess(false)

      try {
        const res = await authFetch("/api/preferences", {
          method: "PUT",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ devRoot: selectedPath }),
        })

        if (!res.ok) {
          const body = await res.json().catch(() => ({}))
          throw new Error((body as { error?: string }).error ?? `Request failed (${res.status})`)
        }

        setSuccess(true)
        onSaved(selectedPath)
      } catch (err) {
        setError(err instanceof Error ? err.message : "Failed to save preference")
      } finally {
        setSaving(false)
      }
    },
    [onSaved],
  )

  const isCompact = !!currentRoot

  if (isCompact) {
    return (
      <div className="space-y-3" data-testid="devroot-settings">
        <div className="flex items-center gap-2">
          <code className="flex-1 truncate rounded border border-border/50 bg-muted/50 px-3 py-2 font-mono text-xs text-foreground">
            {currentRoot}
          </code>
          <Button
            size="sm"
            variant="outline"
            onClick={() => setPickerOpen(true)}
            disabled={saving}
            className="h-9 gap-1.5 shrink-0"
          >
            {saving ? (
              <Loader2 className="h-3.5 w-3.5 animate-spin" />
            ) : success ? (
              <CheckCircle2 className="h-3.5 w-3.5 text-success" />
            ) : (
              <>
                <FolderOpen className="h-3.5 w-3.5" />
                Change
              </>
            )}
          </Button>
        </div>

        {error && <p className="text-xs text-destructive">{error}</p>}
        {success && <p className="text-xs text-success">Dev root updated</p>}

        <FolderPickerDialog
          open={pickerOpen}
          onOpenChange={setPickerOpen}
          onSelect={(path) => void handleSave(path)}
          initialPath={currentRoot}
        />
      </div>
    )
  }

  // Inline setup for first-time configuration
  return (
    <div className="rounded-md border border-border bg-card p-6">
      <div className="flex items-start gap-4">
        <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md border border-border bg-accent">
          <FolderRoot className="h-5 w-5 text-muted-foreground" />
        </div>
        <div className="min-w-0 flex-1">
          <h3 className="text-sm font-semibold text-foreground">Set your development root</h3>
          <p className="mt-1 text-xs text-muted-foreground leading-relaxed">
            Point SF at the folder that contains your project directories. It scans one level deep.
          </p>
          <Button
            onClick={() => setPickerOpen(true)}
            disabled={saving}
            size="sm"
            className="mt-3 gap-2"
            data-testid="projects-devroot-browse"
          >
            {saving ? (
              <Loader2 className="h-3.5 w-3.5 animate-spin" />
            ) : (
              <>
                <FolderOpen className="h-3.5 w-3.5" />
                Browse
              </>
            )}
          </Button>
          {error && <p className="mt-2 text-xs text-destructive">{error}</p>}
        </div>
      </div>

      <FolderPickerDialog open={pickerOpen} onOpenChange={setPickerOpen} onSelect={(path) => void handleSave(path)} />
    </div>
  )
}

// ─── Exported Dev Root Section for Settings ──────────────────────────────

export function DevRootSettingsSection() {
  const [devRoot, setDevRoot] = useState<string | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    authFetch("/api/preferences")
      .then((r) => r.json())
      .then((prefs) => setDevRoot(prefs.devRoot ?? null))
      .catch(() => setDevRoot(null))
      .finally(() => setLoading(false))
  }, [])

  if (loading) {
    return (
      <div className="flex items-center gap-2 py-4 text-xs text-muted-foreground">
        <Loader2 className="h-3.5 w-3.5 animate-spin" />
        Loading preferences…
      </div>
    )
  }

  return (
    <div className="space-y-3" data-testid="settings-devroot">
      <div className="flex items-center gap-2.5">
        <FolderRoot className="h-3.5 w-3.5 text-muted-foreground" />
        <h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
          Development Root
        </h3>
      </div>
      <p className="text-xs text-muted-foreground leading-relaxed">
        The parent folder containing your project directories. SF scans one level deep for projects.
      </p>
      <DevRootSetup currentRoot={devRoot ?? ""} onSaved={(root) => setDevRoot(root)} />
    </div>
  )
}

// ─── Project Selection Gate ─────────────────────────────────────────────
//
// Full-screen IDE-style welcome shown before any project is opened.
// Designed to feel like opening the app — not a wizard or onboarding flow.
// Mirrors the app shell layout: header bar, sidebar-width left column,
// project list as the main content area.

export function ProjectSelectionGate() {
  const manager = useProjectStoreManager()

  const [projects, setProjects] = useState<ProjectMetadata[]>([])
  const [devRoot, setDevRoot] = useState<string | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [newProjectOpen, setNewProjectOpen] = useState(false)
  const [changeRootOpen, setChangeRootOpen] = useState(false)
  const [filter, setFilter] = useState("")

  const loadProjects = useCallback(async (root: string) => {
    const projRes = await authFetch(`/api/projects?root=${encodeURIComponent(root)}&detail=true`)
    if (!projRes.ok) throw new Error(`Failed to discover projects: ${projRes.status}`)
    return (await projRes.json()) as ProjectMetadata[]
  }, [])

  useEffect(() => {
    let cancelled = false

    async function load() {
      setLoading(true)
      setError(null)
      try {
        const prefsRes = await authFetch("/api/preferences")
        if (!prefsRes.ok) throw new Error(`Failed to load preferences: ${prefsRes.status}`)
        const prefs = await prefsRes.json()

        if (!prefs.devRoot) {
          setDevRoot(null)
          setProjects([])
          setLoading(false)
          return
        }

        setDevRoot(prefs.devRoot)
        const discovered = await loadProjects(prefs.devRoot)
        if (!cancelled) setProjects(discovered)
      } catch (err) {
        if (!cancelled) {
          setError(err instanceof Error ? err.message : "Unknown error")
        }
      } finally {
        if (!cancelled) setLoading(false)
      }
    }

    load()
    return () => {
      cancelled = true
    }
  }, [loadProjects])

  const handleDevRootSaved = useCallback(
    async (newRoot: string) => {
      setLoading(true)
      setError(null)
      try {
        const res = await authFetch("/api/switch-root", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ devRoot: newRoot }),
        })

        if (!res.ok) {
          const body = await res.json().catch(() => ({}))
          throw new Error((body as { error?: string }).error ?? `Request failed (${res.status})`)
        }

        const data = await res.json() as { devRoot: string; projects: ProjectMetadata[] }
        setDevRoot(data.devRoot)
        setProjects(data.projects)
      } catch (err) {
        setError(err instanceof Error ? err.message : "Failed to switch project root")
      } finally {
        setLoading(false)
      }
    },
    [],
  )

  const handleProjectCreated = useCallback(
    (newProject: ProjectMetadata) => {
      setProjects((prev) => [...prev, newProject].sort((a, b) => a.name.localeCompare(b.name)))
      setNewProjectOpen(false)
      manager.switchProject(newProject.path)
    },
    [manager],
  )

  function handleSelectProject(project: ProjectMetadata) {
    manager.switchProject(project.path)
  }

  // Sort: active-sf first, then by name
  const sortedProjects = [...projects].sort((a, b) => {
    const kindOrder: Record<ProjectDetectionKind, number> = {
      "active-sf": 0,
      "empty-sf": 1,
      brownfield: 2,
      "v1-legacy": 3,
      blank: 4,
    }
    const ka = kindOrder[a.kind] ?? 5
    const kb = kindOrder[b.kind] ?? 5
    if (ka !== kb) return ka - kb
    return a.name.localeCompare(b.name)
  })

  // Filter projects by name
  const filteredProjects = filter.trim()
    ? sortedProjects.filter((p) => p.name.toLowerCase().includes(filter.toLowerCase()))
    : sortedProjects

  const hasProjects = !loading && sortedProjects.length > 0
  const showFilter = sortedProjects.length > 5

  return (
    <div className="flex h-screen flex-col bg-background text-foreground" data-testid="project-selection-gate">
      {/* ─── Main content ─── */}
      <div className="flex-1 overflow-y-auto">
        <div className="mx-auto max-w-2xl px-6 pt-16 pb-10 md:px-10 lg:pt-24">

          {/* ─── Logo + subtitle ─── */}
          <div className="flex flex-col items-center text-center mb-10">
            <Image
              src="/logo-black.svg"
              alt="SF"
              width={100}
              height={28}
              className="h-7 w-auto dark:hidden"
            />
            <Image
              src="/logo-white.svg"
              alt="SF"
              width={100}
              height={28}
              className="h-7 w-auto hidden dark:block"
            />
            <p className="mt-3 text-sm text-muted-foreground">
              Select a project to get started
            </p>
          </div>

            {/* Loading */}
            {loading && (
              <div className="flex items-center gap-3 py-20 justify-center text-sm text-muted-foreground">
                <Loader2 className="h-4 w-4 animate-spin" />
                Scanning for projects…
              </div>
            )}

            {/* Error */}
            {error && !loading && (
              <div className="rounded-md border border-destructive/20 bg-destructive/[0.06] px-4 py-3 text-sm text-destructive">
                {error}
              </div>
            )}

            {/* No dev root — show setup */}
            {!devRoot && !loading && !error && (
              <div className="space-y-6">
                <div>
                  <h2 className="text-lg font-semibold tracking-tight text-foreground">
                    Welcome to SF
                  </h2>
                  <p className="mt-1 text-sm text-muted-foreground">
                    Set a development root to get started. SF will discover projects inside it.
                  </p>
                </div>
                <DevRootSetup onSaved={handleDevRootSaved} />
              </div>
            )}

            {/* No projects found */}
            {devRoot && !loading && sortedProjects.length === 0 && !error && (
              <div className="space-y-6">
                <div>
                  <h2 className="text-lg font-semibold tracking-tight text-foreground">No projects found</h2>
                  <p className="mt-1 text-sm text-muted-foreground">
                    No project directories were discovered. Create one to get started.
                  </p>
                </div>
                <button
                  type="button"
                  onClick={() => setNewProjectOpen(true)}
                  className="flex items-center gap-3 rounded-md border border-dashed border-border px-4 py-3 text-sm text-muted-foreground transition-colors hover:border-foreground/20 hover:text-foreground"
                >
                  <Plus className="h-4 w-4" />
                  Create a new project
                </button>
              </div>
            )}

            {/* ─── Project list ─── */}
            {hasProjects && (
              <div className="space-y-5">
                {/* Dev root + change button */}
                {devRoot && (
                  <div className="flex items-center gap-2 text-xs text-muted-foreground">
                    <FolderRoot className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
                    <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground truncate">{devRoot}</code>
                    <button
                      type="button"
                      onClick={() => setChangeRootOpen(true)}
                      className="shrink-0 text-[11px] text-primary hover:text-primary/80 transition-colors font-medium"
                      data-testid="gate-change-root"
                    >
                      Change
                    </button>
                  </div>
                )}

                {/* Filter + count */}
                <div className="flex items-center justify-between gap-4">
                  <p className="text-xs text-muted-foreground tabular-nums">
                    {sortedProjects.length} project{sortedProjects.length !== 1 ? "s" : ""}
                  </p>
                  {showFilter && (
                    <div className="relative w-48">
                      <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
                      <input
                        type="text"
                        placeholder="Filter…"
                        value={filter}
                        onChange={(e) => setFilter(e.target.value)}
                        className="h-8 w-full rounded-md border border-border bg-background pl-8 pr-3 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
                      />
                    </div>
                  )}
                </div>

                {/* Project rows — table-like, dense */}
                <div className="rounded-md border border-border bg-card overflow-hidden divide-y divide-border">
                  {filteredProjects.map((project) => {
                    const style = KIND_STYLE[project.kind]
                    const KindIcon = style.icon
                    const stack = techStack(project.signals)
                    const progress = project.progress ? progressLabel(project.progress) : null
                    const hasBar = project.progress && project.progress.milestonesTotal > 0
                    const pct = hasBar
                      ? Math.round((project.progress!.milestonesCompleted / project.progress!.milestonesTotal) * 100)
                      : 0

                    return (
                      <button
                        key={project.path}
                        type="button"
                        onClick={() => handleSelectProject(project)}
                        className="group flex w-full items-center gap-4 px-4 py-3 text-left transition-colors hover:bg-accent/50 focus-visible:outline-none focus-visible:bg-accent/50"
                      >
                        {/* Icon */}
                        <div className={cn("flex h-8 w-8 shrink-0 items-center justify-center rounded-md", style.bgClass)}>
                          <KindIcon className={cn("h-3.5 w-3.5", style.color)} />
                        </div>

                        {/* Name + metadata */}
                        <div className="min-w-0 flex-1">
                          <div className="flex items-center gap-2">
                            <span className="text-sm font-medium text-foreground truncate">{project.name}</span>
                            <span className={cn("text-[10px] font-medium shrink-0", style.color)}>{style.label}</span>
                          </div>
                          {/* Stack tags + progress on one line */}
                          <div className="mt-0.5 flex items-center gap-2 text-[11px] text-muted-foreground">
                            {stack.length > 0 && (
                              <span>{stack.join(" · ")}</span>
                            )}
                            {stack.length > 0 && progress && (
                              <span className="text-muted-foreground/50">—</span>
                            )}
                            {progress && (
                              <span className="truncate">{progress}</span>
                            )}
                          </div>
                        </div>

                        {/* Progress bar (compact) */}
                        {hasBar && (
                          <div className="hidden sm:flex items-center gap-2 shrink-0 w-24">
                            <div className="h-1 flex-1 overflow-hidden rounded-full bg-foreground/[0.08]">
                              <div
                                className="h-full rounded-full bg-success/70 transition-all"
                                style={{ width: `${pct}%` }}
                              />
                            </div>
                            <span className="text-[10px] tabular-nums text-muted-foreground w-6 text-right">
                              {project.progress!.milestonesCompleted}/{project.progress!.milestonesTotal}
                            </span>
                          </div>
                        )}

                        {/* Modified time */}
                        {project.lastModified > 0 && (
                          <span className="hidden lg:inline text-[10px] text-muted-foreground shrink-0 w-16 text-right tabular-nums">
                            {relativeTime(project.lastModified)}
                          </span>
                        )}

                        {/* Arrow */}
                        <ChevronRight className="h-4 w-4 shrink-0 text-muted-foreground/50 transition-colors group-hover:text-muted-foreground" />
                      </button>
                    )
                  })}

                  {/* Empty filter state */}
                  {filteredProjects.length === 0 && filter.trim() && (
                    <div className="px-4 py-8 text-center text-xs text-muted-foreground">
                      No projects matching "{filter}"
                    </div>
                  )}
                </div>

                {/* Create new row */}
                <button
                  type="button"
                  onClick={() => setNewProjectOpen(true)}
                  className="flex items-center gap-3 rounded-md border border-dashed border-border px-4 py-2.5 text-sm text-muted-foreground transition-colors hover:border-foreground/20 hover:text-foreground w-full"
                >
                  <Plus className="h-3.5 w-3.5" />
                  New project
                </button>

                {devRoot && (
                  <NewProjectDialog
                    open={newProjectOpen}
                    onOpenChange={setNewProjectOpen}
                    devRoot={devRoot}
                    existingNames={projects.map((p) => p.name)}
                    onCreated={handleProjectCreated}
                  />
                )}
              </div>
            )}

            {/* Change root for "no projects" and "no devRoot" states */}
            {devRoot && !loading && sortedProjects.length === 0 && !error && (
              <div className="mt-4">
                <button
                  type="button"
                  onClick={() => setChangeRootOpen(true)}
                  className="flex items-center gap-2 text-xs text-primary hover:text-primary/80 transition-colors font-medium"
                  data-testid="gate-change-root-empty"
                >
                  <FolderOpen className="h-3.5 w-3.5" />
                  Change project root
                </button>
              </div>
            )}
        </div>
      </div>

      {/* Folder picker for changing dev root */}
      <FolderPickerDialog
        open={changeRootOpen}
        onOpenChange={setChangeRootOpen}
        onSelect={(path) => void handleDevRootSaved(path)}
        initialPath={devRoot}
      />
    </div>
  )
}
