"use client";

import { useMemo, useState } from "react";
import {
  Check, X, Edit2, AlertTriangle, Plus, GitMerge, Save, Search, Undo2, BadgeCheck,
  DownloadCloud, Loader2, ExternalLink,
} from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import {
  usePlansAll, usePlanDuplicates, useUpsertPlan, useUpdatePlan,
  useReviewPlan, useMergePlans, useFetchPlan, type Plan, type PlanEdit,
} from "@/lib/api/plans";

/* Strip bidi marks (mirror of the halacha panel's cleanCitation). */
function clean(s: string | null | undefined): string {
  if (!s) return "—";
  return s.replace(/[‎‏‪-‮⁦-⁩]/g, "").trim();
}

function fmtDate(iso: string | null | undefined): string {
  if (!iso) return "—";
  const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
  if (!m) return iso;
  return `${Number(m[3])}.${Number(m[2])}.${m[1]}`;
}

type EditForm = {
  plan_number: string;
  display_name: string;
  plan_type: string;
  gazette_date: string;
  yalkut_number: string;
  purpose: string;
};

const BLANK: EditForm = {
  plan_number: "", display_name: "", plan_type: "",
  gazette_date: "", yalkut_number: "", purpose: "",
};

function toForm(p: Plan): EditForm {
  return {
    plan_number: p.plan_number,
    display_name: p.display_name,
    plan_type: p.plan_type,
    gazette_date: p.gazette_date ?? "",
    yalkut_number: p.yalkut_number,
    purpose: p.purpose,
  };
}

/* Client-side mirror of db.format_plan_citation — deterministic preview so the
 * chair sees exactly what block-tet will cite (validity is never free-typed). */
function previewCitation(f: EditForm): string {
  const name = f.display_name.trim() || f.plan_number.trim();
  if (!name) return "—";
  let s = name;
  const d = fmtDate(f.gazette_date);
  if (d !== "—") {
    s = `${name} פורסמה למתן תוקף ברשומות ביום ${d}`;
    if (f.yalkut_number.trim()) s += `, י"פ ${f.yalkut_number.trim()}`;
  }
  if (f.purpose.trim()) s += ` — ${f.purpose.trim()}`;
  return s.endsWith(".") ? s : s + ".";
}

// ─────────────────────────────────────────────────────────────────────────────

type StatusFilter = "pending_review" | "approved" | "all";

const SEGMENTS: { value: StatusFilter; label: string }[] = [
  { value: "pending_review", label: "ממתינים" },
  { value: "approved", label: "מאושרות" },
  { value: "all", label: "כולן" },
];

/** Normalized substring match over a plan's identity + purpose fields. */
function matchesQuery(p: Plan, q: string): boolean {
  const needle = clean(q).toLowerCase();
  if (needle === "—" || !needle.trim()) return true;
  const hay = [p.plan_number, p.display_name, p.purpose, ...(p.aliases ?? [])]
    .map((s) => clean(s).toLowerCase())
    .join(" ");
  return hay.includes(needle);
}

