"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import { useRouter } from "next/navigation"
import { ReactFlow, Background, Controls, type Edge, type Node, type NodeMouseHandler } from "@xyflow/react"
import "@xyflow/react/dist/style.css"
import { useTheme } from "next-themes"
import { LayoutTemplate, Plus, Search } from "lucide-react"

import { productionTemplatesApi } from "@/lib/api/production-templates"
import { errorMessage } from "@/lib/error-map"
import { ProductionTemplateSummary, TemplateStatus } from "@/types/production"
import { PaginationMeta } from "@/types/common"
import {
  LineHeaderNodeComponent,
  LineStageNodeComponent,
  type LineHeaderData,
  type LineStageData,
} from "@/components/production/ProductionLineNodes"

import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { Field, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Skeleton } from "@/components/ui/skeleton"

type StatusFilter = TemplateStatus | "All"

const PAGE_SIZE = 25

const nodeTypes = { lineHeader: LineHeaderNodeComponent, lineStage: LineStageNodeComponent }

const ROW_HEIGHT = 150
const STAGE_START_X = 300
const STAGE_GAP_X = 200

/** One row per template — its production line, header on the left, stages left to right. */
function buildLinesGraph(templates: ProductionTemplateSummary[]): { nodes: Node[]; edges: Edge[] } {
  const nodes: Node[] = []
  const edges: Edge[] = []

  templates.forEach((t, row) => {
    const y = row * ROW_HEIGHT
    nodes.push({
      id: `h${t.templateId}`,
      type: "lineHeader",
      position: { x: 0, y },
      data: {
        templateId: t.templateId,
        code: t.code,
        name: t.name,
        status: t.status,
        activeRunCount: t.activeRunCount,
      } satisfies LineHeaderData,
      draggable: false,
    })

    // Names come straight from the list projection, so the overview needs one request
    // regardless of how many templates it shows.
    t.stageNames.forEach((stageName, i) => {
      const stageId = `s${t.templateId}-${i}`
      nodes.push({
        id: stageId,
        type: "lineStage",
        position: { x: STAGE_START_X + i * STAGE_GAP_X, y: y + 22 },
        data: { templateId: t.templateId, name: stageName } satisfies LineStageData,
        draggable: false,
      })
      edges.push({
        id: `e-${stageId}`,
        source: i === 0 ? `h${t.templateId}` : `s${t.templateId}-${i - 1}`,
        target: stageId,
      })
    })
  })

  return { nodes, edges }
}

