"use client";

import {
  CheckCircle2,
  ChevronDown,
  ChevronRight,
  Download,
  Eye,
  FileText,
  Printer,
  RefreshCw,
  Search,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useMemo, useRef, useState } from "react";

import type { Salarie } from "@/types/type";

type Signataire = { id: number; prenomnom: string | null; fonction: string | null };
type Field = {
  key: string;
  label: string;
  type: "text" | "date" | "number" | "boolean";
  source: "manual" | "employee" | "contract" | "signatory" | "establishment";
  sourcePath: string | null;
  required: boolean;
};
type Template = {
  id: number;
  code: string;
  libelle: string;
  description: string | null;
  categorie: string;
  etablissement: string | null;
  version: number;
  champs: Field[];
};
type Value = string | number | boolean;
type DraftPayload = {
  template: { id: number; code: string; libelle: string; version: number };
  fields: Field[];
  values: Record<string, Value>;
};

function employeeLabel(employee: Salarie) {
  return [employee.NSA, employee.PRE].filter(Boolean).join(" ") || `COS ${employee.COS}`;
}

function sourceLabel(source: Field["source"]) {
  if (source === "employee") return "Salarié";
  if (source === "contract") return "Contrat";
  if (source === "signatory") return "Signataire";
  if (source === "establishment") return "Document";
  return "À renseigner";
}

function errorMessage(status: number, payload: { message?: string } | null) {
  if (status === 401) return "Votre session a expiré.";
  if (status === 403) return "Vous n'avez pas accès aux impressions.";
  if (payload?.message === "TEMPLATE_NOT_FOUND") return "Ce modèle n'est plus actif.";
  if (payload?.message === "EMPLOYE_NOT_FOUND") return "Ce salarié est introuvable ou hors de votre périmètre.";
  if (payload?.message === "SIGNATAIRE_NOT_FOUND") return "Le signataire choisi n'est plus actif.";
  if (payload?.message === "INVALID_VALUES") return "Certains champs obligatoires sont incomplets.";
  return "Une erreur est survenue pendant la préparation du document.";
}

