"use client"

import * as React from "react"
import { ArrowRight, ChevronRight, FileText } from "lucide-react"

import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog"

import type { ObservationDateRow, ObservationDateStructure } from "./deepdex-upload-queue-review-types"

const ROW_LABEL: Record<ObservationDateRow["type"], string> = {
  same: "Same",
  changed: "Changed",
  added: "New in document",
  removed: "Not in document",
}

// Translucent tints read on both light and dark surfaces; text switches to a lighter shade in dark mode.
const TONE = {
  same: {
    badge: "border-emerald-500/40 bg-emerald-500/15 text-emerald-700 dark:text-emerald-300",
    row: "",
  },
  changed: {
    badge: "border-amber-500/50 bg-amber-500/20 text-amber-800 dark:text-amber-300",
    row: "bg-amber-500/10",
  },
  added: {
    badge: "border-sky-500/50 bg-sky-500/20 text-sky-800 dark:text-sky-300",
    row: "bg-sky-500/10",
  },
  removed: {
    badge: "border-red-500/50 bg-red-500/20 text-red-800 dark:text-red-300",
    row: "bg-red-500/10",
  },
  unsaved: {
    badge: "border-violet-500/50 bg-violet-500/20 text-violet-800 dark:text-violet-300",
    row: "",
  },
} as const

type Filter = "all" | "differs" | "matches" | "unsaved"

const statusOf = (s: ObservationDateStructure): Exclude<Filter, "all"> =>
  !s.exists ? "unsaved" : s.differs ? "differs" : "matches"

function StructureStatus({ structure }: { structure: ObservationDateStructure }) {
  if (!structure.exists) return <Badge variant="outline" className={TONE.unsaved.badge}>Not saved yet</Badge>
  if (structure.differs) return <Badge variant="outline" className={TONE.changed.badge}>Differs</Badge>
  return <Badge variant="outline" className={TONE.same.badge}>Matches</Badge>
}