export function PlansReviewPanel() {
  // One fetch of the whole registry; status segments + search filter client-side
  // (the registry is small — instant filtering, accurate counts, no round-trips).
  const { data, isLoading, isError } = usePlansAll();
  const [adding, setAdding] = useState(false);
  const [status, setStatus] = useState<StatusFilter>("pending_review");
  const [query, setQuery] = useState("");

  const all = useMemo(() => data?.items ?? [], [data]);
  const counts = useMemo(
    () => ({
      pending_review: all.filter((p) => p.review_status === "pending_review").length,
      approved: all.filter((p) => p.review_status === "approved").length,
      all: all.length,
    }),
    [all],
  );
  const visible = useMemo(() => {
    const byStatus =
      status === "all" ? all : all.filter((p) => p.review_status === status);
    return byStatus.filter((p) => matchesQuery(p, query));
  }, [all, status, query]);

  const truncated = (data?.count ?? 0) >= 1000;

  if (isLoading) {
    return (
      <div className="space-y-4">
        {[0, 1, 2].map((i) => <Skeleton key={i} className="h-40 w-full rounded-lg" />)}
      </div>
    );
  }
  if (isError) {
    return <p className="text-danger text-sm">שגיאה בטעינת מרשם-התכניות.</p>;
  }

  return (
    <div className="space-y-5">
      <div className="flex items-start justify-between gap-4 flex-wrap">
        <p className="text-ink-soft text-sm leading-relaxed max-w-3xl">
          מרשם-התכניות: זהות + תוקף קנוניים של כל תב&quot;ע, בשימוש חוזר בין תיקים.
          רק תכנית מאושרת מצוטטת בבלוק ט; התוקף נכתב בנוסח אחיד{" "}
          <span className="text-gold-deep font-semibold">דטרמיניסטי</span> —
          תאריך-הרשומות ומס&apos; הילקוט לעולם אינם מומצאים.
        </p>
        <Button
          onClick={() => setAdding((v) => !v)}
          className="bg-navy text-white hover:bg-navy-soft shrink-0"
        >
          <Plus className="size-4" /> הוספת תכנית ידנית
        </Button>
      </div>

      {/* status segments + fuzzy search over the whole registry */}
      <div className="flex items-center gap-3 flex-wrap">
        <div className="inline-flex gap-0.5 rounded-lg border border-rule bg-parchment p-1">
          {SEGMENTS.map((seg) => (
            <button
              key={seg.value}
              type="button"
              onClick={() => setStatus(seg.value)}
              className={cn(
                "inline-flex items-center gap-2 rounded-md px-4 py-1.5 text-sm font-semibold transition-colors",
                status === seg.value
                  ? "bg-surface text-navy shadow-sm"
                  : "text-ink-muted hover:text-ink-soft",
              )}
            >
              {seg.label}
              <span
                className={cn(
                  "rounded-full px-1.5 text-[0.7rem] font-bold tabular-nums",
                  status === seg.value ? "bg-gold text-white" : "bg-rule text-ink-muted",
                )}
              >
                {counts[seg.value]}
              </span>
            </button>
          ))}
        </div>
        <div className="relative min-w-[220px] max-w-sm flex-1">
          <Search className="pointer-events-none absolute start-3 top-1/2 size-4 -translate-y-1/2 text-ink-muted" />
          <Input
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder="חיפוש במרשם — מספר-תכנית / שם / ייעוד…"
            className="ps-9"
          />
        </div>
      </div>

      {adding && (
        <PlanForm
          title="הוספת תכנית"
          subtitle="מספר-התכנית מנורמל אוטומטית. ברירת-מחדל: מאושר (קלט-יו״ר)."
          initial={BLANK}
          mode="add"
          onClose={() => setAdding(false)}
        />
      )}

      {truncated && (
        <p className="text-warn text-[0.78rem] font-medium">
          ⚠ המרשם חרג מ-1000 רשומות — לא כל התכניות מוצגות. יש להוסיף עימוד.
        </p>
      )}

      {visible.length === 0 ? (
        <div className="rounded-lg border border-rule bg-surface shadow-sm p-6 text-center text-ink-muted text-sm">
          {query
            ? "לא נמצאו תכניות התואמות לחיפוש."
            : status === "pending_review"
              ? "אין תכניות הממתינות לאישור."
              : status === "approved"
                ? "אין תכניות מאושרות במרשם."
                : "המרשם ריק."}
        </div>
      ) : (
        visible.map((p) => <PlanCard key={p.id} plan={p} />)
      )}
    </div>
  );
}

// ─── one plan card ────────────────────────────────────────────────────────────