export default function ProductionTemplatesPage() {
  const router = useRouter()
  const { resolvedTheme } = useTheme()

  // null = still loading (the codebase convention for "no data yet" vs "empty result").
  const [templates, setTemplates] = useState<ProductionTemplateSummary[] | null>(null)
  const [pagination, setPagination] = useState<PaginationMeta | null>(null)
  const [loadError, setLoadError] = useState<string | null>(null)

  const [searchInput, setSearchInput] = useState("")
  const [query, setQuery] = useState("")
  const [status, setStatus] = useState<StatusFilter>("All")
  const [page, setPage] = useState(1)

  const [open, setOpen] = useState(false)
  const [code, setCode] = useState("")
  const [name, setName] = useState("")
  const [errors, setErrors] = useState<{ code?: string; name?: string }>({})

  // Same hydration-mismatch guard as the builder canvas: colorMode depends on resolvedTheme,
  // which is unknown on the server and on first paint.
  const [mounted, setMounted] = useState(false)
  useEffect(() => setMounted(true), [])

  // 300ms debounce, matching app/dashboard/receiving/grn/page.tsx. Resets to page 1 with the
  // query so a narrower search can't leave you stranded past the last page — done here rather
  // than in a second effect watching [query, status], which would be a cascading render.
  useEffect(() => {
    const timer = setTimeout(() => {
      setQuery(searchInput.trim())
      setPage(1)
    }, 300)
    return () => clearTimeout(timer)
  }, [searchInput])

  const load = useCallback(() => {
    setLoadError(null)
    productionTemplatesApi
      .list({
        page,
        pageSize: PAGE_SIZE,
        q: query || undefined,
        status: status === "All" ? undefined : status,
      })
      .then((res) => {
        setTemplates(res.items)
        setPagination(res.pagination)
      })
      .catch((err) => {
        setTemplates([])
        setLoadError(errorMessage(err))
      })
  }, [page, query, status])

  useEffect(load, [load])

  const hasFilters = query.length > 0 || status !== "All"
  const { nodes, edges } = useMemo(() => buildLinesGraph(templates ?? []), [templates])

  const onNodeClick: NodeMouseHandler = (_, node) => {
    const templateId = (node.data as LineHeaderData | LineStageData).templateId
    router.push(`/dashboard/production/templates/${templateId}`)
  }

  function openCreateDialog() {
    setCode("")
    setName("")
    setErrors({})
    setOpen(true)
  }

  /**
   * Opens the builder on an unsaved draft rather than creating anything now.
   *
   * A template cannot exist without a valid graph: the server requires at least one stage and
   * a terminal output naming a real finished item (FR-MFG-02/05). There is nothing sensible to
   * POST from a name alone, so the draft lives in the builder and the first Save creates it.
   */
  function handleCreate() {
    const nextErrors: { code?: string; name?: string } = {}
    if (!code.trim()) nextErrors.code = "Code is required."
    if (!name.trim()) nextErrors.name = "Name is required."
    setErrors(nextErrors)
    if (Object.keys(nextErrors).length > 0) return

    setOpen(false)
    router.push(
      `/dashboard/production/templates/new?code=${encodeURIComponent(code.trim())}&name=${encodeURIComponent(name.trim())}`,
    )
  }

  return (
    <div className="flex flex-col gap-6">
      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        <div>
          <h1 className="text-2xl font-bold text-foreground">Production Templates</h1>
          <p className="text-base text-muted-foreground">Every production line, stage by stage. Click a line to open its builder.</p>
        </div>
        <Dialog open={open} onOpenChange={setOpen}>
          <DialogTrigger render={<Button size="lg" onClick={openCreateDialog}><Plus className="size-5" />New Template</Button>} />
          <DialogContent className="sm:max-w-sm">
            <DialogHeader className="items-center text-center">
              <DialogTitle>New template</DialogTitle>
              <DialogDescription>
                Name it, then build its stage graph. It&apos;s saved once the graph is valid.
              </DialogDescription>
            </DialogHeader>
            <FieldGroup>
              <Field data-invalid={!!errors.code}>
                <FieldLabel htmlFor="tpl-code">Code</FieldLabel>
                <Input
                  id="tpl-code"
                  value={code}
                  onChange={(e) => setCode(e.target.value)}
                  placeholder="e.g. PT-CHAIR"
                  aria-invalid={!!errors.code}
                />
                <FieldError errors={[errors.code ? { message: errors.code } : undefined]} />
              </Field>
              <Field data-invalid={!!errors.name}>
                <FieldLabel htmlFor="tpl-name">Name</FieldLabel>
                <Input
                  id="tpl-name"
                  value={name}
                  onChange={(e) => setName(e.target.value)}
                  placeholder="e.g. Aluminium Frame Assembly"
                  aria-invalid={!!errors.name}
                  onKeyDown={(e) => e.key === "Enter" && handleCreate()}
                />
                <FieldError errors={[errors.name ? { message: errors.name } : undefined]} />
              </Field>
            </FieldGroup>
            <div className="flex flex-col-reverse gap-3 pt-2 sm:flex-row sm:justify-center">
              <Button variant="outline" className="w-full sm:w-auto sm:min-w-36" onClick={() => setOpen(false)}>
                Cancel
              </Button>
              <Button className="w-full sm:w-auto sm:min-w-36" onClick={handleCreate}>
                Open builder
              </Button>
            </div>
          </DialogContent>
        </Dialog>
      </div>

      <div className="flex flex-col gap-3 sm:flex-row sm:items-center">
        <div className="relative flex-1 basis-0">
          <Search className="pointer-events-none absolute top-1/2 left-4 size-5 -translate-y-1/2 text-muted-foreground" />
          <Input
            value={searchInput}
            onChange={(e) => setSearchInput(e.target.value)}
            placeholder="Search by code or name…"
            className="h-14 w-full pl-11 text-base"
            aria-label="Search templates"
          />
        </div>
        <Select<StatusFilter> value={status} onValueChange={(v) => setStatus(v ?? "All")}>
          <SelectTrigger className="h-14! w-full sm:w-48 text-base">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="All" className="text-base">All statuses</SelectItem>
            <SelectItem value="Active" className="text-base">Active</SelectItem>
            <SelectItem value="Inactive" className="text-base">Inactive</SelectItem>
          </SelectContent>
        </Select>
      </div>

      {loadError && (
        <div className="rounded-lg border border-destructive/30 bg-destructive/5 p-5 text-base text-destructive">
          {loadError}
        </div>
      )}

      {templates === null ? (
        <Skeleton className="h-[70vh] min-h-105 w-full rounded-2xl" />
      ) : templates.length === 0 ? (
        <div className="flex flex-col items-center gap-4 rounded-xl border border-dashed py-20 text-center">
          <LayoutTemplate className="size-12 text-muted-foreground" />
          <p className="text-base text-muted-foreground">
            {hasFilters ? "No templates match your search/filter." : "No templates yet."}
          </p>
        </div>
      ) : (
        <>
          <div className="h-[70vh] min-h-105 overflow-hidden rounded-2xl bg-muted ring-1 ring-foreground/10">
            {mounted ? (
              <ReactFlow
                nodes={nodes}
                edges={edges}
                nodeTypes={nodeTypes}
                onNodeClick={onNodeClick}
                nodesDraggable={false}
                nodesConnectable={false}
                elementsSelectable={false}
                panOnDrag
                zoomOnScroll
                colorMode={resolvedTheme === "dark" ? "dark" : "light"}
                fitView
                proOptions={{ hideAttribution: true }}
              >
                <Background />
                <Controls showInteractive={false} />
              </ReactFlow>
            ) : (
              <Skeleton className="size-full rounded-none" />
            )}
          </div>

          {pagination && pagination.totalPages > 1 && (
            <div className="flex items-center justify-between">
              <p className="text-base text-muted-foreground">
                Showing {(pagination.page - 1) * pagination.pageSize + 1}–
                {Math.min(pagination.page * pagination.pageSize, pagination.totalItems)} of {pagination.totalItems}
              </p>
              <div className="flex gap-2">
                <Button variant="outline" onClick={() => setPage((p) => p - 1)} disabled={pagination.page <= 1}>
                  Previous
                </Button>
                <Button
                  variant="outline"
                  onClick={() => setPage((p) => p + 1)}
                  disabled={pagination.page >= pagination.totalPages}
                >
                  Next
                </Button>
              </div>
            </div>
          )}
        </>
      )}
    </div>
  )
}