export function DeepdexUploadQueueReviewObservationDatesDialog({
  open,
  onOpenChange,
  structures,
  onUpdateAll,
  isUpdating,
}: {
  open: boolean
  onOpenChange: (open: boolean) => void
  structures: ObservationDateStructure[]
  /** Starts the background job that replaces the saved dates of every differing structure. */
  onUpdateAll?: () => void
  isUpdating?: boolean
}) {
  const [filter, setFilter] = React.useState<Filter>("all")
  // Documents that differ start expanded; the rest stay collapsed until opened.
  const [openDocs, setOpenDocs] = React.useState<Set<number>>(new Set())
  const [wasOpen, setWasOpen] = React.useState(false)
  if (open !== wasOpen) {
    setWasOpen(open)
    if (open) {
      setFilter("all")
      setOpenDocs(new Set(structures.filter((s) => s.exists && s.differs).map((s) => s.queue_index)))
    }
  }

  // Same order and numbering as the review table.
  const ordered = React.useMemo(
    () => [...structures].sort((a, b) => a.queue_index - b.queue_index),
    [structures],
  )

  const visible = React.useMemo(
    () => (filter === "all" ? ordered : ordered.filter((s) => statusOf(s) === filter)),
    [ordered, filter],
  )

  const setDocOpen = (index: number, isOpen: boolean) =>
    setOpenDocs((prev) => {
      const next = new Set(prev)
      if (isOpen) next.add(index)
      else next.delete(index)
      return next
    })

  const counts = React.useMemo(
    () => ({
      differs: structures.filter((s) => s.exists && s.differs).length,
      matches: structures.filter((s) => s.exists && !s.differs).length,
      unsaved: structures.filter((s) => !s.exists).length,
    }),
    [structures],
  )

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-4xl">
        <DialogHeader>
          <DialogTitle>Observation dates: saved vs document</DialogTitle>
          <DialogDescription>
            {structures.length === 0
              ? "No structures with an ISIN to compare."
              : `${structures.length} structure(s) checked: ${counts.differs} differ, ${counts.matches} match, ${counts.unsaved} not saved yet. To replace the saved dates, edit the row and save, or update all that differ.`}
          </DialogDescription>
        </DialogHeader>

        {structures.length > 0 ? (
          <div className="flex flex-wrap items-center justify-between gap-2">
            <div className="flex flex-wrap gap-1.5">
              {(
                [
                  ["all", "All", structures.length],
                  ["differs", "Differs", counts.differs],
                  ["matches", "Matches", counts.matches],
                  ["unsaved", "Not saved yet", counts.unsaved],
                ] as const
              ).map(([value, label, count]) => (
                <Button
                  key={value}
                  type="button"
                  size="sm"
                  variant={filter === value ? "default" : "outline"}
                  onClick={() => setFilter(value)}
                >
                  {label} ({count})
                </Button>
              ))}
            </div>
            <div className="flex gap-1">
              <Button
                type="button"
                size="sm"
                variant="ghost"
                onClick={() => setOpenDocs(new Set(visible.map((s) => s.queue_index)))}
              >
                Expand all
              </Button>
              <Button type="button" size="sm" variant="ghost" onClick={() => setOpenDocs(new Set())}>
                Collapse all
              </Button>
            </div>
          </div>
        ) : null}

        {visible.length > 0 ? (
          <div className="max-h-[60vh] space-y-2 overflow-y-auto pr-1">
            {visible.map((structure) => (
              <Collapsible
                key={`${structure.queue_index}:${structure.isin}`}
                open={openDocs.has(structure.queue_index)}
                onOpenChange={(isOpen) => setDocOpen(structure.queue_index, isOpen)}
                className="rounded-lg border border-border"
              >
                <CollapsibleTrigger className="group flex w-full items-center gap-3 bg-muted/50 px-3 py-2 text-left hover:bg-muted">
                  <ChevronRight className="size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-90" />
                  <span className="w-6 shrink-0 text-xs tabular-nums text-muted-foreground">
                    #{structure.queue_index + 1}
                  </span>
                  <div className="min-w-0 flex-1">
                    <p className="flex items-center gap-1.5 truncate text-sm font-medium">
                      <FileText className="size-3.5 shrink-0 text-muted-foreground" />
                      <span className="truncate">{structure.document || structure.name || structure.isin}</span>
                    </p>
                    <p className="truncate text-xs text-muted-foreground">
                      <code>{structure.isin}</code>
                      {structure.name ? ` · ${structure.name}` : ""}
                    </p>
                  </div>
                  <span className="hidden shrink-0 text-xs text-muted-foreground sm:inline">
                    {structure.saved.length} saved · {structure.rows.filter((r) => r.type !== "removed").length} in document
                  </span>
                  <StructureStatus structure={structure} />
                </CollapsibleTrigger>
                <CollapsibleContent>
                  <div className="divide-y divide-border border-t border-border">
                    <div className="grid grid-cols-[1fr_auto_1fr_auto] items-center gap-3 px-3 py-1.5 text-[11px] uppercase tracking-wide text-muted-foreground">
                      <span>Saved (old)</span>
                      <span className="w-4" />
                      <span>Document (new)</span>
                      <span className="w-28 text-right">Difference</span>
                    </div>
                    {structure.rows.length === 0 ? (
                      <p className="px-3 py-2 text-sm text-muted-foreground">No observation dates on either side.</p>
                    ) : null}
                    {structure.rows.map((row, index) => (
                      <div
                        key={`${structure.isin}:${index}`}
                        className={`grid grid-cols-[1fr_auto_1fr_auto] items-center gap-3 px-3 py-2 text-sm ${
                          structure.exists ? TONE[row.type].row : ""
                        }`}
                      >
                        <span className={row.from ? "font-mono" : "text-muted-foreground"}>{row.from || "—"}</span>
                        <ArrowRight className="size-4 text-muted-foreground" />
                        <span className={row.to ? "font-mono font-medium" : "text-muted-foreground"}>
                          {row.to || "—"}
                        </span>
                        <span className="flex w-28 justify-end">
                          <Badge
                            variant="outline"
                            className={structure.exists ? TONE[row.type].badge : TONE.unsaved.badge}
                          >
                            {structure.exists ? ROW_LABEL[row.type] : "New"}
                          </Badge>
                        </span>
                      </div>
                    ))}
                  </div>
                </CollapsibleContent>
              </Collapsible>
            ))}
          </div>
        ) : structures.length > 0 ? (
          <p className="py-6 text-center text-sm text-muted-foreground">No documents in this filter.</p>
        ) : null}

        <DialogFooter>
          {onUpdateAll && counts.differs > 0 ? (
            <Button type="button" onClick={onUpdateAll} disabled={isUpdating}>
              {isUpdating ? "Updating in background…" : `Update ${counts.differs} differing date set(s)`}
            </Button>
          ) : null}
          <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
            Close
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