export default function DynamicDocumentsAutomatiques() {
  const router = useRouter();
  const previewFrameRef = useRef<HTMLIFrameElement | null>(null);
  const previewUrlRef = useRef<string | null>(null);
  const [employees, setEmployees] = useState<Salarie[]>([]);
  const [signatories, setSignatories] = useState<Signataire[]>([]);
  const [templates, setTemplates] = useState<Template[]>([]);
  const [selectedCos, setSelectedCos] = useState<number | null>(null);
  const [selectedTemplateId, setSelectedTemplateId] = useState<number | null>(null);
  const [templateSearch, setTemplateSearch] = useState("");
  const [expandedCategory, setExpandedCategory] = useState<string | null>(null);
  const [signatoryId, setSignatoryId] = useState<number | null>(null);
  const [search, setSearch] = useState("");
  const [draft, setDraft] = useState<DraftPayload | null>(null);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const [previewBlob, setPreviewBlob] = useState<Blob | null>(null);
  const [loading, setLoading] = useState(true);
  const [loadingDraft, setLoadingDraft] = useState(false);
  const [generating, setGenerating] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState<string | null>(null);

  function replacePreview(blob: Blob | null) {
    if (previewUrlRef.current) URL.revokeObjectURL(previewUrlRef.current);
    const url = blob ? URL.createObjectURL(blob) : null;
    previewUrlRef.current = url;
    setPreviewUrl(url);
    setPreviewBlob(blob);
  }

  useEffect(() => () => {
    if (previewUrlRef.current) URL.revokeObjectURL(previewUrlRef.current);
  }, []);

  useEffect(() => {
    let cancelled = false;
    async function load() {
      const token = localStorage.getItem("token");
      if (!token) {
        router.push("/login");
        return;
      }
      try {
        const headers = { Authorization: `Bearer ${token}` };
        const [employeesResponse, signatoriesResponse, templatesResponse] = await Promise.all([
          fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/employes`, { headers, cache: "no-store" }),
          fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/impressions/signataires`, { headers, cache: "no-store" }),
          fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/impressions/modeles-documents`, { headers, cache: "no-store" }),
        ]);
        if ([employeesResponse, signatoriesResponse, templatesResponse].some((response) => response.status === 401)) {
          localStorage.removeItem("token");
          localStorage.removeItem("user");
          router.push("/login");
          return;
        }
        if (!employeesResponse.ok || !signatoriesResponse.ok || !templatesResponse.ok) {
          throw new Error("Impossible de charger les modèles de documents.");
        }
        const [employeesData, signatoriesData, templatesData] = await Promise.all([
          employeesResponse.json(), signatoriesResponse.json(), templatesResponse.json(),
        ]);
        if (cancelled) return;
        const loadedEmployees = Array.isArray(employeesData.items) ? employeesData.items : [];
        const loadedSignatories = Array.isArray(signatoriesData.items) ? signatoriesData.items : [];
        const loadedTemplates = Array.isArray(templatesData.items) ? templatesData.items : [];
        setEmployees(loadedEmployees);
        setSignatories(loadedSignatories);
        setTemplates(loadedTemplates);
        setExpandedCategory((current) => current ?? (loadedTemplates[0]?.categorie || "Autres"));
        if (loadedTemplates.length === 1) setSelectedTemplateId(loadedTemplates[0].id);
        if (loadedSignatories.length === 1) setSignatoryId(loadedSignatories[0].id);
        const requestedCos = Number(new URLSearchParams(window.location.search).get("cos"));
        if (Number.isInteger(requestedCos) && loadedEmployees.some((employee: Salarie) => employee.COS === requestedCos)) {
          setSelectedCos(requestedCos);
        }
      } catch (caught) {
        if (!cancelled) setError(caught instanceof Error ? caught.message : "Chargement impossible.");
      } finally {
        if (!cancelled) setLoading(false);
      }
    }
    void load();
    return () => { cancelled = true; };
  }, [router]);

  useEffect(() => {
    if (selectedCos === null || selectedTemplateId === null) {
      setDraft(null);
      replacePreview(null);
      return;
    }
    const controller = new AbortController();
    async function loadDraft() {
      const token = localStorage.getItem("token");
      if (!token) return;
      setLoadingDraft(true);
      setError(null);
      setSuccess(null);
      replacePreview(null);
      const query = new URLSearchParams({
        cos: String(selectedCos),
        signataireId: signatoryId == null ? "" : String(signatoryId),
      });
      try {
        const response = await fetch(
          `${process.env.NEXT_PUBLIC_API_URL}/api/impressions/modeles-documents/${selectedTemplateId}/draft?${query}`,
          { headers: { Authorization: `Bearer ${token}` }, cache: "no-store", signal: controller.signal },
        );
        if (!response.ok) {
          const payload = await response.json().catch(() => null);
          throw new Error(errorMessage(response.status, payload));
        }
        setDraft(await response.json());
      } catch (caught) {
        if (caught instanceof DOMException && caught.name === "AbortError") return;
        setDraft(null);
        setError(caught instanceof Error ? caught.message : "Préparation impossible.");
      } finally {
        if (!controller.signal.aborted) setLoadingDraft(false);
      }
    }
    void loadDraft();
    return () => controller.abort();
  }, [selectedCos, selectedTemplateId, signatoryId]);

  const sortedEmployees = useMemo(
    () => [...employees].sort((a, b) => employeeLabel(a).localeCompare(employeeLabel(b), "fr")),
    [employees],
  );
  const filteredEmployees = useMemo(() => {
    const needle = search.trim().toLocaleLowerCase("fr");
    if (!needle) return sortedEmployees;
    return sortedEmployees.filter((employee) =>
      `${employee.NSA ?? ""} ${employee.PRE ?? ""} ${employee.COS}`.toLocaleLowerCase("fr").includes(needle),
    );
  }, [search, sortedEmployees]);
  const templatesByCategory = useMemo(() => {
    const needle = templateSearch.trim().toLocaleLowerCase("fr");
    const filtered = needle
      ? templates.filter((template) =>
          `${template.libelle} ${template.description ?? ""} ${template.categorie}`
            .toLocaleLowerCase("fr")
            .includes(needle),
        )
      : templates;
    const groups = new Map<string, Template[]>();
    for (const template of filtered) {
      const category = template.categorie || "Autres";
      groups.set(category, [...(groups.get(category) ?? []), template]);
    }
    return [...groups.entries()]
      .sort(([left], [right]) => left.localeCompare(right, "fr"))
      .map(([category, items]) => ({
        category,
        items: [...items].sort((left, right) => left.libelle.localeCompare(right.libelle, "fr")),
      }));
  }, [templateSearch, templates]);
  const selectedTemplate = templates.find((template) => template.id === selectedTemplateId) ?? null;
  const missingFields = useMemo(() => {
    if (!draft) return [];
    return draft.fields.filter((field) => {
      if (!field.required) return false;
      const value = draft.values[field.key];
      return value == null || (typeof value === "string" && !value.trim());
    });
  }, [draft]);

  function updateValue(key: string, value: Value) {
    setDraft((current) => current ? { ...current, values: { ...current.values, [key]: value } } : current);
    replacePreview(null);
    setSuccess(null);
  }

  async function generate(format: "pdf" | "docx", download = false) {
    if (!draft || selectedCos === null || selectedTemplateId === null || missingFields.length > 0) return;
    const token = localStorage.getItem("token");
    if (!token) return router.push("/login");
    setGenerating(true);
    setError(null);
    setSuccess(null);
    try {
      const response = await fetch(
        `${process.env.NEXT_PUBLIC_API_URL}/api/impressions/modeles-documents/${selectedTemplateId}/generate?format=${format}`,
        {
          method: "POST",
          headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
          body: JSON.stringify({ cos: selectedCos, signataireId: signatoryId, values: draft.values }),
        },
      );
      if (!response.ok) {
        const payload = await response.json().catch(() => null);
        throw new Error(errorMessage(response.status, payload));
      }
      const blob = await response.blob();
      if (format === "pdf" && !download) {
        replacePreview(blob);
        setSuccess("L'aperçu a été actualisé.");
      } else {
        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = `${selectedTemplate?.code ?? "document"}-${selectedCos}.${format}`;
        document.body.appendChild(link);
        link.click();
        link.remove();
        URL.revokeObjectURL(url);
        setSuccess(`${format === "pdf" ? "Le PDF" : "Le document Word"} a été téléchargé.`);
      }
    } catch (caught) {
      setError(caught instanceof Error ? caught.message : "Génération impossible.");
    } finally {
      setGenerating(false);
    }
  }

  function printPdf() {
    const previewWindow = previewFrameRef.current?.contentWindow;
    if (!previewWindow) return setError("Générez d'abord l'aperçu PDF.");
    previewWindow.focus();
    previewWindow.print();
  }

  if (loading) {
    return <div className="p-10 text-center text-slate-600">Chargement des modèles actifs...</div>;
  }

  return (
    <main className="min-h-screen bg-slate-100 p-4 text-slate-900 md:p-6">
      <div className="mx-auto max-w-[1500px]">
        {error && <div className="mb-4 rounded-md border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-700">{error}</div>}
        {success && <div className="mb-4 flex items-center gap-2 rounded-md border border-emerald-300 bg-emerald-50 px-4 py-3 text-sm text-emerald-700"><CheckCircle2 size={18} />{success}</div>}
        <div className="grid items-start gap-5 xl:grid-cols-[420px_minmax(0,1fr)]">
          <div className="space-y-5">
            <section className="rounded-lg bg-white p-5 shadow">
              <h2 className="font-bold">1. Salarié</h2>
              <p className="mb-3 text-sm text-slate-500">{sortedEmployees.length} salarié(s) disponible(s)</p>
              <label className="relative mb-3 block">
                <Search className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" size={18} />
                <input type="search" value={search} onChange={(event) => setSearch(event.target.value)} placeholder="Nom, prénom ou COS..." className="w-full rounded-md border border-slate-300 py-2 pl-10 pr-3 text-sm outline-none focus:border-blue-500" />
              </label>
              <div className="max-h-52 overflow-y-auto rounded-md border border-slate-200">
                {filteredEmployees.map((employee) => (
                  <button key={employee.COS} type="button" onClick={() => setSelectedCos(employee.COS)} className={`flex w-full justify-between border-b border-slate-100 px-3 py-2.5 text-left text-sm ${selectedCos === employee.COS ? "bg-blue-600 text-white" : "bg-white hover:bg-slate-50"}`}>
                    <span className="font-semibold">{employeeLabel(employee)}</span><span className="opacity-70">{employee.COS}</span>
                  </button>
                ))}
              </div>
            </section>

            <section className="rounded-lg bg-white p-5 shadow">
              <h2 className="mb-3 font-bold">2. Modèle actif</h2>
              {templates.length === 0 ? (
                <p className="rounded-md bg-amber-50 p-3 text-sm text-amber-800">Aucun modèle importé n'est actif. Activez-en un dans Paramètres → Modèles de documents.</p>
              ) : (
                <div>
                  <label className="relative mb-3 block">
                    <Search className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" size={17} />
                    <input
                      type="search"
                      value={templateSearch}
                      onChange={(event) => setTemplateSearch(event.target.value)}
                      placeholder="Rechercher un modèle..."
                      className="w-full rounded-md border border-slate-300 py-2 pl-9 pr-3 text-sm outline-none focus:border-blue-500"
                    />
                  </label>
                  <div className="max-h-[28rem] space-y-2 overflow-y-auto pr-1">
                    {templatesByCategory.length === 0 ? (
                      <p className="rounded-md bg-slate-50 p-3 text-center text-sm text-slate-500">Aucun modèle trouvé.</p>
                    ) : templatesByCategory.map(({ category, items }) => {
                      const isOpen = Boolean(templateSearch.trim()) || expandedCategory === category;
                      return (
                        <div key={category} className="overflow-hidden rounded-lg border border-slate-200 bg-white">
                          <button
                            type="button"
                            onClick={() => setExpandedCategory(isOpen ? null : category)}
                            className={`flex w-full items-center justify-between gap-3 px-3 py-3 text-left transition ${isOpen ? "bg-blue-50 text-blue-800" : "hover:bg-slate-50"}`}
                          >
                            <span className="flex min-w-0 items-center gap-2 font-semibold">
                              {isOpen ? <ChevronDown size={18} /> : <ChevronRight size={18} />}
                              <span className="truncate">{category}</span>
                            </span>
                            <span className="rounded-full bg-white px-2 py-0.5 text-xs font-bold text-slate-500 shadow-sm">{items.length}</span>
                          </button>
                          {isOpen && (
                            <div className="space-y-2 border-t border-slate-200 bg-slate-50 p-2">
                              {items.map((template) => (
                                <button
                                  key={template.id}
                                  type="button"
                                  onClick={() => {
                                    setSelectedTemplateId(template.id);
                                    setExpandedCategory(category);
                                  }}
                                  className={`flex w-full gap-3 rounded-md border p-3 text-left transition ${selectedTemplateId === template.id ? "border-blue-500 bg-white ring-2 ring-blue-100" : "border-slate-200 bg-white hover:border-blue-300"}`}
                                >
                                  <FileText className="mt-0.5 shrink-0 text-blue-600" size={20} />
                                  <span className="min-w-0">
                                    <span className="block font-bold leading-snug">{template.libelle}</span>
                                    {template.description && <span className="mt-1 block text-xs text-slate-500">{template.description}</span>}
                                    <span className="mt-1 block text-[11px] text-slate-400">Version {template.version} · {template.champs.length} champ(s)</span>
                                  </span>
                                </button>
                              ))}
                            </div>
                          )}
                        </div>
                      );
                    })}
                  </div>
                </div>
              )}
              <label className="mt-4 block text-xs font-semibold text-slate-600">Signataire (si le modèle en utilise un)</label>
              <select value={signatoryId ?? ""} onChange={(event) => setSignatoryId(event.target.value ? Number(event.target.value) : null)} className="mt-1 w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm">
                <option value="">Aucun signataire</option>
                {signatories.map((signatory) => <option key={signatory.id} value={signatory.id}>{[signatory.prenomnom, signatory.fonction].filter(Boolean).join(" - ")}</option>)}
              </select>
            </section>

            <section className="rounded-lg bg-white p-5 shadow">
              <h2 className="font-bold">3. Valeurs du document</h2>
              <p className="mb-4 text-xs text-slate-500">Les valeurs préremplies restent modifiables avant génération.</p>
              {loadingDraft ? <p className="flex items-center gap-2 text-sm text-slate-500"><RefreshCw className="animate-spin" size={16} />Préparation...</p> : !draft ? <p className="text-sm text-slate-500">Sélectionnez un salarié et un modèle.</p> : (
                <div className="grid gap-3 sm:grid-cols-2">
                  {draft.fields.map((field) => (
                    <label key={field.key} className={field.type === "text" && String(draft.values[field.key] ?? "").length > 120 ? "sm:col-span-2" : ""}>
                      <span className="mb-1 flex items-center justify-between gap-2 text-xs font-semibold text-slate-600"><span>{field.label}{field.required && <span className="text-red-500"> *</span>}</span><span className={`rounded px-1.5 py-0.5 text-[10px] ${field.source === "manual" ? "bg-amber-100 text-amber-700" : "bg-slate-100 text-slate-500"}`}>{sourceLabel(field.source)}</span></span>
                      {field.type === "boolean" ? (
                        <select value={String(draft.values[field.key] ?? false)} onChange={(event) => updateValue(field.key, event.target.value === "true")} className="w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm"><option value="true">Oui</option><option value="false">Non</option></select>
                      ) : String(draft.values[field.key] ?? "").length > 120 ? (
                        <textarea rows={4} value={String(draft.values[field.key] ?? "")} onChange={(event) => updateValue(field.key, event.target.value)} className="w-full rounded-md border border-slate-300 px-3 py-2 text-sm" />
                      ) : (
                        <input type={field.key.toLowerCase().includes("dateheure") ? "datetime-local" : field.type === "date" ? "date" : field.type === "number" ? "number" : "text"} value={String(draft.values[field.key] ?? "")} onChange={(event) => updateValue(field.key, field.type === "number" ? Number(event.target.value) : event.target.value)} className="w-full rounded-md border border-slate-300 px-3 py-2 text-sm" />
                      )}
                    </label>
                  ))}
                </div>
              )}
              {missingFields.length > 0 && <p className="mt-3 rounded-md bg-amber-50 p-2 text-xs text-amber-800">À compléter : {missingFields.map((field) => field.label).join(", ")}.</p>}
            </section>
          </div>

          <section className="sticky top-4 rounded-lg bg-slate-800 p-4 shadow-xl">
            <div className="mb-3 flex flex-wrap items-center justify-between gap-2 text-white">
              <div><h2 className="flex items-center gap-2 font-bold"><Eye size={19} />Aperçu PDF</h2><p className="text-xs text-slate-300">Word et PDF proviennent du même modèle actif.</p></div>
              <div className="flex flex-wrap gap-2">
                <button type="button" disabled={!draft || missingFields.length > 0 || generating} onClick={() => void generate("pdf")} className="inline-flex items-center gap-2 rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold disabled:opacity-50"><RefreshCw className={generating ? "animate-spin" : ""} size={17} />Aperçu</button>
                <button type="button" disabled={!previewBlob || generating} onClick={() => void generate("pdf", true)} className="inline-flex items-center gap-2 rounded-md bg-slate-600 px-3 py-2 text-sm font-semibold disabled:opacity-50"><Download size={17} />PDF</button>
                <button type="button" disabled={!draft || missingFields.length > 0 || generating} onClick={() => void generate("docx", true)} className="inline-flex items-center gap-2 rounded-md bg-slate-600 px-3 py-2 text-sm font-semibold disabled:opacity-50"><FileText size={17} />Word</button>
                <button type="button" disabled={!previewUrl || generating} onClick={printPdf} className="inline-flex items-center gap-2 rounded-md bg-slate-600 px-3 py-2 text-sm font-semibold disabled:opacity-50"><Printer size={17} />Imprimer</button>
              </div>
            </div>
            <div className="flex min-h-[720px] items-center justify-center overflow-hidden rounded bg-white">
              {previewUrl ? <iframe ref={previewFrameRef} title="Aperçu PDF" src={previewUrl} className="h-[calc(100vh-180px)] min-h-[720px] w-full" /> : <div className="text-center text-slate-400"><FileText className="mx-auto mb-3" size={48} /><p className="font-semibold">L'aperçu apparaîtra ici</p><p className="text-sm">Complétez les champs puis cliquez sur « Aperçu ».</p></div>}
            </div>
          </section>
        </div>
      </div>
    </main>
  );
}
