// src/components/flight-check/checklist.tsx
"use client";

import { BookOpen, Copy, Loader2, Play, Search } from "lucide-react";
import Link from "next/link";
import { useLocale } from "next-intl";
import { useMemo, useState, useTransition } from "react";
import { toast } from "sonner";
import { Overview } from "@/components/flight-check/overview";
import { relativeFr } from "@/components/keywords/keyword-modal-utils";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useChecklistState } from "@/hooks/use-checklist-state";
import { isLocale } from "@/i18n/config";
import {
  type OrgAuditOverviewRow,
  runSiteAuditAction,
} from "@/lib/spec/audit/actions";
import {
  buildAuditFixPrompt,
  type FixPromptItem,
} from "@/lib/spec/audit/fix-prompt";
import type { AuditResult, AuditResultStatus } from "@/lib/spec/audit/run";
import type { SpecCategory, SpecItem, SpecStatus } from "@/lib/spec/types";
import { cn } from "@/lib/utils";

/** Latest audit verdict for a single item, looked up from the latest run. */
type AuditCell = { status: AuditResultStatus; detail: string };

// ── Status filters ───────────────────────────────────────────────────────────
type StatusFilter = "all" | "required" | "recommended" | "todo" | "done";

const FILTER_LABELS: Record<StatusFilter, string> = {
  all: "Tous",
  required: "Requis",
  recommended: "Recommandés",
  todo: "À faire",
  done: "Faits",
};

const FILTER_ORDER: StatusFilter[] = [
  "all",
  "required",
  "recommended",
  "todo",
  "done",
];

// An item title is rendered in mono when it reads like code — the design uses
// monospace for `<meta charset>`, `rel="canonical"`, etc. Heuristic: it contains
// an angle bracket or an attribute assignment.
function isCodeLike(title: string) {
  return title.includes("<") || title.includes('="');
}

const STATUS_BADGE: Record<SpecStatus, { label: string; className: string }> = {
  required: {
    label: "Requis",
    className: "border-red-500/25 bg-red-500/10 text-red-700 dark:text-red-300",
  },
  recommended: {
    label: "Recommandé",
    className:
      "border-blue-500/25 bg-blue-500/10 text-blue-700 dark:text-blue-300",
  },
  optional: {
    label: "Optionnel",
    className: "border-border bg-muted text-muted-foreground",
  },
  avoid: {
    label: "À éviter",
    className:
      "border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300",
  },
};

const AUDIT_BADGE: Record<
  AuditResultStatus,
  { label: string; className: string }
> = {
  pass: {
    label: "Pass",
    className:
      "border-green-500/25 bg-green-500/10 text-green-700 dark:text-green-300",
  },
  fail: {
    label: "Fail",
    className: "border-red-500/25 bg-red-500/10 text-red-700 dark:text-red-300",
  },
  warn: {
    label: "Warn",
    className:
      "border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300",
  },
  na: {
    label: "N/A",
    className: "border-border bg-muted text-muted-foreground",
  },
  manual: {
    label: "Manuel",
    className: "border-border bg-muted text-muted-foreground",
  },
};

/** Auto (audited) when an audit verdict exists and isn't "manual". */
function isAuto(cell: AuditCell | undefined): cell is AuditCell {
  return cell != null && cell.status !== "manual";
}

// ── Hero progress ring ───────────────────────────────────────────────────────
function HeroRing({
  pct,
  label,
}: {
  /** 0..100 — fill fraction and the big number. */
  pct: number;
  /** caption under the number, e.g. "score" or "vérifié". */
  label: string;
}) {
  const r = 33;
  const c = 2 * Math.PI * r;
  const filled = (Math.min(100, Math.max(0, pct)) / 100) * c;
  return (
    <span className="relative inline-flex size-[78px] shrink-0 items-center justify-center">
      <svg
        width={78}
        height={78}
        viewBox="0 0 78 78"
        className="-rotate-90"
        role="img"
        aria-label={`${pct}% ${label}`}
      >
        <circle
          cx={39}
          cy={39}
          r={r}
          fill="none"
          strokeWidth={7}
          className="stroke-muted"
        />
        <circle
          cx={39}
          cy={39}
          r={r}
          fill="none"
          strokeWidth={7}
          strokeLinecap="round"
          className="stroke-green-500 transition-[stroke-dasharray] duration-500"
          strokeDasharray={`${filled} ${c - filled}`}
        />
      </svg>
      <span className="absolute flex flex-col items-center justify-center leading-none">
        <b className="font-bold text-lg tabular-nums">{pct}%</b>
        <span className="mt-0.5 text-[10px] text-muted-foreground">
          {label}
        </span>
      </span>
    </span>
  );
}