function PlanCard({ plan }: { plan: Plan }) {
  const [editing, setEditing] = useState(false);
  const review = useReviewPlan();
  const merge = useMergePlans();
  const isPending = plan.review_status === "pending_review";
  const isApproved = plan.review_status === "approved";
  // Dedup candidates only matter while the plan is in the chair queue.
  const { data: dups } = usePlanDuplicates(plan.id, isPending);
  const duplicates = dups?.items ?? [];

  const noDate = !plan.gazette_date;

  async function decide(status: "approved" | "rejected" | "pending_review") {
    try {
      await review.mutateAsync({ id: plan.id, status });
      toast.success(
        status === "approved"
          ? "התכנית אושרה"
          : status === "rejected"
            ? "התכנית נדחתה"
            : "התכנית הוחזרה לתור",
      );
    } catch {
      toast.error("שגיאה בעדכון התכנית");
    }
  }

  async function foldIn(sourceId: string, label: string) {
    try {
      await merge.mutateAsync({ sourceId, targetId: plan.id });
      toast.success(`מוזג: ${label} → ${plan.display_name || plan.plan_number}`);
    } catch {
      toast.error("שגיאה במיזוג");
    }
  }

  if (editing) {
    return (
      <PlanForm
        title={`עריכת תכנית — ${plan.display_name || plan.plan_number}`}
        subtitle="שינוי מספר-התכנית מנורמל אוטומטית; אם הוא מתנגש בתכנית קיימת — תופנה למיזוג."
        initial={toForm(plan)}
        mode="edit"
        planId={plan.id}
        onClose={() => setEditing(false)}
      />
    );
  }

  return (
    <div
      className={cn(
        "rounded-lg border border-rule shadow-sm p-4 space-y-3",
        isPending ? "bg-gold-wash" : "bg-surface",
      )}
    >
      <div className="flex items-center gap-2 flex-wrap">
        <Badge className="bg-gold text-white rounded">תכנית</Badge>
        <span className="text-navy font-bold text-[0.95rem]">
          {plan.display_name || plan.plan_number}
        </span>
        {isApproved && (
          <span className="inline-flex items-center gap-1 rounded-full bg-success-bg text-success text-[0.72rem] font-semibold px-2.5 py-0.5">
            <BadgeCheck className="size-3.5" /> מאושרת
          </span>
        )}
        {!isPending && !isApproved && (
          <span className="rounded-full bg-danger-bg text-danger text-[0.72rem] font-semibold px-2.5 py-0.5">
            נדחתה
          </span>
        )}
        {plan.plan_type && (
          <span className="rounded-full bg-info-bg text-[#365070] text-[0.72rem] font-semibold px-2.5 py-0.5">
            {plan.plan_type}
          </span>
        )}
        {plan.source_case_number && (
          <span className="ms-auto text-ink-muted text-[0.72rem]">
            מקור: {plan.source_case_number}
          </span>
        )}
      </div>

      {duplicates.length > 0 && (
        <div className="rounded-md border border-[#e8d3a8] bg-warn-bg px-3 py-2 space-y-1.5">
          <div className="flex items-center gap-2 text-[0.78rem] text-gold-deep">
            <AlertTriangle className="size-4 shrink-0" />
            <span>
              כפילות אפשרית — ייתכן שאותה תכנית כבר במרשם בצורה אחרת. מזג כדי לא
              לפצל תכנית אחת לשתי רשומות.
            </span>
          </div>
          {duplicates.map((d) => (
            <div key={d.id} className="flex items-center gap-2 flex-wrap ps-6">
              <span className="text-[0.78rem] text-navy font-medium">
                {d.display_name || d.plan_number}
              </span>
              {d.match_reason && (
                <span className="text-[0.68rem] text-ink-muted">({d.match_reason})</span>
              )}
              <Button
                size="sm" variant="outline"
                className="ms-auto h-7 border-gold text-gold-deep text-[0.72rem]"
                disabled={merge.isPending}
                onClick={() => foldIn(d.id, d.display_name || d.plan_number)}
              >
                <GitMerge className="size-3.5" /> מזג לכאן
              </Button>
            </div>
          ))}
        </div>
      )}

      <div>
        <div className="text-[0.72rem] text-ink-muted font-semibold mb-1.5">
          משפט-הציטוט הקנוני (כפי שייכתב בבלוק ט)
        </div>
        <div className="border-s-[3px] border-gold bg-white/65 rounded-e px-3.5 py-2.5 text-[0.92rem] leading-relaxed font-medium">
          {clean(plan.citation_formatted)}
        </div>
      </div>

      <div className="flex flex-wrap gap-x-6 gap-y-1 text-[0.78rem] text-ink-soft">
        <div><span className="text-ink-muted">תאריך רשומות:</span> {fmtDate(plan.gazette_date)}</div>
        <div><span className="text-ink-muted">ילקוט פרסומים:</span> {plan.yalkut_number || "—"}</div>
        {plan.plan_type && <div><span className="text-ink-muted">סוג:</span> {plan.plan_type}</div>}
      </div>

      {noDate && (
        <div className="text-[0.78rem] text-danger font-medium">
          ⚠ חסר תאריך-תוקף — תצוטט ללא תוקף עד השלמה.
        </div>
      )}

      <div className="flex items-center gap-2 flex-wrap justify-end pt-2.5 border-t border-rule-soft">
        <Button
          variant="ghost" size="sm" className="me-auto text-ink-muted"
          onClick={() => setEditing(true)}
        >
          <Edit2 className="size-4" /> {noDate && isPending ? "השלם תוקף" : "ערוך / תקן"}
        </Button>
        {isPending ? (
          <>
            <Button
              variant="outline" size="sm" className="border-[#e3c4c4] text-danger"
              disabled={review.isPending}
              onClick={() => decide("rejected")}
            >
              <X className="size-4" /> דחה
            </Button>
            <Button
              size="sm" className="bg-gold text-navy hover:bg-gold-deep hover:text-white"
              disabled={review.isPending}
              onClick={() => decide("approved")}
            >
              <Check className="size-4" /> {noDate ? "אשר (ללא תוקף)" : "אשר"}
            </Button>
          </>
        ) : (
          // Already decided (approved / rejected) — only return it to the queue.
          <Button
            variant="outline" size="sm" className="border-gold text-gold-deep"
            disabled={review.isPending}
            onClick={() => decide("pending_review")}
          >
            <Undo2 className="size-4" /> החזר לתור
          </Button>
        )}
      </div>
    </div>
  );
}