export function Checklist({
  categories,
  siteId,
  siteName,
  siteUrl,
  appUrl,
  initialAudit,
  overview,
}: {
  categories: SpecCategory[];
  siteId: string;
  siteName: string;
  siteUrl: string;
  appUrl: string;
  initialAudit: AuditResult | null;
  overview: OrgAuditOverviewRow[];
}) {
  const [active, setActive] = useState<string>(categories[0]?.id ?? "");
  const [query, setQuery] = useState("");
  const [filter, setFilter] = useState<StatusFilter>("all");
  const [audit, setAudit] = useState<AuditResult | null>(initialAudit);
  const [running, startRun] = useTransition();
  const { isChecked, toggle } = useChecklistState(siteId);

  // itemId → audit verdict, rebuilt whenever a fresh audit lands.
  const auditMap = useMemo(() => {
    const map = new Map<string, AuditCell>();
    if (audit) {
      for (const r of audit.results) {
        map.set(r.itemId, { status: r.status, detail: r.detail });
      }
    }
    return map;
  }, [audit]);

  // An item is "satisfied" when the audit passes it, or — for manual / not-yet
  // audited items — when the user has checked it off. This single definition
  // drives the rail counts, the ring sub-rows and the À faire/Faits filters.
  const isSatisfied = useMemo(
    () => (item: SpecItem) => {
      const v = auditMap.get(item.id);
      if (isAuto(v)) return v.status === "pass";
      return isChecked(item.id);
    },
    [auditMap, isChecked],
  );

  const q = query.trim().toLowerCase();

  // ── Rail: per-category satisfied / total ───────────────────────────────────
  const railStats = useMemo(() => {
    const map: Record<string, { done: number; total: number }> = {};
    for (const cat of categories) {
      const done = cat.items.reduce(
        (n, it) => (isSatisfied(it) ? n + 1 : n),
        0,
      );
      map[cat.id] = { done, total: cat.items.length };
    }
    return map;
  }, [categories, isSatisfied]);

  // ── Hero metrics ───────────────────────────────────────────────────────────
  const totals = useMemo(() => {
    let satisfied = 0;
    let total = 0;
    let autoChecked = 0;
    let autoPass = 0;
    let autoFail = 0;
    let reqRemaining = 0;
    for (const cat of categories) {
      for (const it of cat.items) {
        total++;
        const v = auditMap.get(it.id);
        if (isAuto(v)) {
          autoChecked++;
          if (v?.status === "pass") autoPass++;
          else if (v?.status === "fail") autoFail++;
        }
        const ok = isSatisfied(it);
        if (ok) satisfied++;
        if (it.status === "required" && !ok) reqRemaining++;
      }
    }
    return { satisfied, total, autoChecked, autoPass, autoFail, reqRemaining };
  }, [categories, auditMap, isSatisfied]);

  // The ring is OVERALL coverage — items satisfied (audit pass OR manually
  // checked) over the whole spec — the same metric the category rail shows.
  // The audit's auto pass-rate (which can be 100% even when only a handful of
  // items were auto-checkable) is surfaced separately so it can't masquerade as
  // full compliance.
  const ringPct = totals.total
    ? Math.round((totals.satisfied / totals.total) * 100)
    : 0;

  const activeCat = categories.find((c) => c.id === active) ?? categories[0];

  // ── Filter chip counts (scoped to the active category) ─────────────────────
  const chipCounts = useMemo(() => {
    const items = activeCat?.items ?? [];
    return {
      all: items.length,
      required: items.filter((i) => i.status === "required").length,
      recommended: items.filter((i) => i.status === "recommended").length,
      todo: items.filter((i) => !isSatisfied(i)).length,
      done: items.filter((i) => isSatisfied(i)).length,
    } satisfies Record<StatusFilter, number>;
  }, [activeCat, isSatisfied]);

  function passesFilter(it: SpecItem) {
    if (filter === "required" && it.status !== "required") return false;
    if (filter === "recommended" && it.status !== "recommended") return false;
    if (filter === "todo" && isSatisfied(it)) return false;
    if (filter === "done" && !isSatisfied(it)) return false;
    if (q) {
      return (
        it.title.toLowerCase().includes(q) ||
        it.description.toLowerCase().includes(q)
      );
    }
    return true;
  }

  const visibleItems = (activeCat?.items ?? []).filter(passesFilter);

  const runAudit = () => {
    if (!siteId || !siteUrl) {
      toast.error("Ce site n'a pas d'URL configurée.");
      return;
    }
    startRun(async () => {
      try {
        const result = await runSiteAuditAction(siteId);
        setAudit(result);
        const pass = result.results.filter((r) => r.status === "pass").length;
        const checked = result.results.filter((r) =>
          ["pass", "fail", "warn"].includes(r.status),
        ).length;
        toast.success(
          `Audit terminé — ${pass}/${checked} vérifications auto OK`,
        );
      } catch (err) {
        toast.error(err instanceof Error ? err.message : "L'audit a échoué.");
      }
    });
  };

  // Le squelette du prompt suit la langue de l'utilisateur (settings) ; "en"
  // si la locale résolue sort des locales supportées.
  const uiLocale = useLocale();

  const copyFixPrompt = async () => {
    if (!audit) return;
    const byId = new Map(
      categories.flatMap((c) => c.items).map((it) => [it.id, it]),
    );
    const items: FixPromptItem[] = audit.results.flatMap((r) => {
      const item = byId.get(r.itemId);
      if (!item) return []; // item disparu de la checklist courante
      return [
        {
          itemId: r.itemId,
          title: item.title,
          specStatus: item.status,
          status: r.status,
          detail: r.detail,
        },
      ];
    });
    const prompt = buildAuditFixPrompt({
      siteId,
      siteName,
      siteUrl,
      appUrl,
      score: audit.score,
      ranAt: audit.ranAt,
      items,
      locale: isLocale(uiLocale) ? uiLocale : "en",
    });
    try {
      await navigator.clipboard.writeText(prompt);
      toast.success("Prompt copié", {
        description:
          "Collez-le dans votre agent (Claude Code, Cursor…) avec votre clé API (Réglages → Clés API).",
      });
    } catch {
      toast.error("Impossible de copier le prompt");
    }
  };

  const canAudit = Boolean(siteId && siteUrl);
  const ranAt = relativeFr(audit?.ranAt);

  function resetFilters() {
    setQuery("");
    setFilter("all");
  }

  return (
    <div className="flex flex-col gap-4 p-4">
      <Tabs defaultValue="site" className="w-full">
        <TabsList variant="line">
          <TabsTrigger value="site">{siteName || "Ce site"}</TabsTrigger>
          <TabsTrigger value="overview">Vue d'ensemble</TabsTrigger>
        </TabsList>

        <TabsContent value="overview" className="mt-4">
          <Overview rows={overview} />
        </TabsContent>

        <TabsContent value="site" className="mt-4">
          {/* ── Progress hero ──────────────────────────────────────────── */}
          <div className="mb-6 grid gap-4 lg:grid-cols-[1fr_300px]">
            {/* Audit CTA card */}
            <div className="flex items-center gap-5 rounded-2xl border bg-card p-5">
              <div className="grid size-12 shrink-0 place-items-center rounded-xl border border-amber-500/25 bg-amber-500/10 text-amber-600 dark:text-amber-400">
                <Play className="size-5" />
              </div>
              <div className="min-w-0 flex-1">
                <h3 className="flex items-center gap-2 font-semibold text-base">
                  <span className="size-2 rounded-full bg-amber-500 shadow-[0_0_0_3px_rgba(245,181,68,0.18)]" />
                  {audit ? "Audit automatique" : "Pas encore audité"}
                </h3>
                <p className="mt-1 text-muted-foreground text-sm">
                  {!canAudit
                    ? "Ce site n'a pas d'URL configurée — ajoutez-en une dans Projets pour lancer l'audit."
                    : audit
                      ? `${ranAt ? `Audité ${ranAt}. ` : ""}${totals.autoPass}/${totals.autoChecked} vérifications auto réussies${
                          totals.autoFail
                            ? `, ${totals.autoFail} à corriger`
                            : ""
                        }. Les ${totals.total - totals.autoChecked} autres points sont à vérifier manuellement.`
                      : "Lancez l'audit automatique pour vérifier ce site et cocher les points détectables."}
                </p>
              </div>
              <Button
                onClick={runAudit}
                disabled={running || !canAudit}
                className="shrink-0"
                title={
                  !canAudit ? "Le site n'a pas d'URL configurée." : undefined
                }
              >
                {running ? (
                  <Loader2 className="size-4 animate-spin" />
                ) : (
                  <Play className="size-4" />
                )}
                {running
                  ? "Audit en cours…"
                  : audit
                    ? "Relancer l'audit"
                    : "Lancer l'audit"}
              </Button>
              <Button
                variant="outline"
                onClick={copyFixPrompt}
                disabled={!audit || running}
                className="shrink-0"
                title={
                  !audit
                    ? "Lancez d'abord un audit pour générer le prompt."
                    : "Prompt prêt-à-coller pour votre agent (nécessite une clé API — Réglages → Clés API)."
                }
              >
                <Copy className="size-4" />
                Copier le prompt agent
              </Button>
            </div>

            {/* Progress ring card */}
            <div className="flex items-center gap-4 rounded-2xl border bg-card p-4">
              <HeroRing pct={ringPct} label="conforme" />
              <div className="min-w-0 flex-1">
                <div className="font-medium text-muted-foreground text-sm">
                  Conformité à la spec
                </div>
                <div className="mt-2.5 flex items-center gap-2 text-muted-foreground text-xs">
                  <span className="size-2.5 shrink-0 rounded bg-green-500" />
                  <b className="font-semibold text-foreground tabular-nums">
                    {totals.autoChecked}
                  </b>
                  /{totals.total} auto-vérifiés
                </div>
                <div className="mt-1.5 flex items-center gap-2 text-muted-foreground text-xs">
                  <span className="size-2.5 shrink-0 rounded bg-red-500" />
                  Requis ·{" "}
                  <b className="font-semibold text-foreground tabular-nums">
                    {totals.reqRemaining}
                  </b>{" "}
                  restants
                </div>
              </div>
            </div>
          </div>

          {/* ── Body grid: category rail + items panel ─────────────────── */}
          <div className="grid items-start gap-6 lg:grid-cols-[248px_1fr]">
            {/* Category rail */}
            <aside className="flex flex-col gap-1 lg:sticky lg:top-2">
              <div className="px-3 pb-2 font-semibold text-[11px] text-muted-foreground uppercase tracking-wide">
                Catégories
              </div>
              {categories.map((cat) => {
                const { done, total } = railStats[cat.id];
                const pct = total ? (done / total) * 100 : 0;
                const isActive = cat.id === active;
                return (
                  <button
                    type="button"
                    key={cat.id}
                    onClick={() => setActive(cat.id)}
                    className={cn(
                      "grid grid-cols-[1fr_auto] gap-x-2 gap-y-1 rounded-xl border border-transparent px-3 py-2.5 text-left transition-colors hover:bg-accent",
                      isActive && "border-border bg-accent",
                    )}
                  >
                    <span
                      className={cn(
                        "truncate font-semibold text-[13.5px]",
                        isActive ? "text-foreground" : "text-muted-foreground",
                      )}
                    >
                      {cat.label}
                    </span>
                    <span className="font-semibold text-[11.5px] text-muted-foreground tabular-nums">
                      {done}/{total}
                    </span>
                    <span className="col-span-2 h-1 overflow-hidden rounded bg-muted">
                      <span
                        className="block h-full rounded bg-green-500 transition-[width] duration-500"
                        style={{ width: `${pct}%` }}
                      />
                    </span>
                  </button>
                );
              })}
            </aside>

            {/* Items panel */}
            <div className="min-w-0">
              {/* Filter bar */}
              <div className="mb-4 flex flex-wrap items-center gap-3">
                <div className="relative flex-[0_1_290px]">
                  <Search className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
                  <Input
                    placeholder="Rechercher dans la spec…"
                    value={query}
                    onChange={(e) => setQuery(e.target.value)}
                    className="pl-9"
                  />
                </div>
                <div className="flex flex-wrap items-center gap-1.5">
                  {FILTER_ORDER.map((f) => {
                    const isActive = filter === f;
                    return (
                      <button
                        type="button"
                        key={f}
                        onClick={() => setFilter(f)}
                        className={cn(
                          "inline-flex h-8 items-center gap-1.5 rounded-lg border px-3 font-semibold text-[12.5px] transition-colors",
                          isActive
                            ? "border-primary/40 bg-primary/10 text-primary"
                            : "border-border bg-card text-muted-foreground hover:text-foreground",
                        )}
                      >
                        {FILTER_LABELS[f]}
                        <span
                          className={cn(
                            "text-[11px] tabular-nums",
                            isActive
                              ? "text-primary/70"
                              : "text-muted-foreground/70",
                          )}
                        >
                          {chipCounts[f]}
                        </span>
                      </button>
                    );
                  })}
                </div>
                <button
                  type="button"
                  onClick={resetFilters}
                  className="ml-auto rounded-lg px-2.5 py-1.5 font-semibold text-muted-foreground text-sm transition-colors hover:bg-accent hover:text-foreground"
                >
                  Réinitialiser
                </button>
              </div>

              {/* Category heading */}
              <div className="mb-3.5">
                <h2 className="flex items-center gap-3 font-semibold text-lg tracking-tight">
                  {activeCat?.label}
                  <span className="rounded-md border bg-card px-2 py-0.5 font-semibold text-[12.5px] text-muted-foreground tabular-nums">
                    {railStats[active]?.done ?? 0} /{" "}
                    {railStats[active]?.total ?? 0}
                  </span>
                </h2>
                <p className="mt-1.5 text-muted-foreground text-sm">
                  {activeCat?.summary}
                </p>
              </div>

              {/* Item rows */}
              <div className="flex flex-col overflow-hidden rounded-xl border bg-card">
                {visibleItems.length === 0 ? (
                  <div className="px-5 py-12 text-center text-muted-foreground text-sm">
                    Aucun point ne correspond à ce filtre.
                  </div>
                ) : (
                  visibleItems.map((it) => (
                    <ItemRow
                      key={it.id}
                      item={it}
                      audit={auditMap.get(it.id)}
                      checked={isChecked(it.id)}
                      onToggle={() => toggle(it.id)}
                    />
                  ))
                )}
              </div>
            </div>
          </div>
        </TabsContent>
      </Tabs>
    </div>
  );
}