// ─── add / edit form (inline) ───────────────────────────────────────────────

function PlanForm({
  title, subtitle, initial, mode, planId, onClose,
}: {
  title: string;
  subtitle: string;
  initial: EditForm;
  mode: "add" | "edit";
  planId?: string;
  onClose: () => void;
}) {
  const [f, setF] = useState<EditForm>(initial);
  const [sourceUrl, setSourceUrl] = useState("");
  const upsert = useUpsertPlan();
  const update = useUpdatePlan();
  const fetchMavat = useFetchPlan();
  const busy = upsert.isPending || update.isPending;

  function set<K extends keyof EditForm>(k: K, v: string) {
    setF((prev) => ({ ...prev, [k]: v }));
  }

  // Pull identity + validity from the official source (mavat) into the fields.
  // Fills each field mavat returns (keeps a chair value only where mavat is
  // empty); the chair still reviews + saves. Slow — a real browser on the host.
  async function pullFromMavat() {
    const num = f.plan_number.trim();
    if (!num) {
      toast.error("הקלד מספר-תכנית למשיכה");
      return;
    }
    try {
      const r = await fetchMavat.mutateAsync(num);
      setF((prev) => ({
        plan_number: r.plan_number || prev.plan_number,
        display_name: r.display_name || prev.display_name,
        plan_type: r.plan_type || prev.plan_type,
        gazette_date: r.gazette_date || prev.gazette_date,
        yalkut_number: r.yalkut_number || prev.yalkut_number,
        purpose: r.purpose || prev.purpose,
      }));
      setSourceUrl(r.source_url || "");
      toast.success(
        r.yalkut_number || r.gazette_date
          ? "נמשך ממנהל-התכנון — בדוק ואשר"
          : "נמצא במנהל-התכנון אך ללא תוקף מפורסם — השלם ידנית",
      );
    } catch (e) {
      const msg = (e as { body?: { detail?: string } })?.body?.detail;
      toast.error(msg || "שגיאה במשיכה ממנהל-התכנון");
    }
  }

  async function save() {
    if (!f.plan_number.trim()) {
      toast.error("חובה מספר-תכנית");
      return;
    }
    try {
      if (mode === "edit" && planId) {
        const patch: PlanEdit = {
          plan_number: f.plan_number, display_name: f.display_name,
          plan_type: f.plan_type, gazette_date: f.gazette_date,
          yalkut_number: f.yalkut_number, purpose: f.purpose,
        };
        await update.mutateAsync({ id: planId, patch });
      } else {
        await upsert.mutateAsync({
          plan_number: f.plan_number, display_name: f.display_name,
          plan_type: f.plan_type, gazette_date: f.gazette_date,
          yalkut_number: f.yalkut_number, purpose: f.purpose,
          review_status: "approved",
        });
      }
      toast.success(mode === "edit" ? "התכנית נשמרה" : "התכנית נוספה");
      onClose();
    } catch (e) {
      // surface the backend's collision/validation message (no silent failure)
      const msg = (e as { body?: { detail?: string } })?.body?.detail;
      toast.error(msg || "שגיאה בשמירה");
    }
  }

  const field = (
    k: keyof EditForm, label: string, hint?: string, full = false,
  ) => (
    <div className={full ? "col-span-2" : ""}>
      <Label className="text-[0.72rem] text-ink-muted mb-1 block">{label}</Label>
      <Input value={f[k]} onChange={(e) => set(k, e.target.value)} />
      {hint && <p className="text-[0.66rem] text-ink-muted mt-1">{hint}</p>}
    </div>
  );

  return (
    <div className="rounded-lg border border-rule bg-surface shadow-sm p-4 space-y-3">
      <div>
        <h3 className="text-navy text-sm font-bold m-0">{title}</h3>
        <p className="text-ink-muted text-xs mt-0.5">{subtitle}</p>
      </div>

      {/* pull validity from the official source (mavat) into the fields below */}
      <div className="flex items-center gap-3 flex-wrap rounded-md border border-[#cdd9e8] bg-info-bg px-3 py-2.5">
        <Button
          size="sm"
          className="bg-navy text-white hover:bg-navy-soft shrink-0"
          disabled={fetchMavat.isPending}
          onClick={pullFromMavat}
        >
          {fetchMavat.isPending ? (
            <><Loader2 className="size-4 animate-spin" /> מושך…</>
          ) : (
            <><DownloadCloud className="size-4" /> משוך מ-מנהל-התכנון</>
          )}
        </Button>
        <span className="text-[0.72rem] text-ink-soft leading-relaxed flex-1 min-w-[220px]">
          {fetchMavat.isPending
            ? "מושך ממנהל-התכנון — עד כדקה (דפדפן חי)…"
            : 'הקלד מספר-תכנית ולחץ — שם, תאריך-רשומות, מס\' ילקוט (י"פ), סוג וייעוד יימשכו מהמקור הרשמי. שדה שהמקור אינו חושף יישאר ריק — לא מומצא.'}
        </span>
      </div>

      <div className="grid grid-cols-2 gap-3">
        {field("plan_number", "מספר תכנית", 'מזהה בלבד (ללא המילה "תכנית")')}
        {field("display_name", "שם תצוגה")}
        {field("gazette_date", "תאריך פרסום ברשומות", "YYYY-MM-DD")}
        {field("yalkut_number", 'מס\' ילקוט פרסומים (י"פ)')}
        {field("plan_type", "סוג תכנית", "ארצית / מחוזית / מקומית / מפורטת / כוללנית")}
        {field("purpose", "ייעוד (משפט אחד)", undefined, true)}
      </div>
      <div className="rounded-md border border-dashed border-gold bg-gold-wash px-3.5 py-2.5">
        <span className="block text-[0.66rem] text-gold-deep font-bold mb-1">
          תצוגה מקדימה — כך ייכתב בבלוק ט:
        </span>
        <span className="text-[0.88rem] leading-relaxed">{previewCitation(f)}</span>
        {sourceUrl && (
          <a
            href={sourceUrl} target="_blank" rel="noopener noreferrer"
            className="mt-2 inline-flex items-center gap-1 text-[0.7rem] text-[#365070] hover:underline"
          >
            <ExternalLink className="size-3" /> מקור: מנהל-התכנון
          </a>
        )}
      </div>
      <div className="flex items-center gap-2 justify-end">
        <Button variant="ghost" size="sm" className="me-auto text-ink-muted" onClick={onClose}>
          ביטול
        </Button>
        <Button
          size="sm" className="bg-gold text-navy hover:bg-gold-deep hover:text-white"
          disabled={busy} onClick={save}
        >
          <Save className="size-4" /> שמור
        </Button>
      </div>
    </div>
  );
}