function ItemRow({
  item,
  audit,
  checked,
  onToggle,
}: {
  item: SpecItem;
  audit: AuditCell | undefined;
  checked: boolean;
  onToggle: () => void;
}) {
  const auto = isAuto(audit);
  // Satisfied (for the dimmed/strike style): pass for auto, checked for manual.
  const satisfied = auto ? audit.status === "pass" : checked;
  const codeLike = isCodeLike(item.title);
  const statusMeta = STATUS_BADGE[item.status];
  // Auto rows show the audit detail; manual rows show the spec description.
  const body = auto ? audit.detail : item.description;

  return (
    <div
      className={cn(
        "grid grid-cols-[24px_116px_1fr_34px] items-start gap-4 border-b px-4 py-4 transition-opacity last:border-b-0 hover:bg-accent/30",
        satisfied && "opacity-60",
      )}
    >
      {/* checkbox (manual) or spacer (auto — the badge is authoritative) */}
      <div className="pt-0.5">
        {auto ? null : (
          <Checkbox
            checked={checked}
            onCheckedChange={onToggle}
            aria-label={`Marquer « ${item.title} » comme fait`}
            className="data-[state=checked]:border-green-500 data-[state=checked]:bg-green-500"
          />
        )}
      </div>

      {/* status / audit badge */}
      <div className="pt-0.5">
        {auto ? (
          <Badge
            variant="outline"
            className={AUDIT_BADGE[audit.status].className}
          >
            {AUDIT_BADGE[audit.status].label}
          </Badge>
        ) : (
          <Badge variant="outline" className={statusMeta.className}>
            {statusMeta.label}
          </Badge>
        )}
      </div>

      {/* main: title + audit/description text */}
      <div className="min-w-0">
        <div
          className={cn(
            "break-words font-semibold text-sm",
            codeLike && "font-mono text-[13px] text-primary",
            satisfied && "text-muted-foreground line-through",
          )}
        >
          {item.title}
        </div>
        {body ? (
          <p className="mt-1 max-w-[62ch] text-muted-foreground text-[13px] leading-relaxed">
            {body}
          </p>
        ) : null}
      </div>

      {/* guide link — internal page (we host the spec content, no redirect) */}
      <Link
        href={`/flight-checker/spec/${item.id}`}
        className="grid size-[30px] place-items-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-primary"
        aria-label={`Voir le guide pour ${item.title}`}
      >
        <BookOpen className="size-4" />
      </Link>
    </div>
  );
}
