"use client";

import { useEffect, useMemo, useState } from "react";

type TemplateField = {
  key: string;
  label: string;
  type: string;
  source: string;
  required: boolean;
};

type TemplateVersion = {
  id: number;
  numero: number;
  nom_fichier_original: string;
  taille_octets: number;
  sha256: string;
  champs: TemplateField[];
  statut: string;
  date_creation: string;
};

type DocumentTemplate = {
  id: number;
  code: string;
  libelle: string;
  description: string | null;
  categorie: string;
  etablissement: string | null;
  version_active: number | null;
  actif: boolean;
  versions: TemplateVersion[];
};

type Signataire = {
  id: number;
  prenomnom: string;
  fonction: string;
  actif: boolean;
};

type BatchImportItem = {
  fichier: string;
  code: string;
  libelle: string;
  status: "imported" | "skipped" | "failed";
  message?: string;
};

type BatchImportReport = {
  summary: {
    total: number;
    imported: number;
    skipped: number;
    failed: number;
  };
  results: BatchImportItem[];
};

const CATEGORIES = [
  "Attestations",
  "Courriers",
  "Sanctions et avertissements",
  "Mutuelle et prévoyance",
  "Préfecture et titres de séjour",
  "Visites médicales",
  "Contrats",
  "Avenants",
  "Autres",
];

function slug(value: string) {
  return value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-|-$/g, "")
    .slice(0, 100);
}

function formatBytes(value: number) {
  if (value < 1024) return `${value} o`;
  if (value < 1024 * 1024) return `${Math.round(value / 1024)} Ko`;
  return `${(value / 1024 / 1024).toFixed(1)} Mo`;
}

function messageFor(code?: string) {
  if (code === "MACRO_FORBIDDEN") return "Les macros sont interdites. Convertissez le fichier en .docx.";
  if (code === "NO_PLACEHOLDER") return "Aucun champ {nomDuChamp} n'a été détecté dans le document.";
  if (code === "INVALID_FILE") return "Le fichier DOCX est invalide, corrompu ou trop volumineux.";
  if (code === "CODE_EXISTS") return "Ce code technique est déjà utilisé par un autre modèle.";
  if (code === "VERSION_NOT_FOUND") return "Cette version est introuvable.";
  if (code === "INVALID_ARCHIVE") return "Le ZIP est invalide, corrompu ou trop volumineux.";
  if (code === "MANIFEST_REQUIRED") return "Le ZIP ne contient aucun manifest.csv ou manifest.json.";
  if (code === "INVALID_MANIFEST") return "Le manifeste est invalide ou incomplet.";
  if (code === "TOO_MANY_TEMPLATES") return "Un lot ne peut pas contenir plus de 100 modèles.";
  if (code === "FILE_NOT_FOUND_OR_AMBIGUOUS") return "Le DOCX indiqué est absent ou son chemin est ambigu.";
  if (code === "DUPLICATE_IN_MANIFEST") return "Ce code apparaît plusieurs fois dans le manifeste.";
  if (code === "IMPORT_FAILED") return "L'import de ce DOCX a échoué.";
  return code || "L'opération a échoué.";
}

export default function DocumentTemplatesPanel() {
  const [templates, setTemplates] = useState<DocumentTemplate[]>([]);
  const [selectedId, setSelectedId] = useState<number | null>(null);
  const [libelle, setLibelle] = useState("");
  const [code, setCode] = useState("");
  const [description, setDescription] = useState("");
  const [categorie, setCategorie] = useState(CATEGORIES[0]);
  const [etablissement, setEtablissement] = useState("");
  const [file, setFile] = useState<File | null>(null);
  const [batchFile, setBatchFile] = useState<File | null>(null);
  const [batchReport, setBatchReport] = useState<BatchImportReport | null>(null);
  const [batchImporting, setBatchImporting] = useState(false);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [previewing, setPreviewing] = useState(false);
  const [previewed, setPreviewed] = useState<Set<string>>(new Set());
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState<string | null>(null);

  const [signataires, setSignataires] = useState<Signataire[]>([]);
  const [signatairesLoading, setSignatairesLoading] = useState(true);
  const [newSignataire, setNewSignataire] = useState({ prenomnom: "", fonction: "" });
  const [signatairesError, setSignatairesError] = useState<string | null>(null);

  const selected = templates.find((item) => item.id === selectedId) ?? null;
  const availableCategories = useMemo(() => {
    const importedCategories = templates
      .map((item) => item.categorie.trim())
      .filter((name) => name.length > 0 && !CATEGORIES.includes(name))
      .sort((left, right) => left.localeCompare(right, "fr"));

    return [...CATEGORIES, ...new Set(importedCategories)];
  }, [templates]);
  const grouped = useMemo(
    () => availableCategories.map((name) => ({
      name,
      items: templates.filter((item) => (item.categorie.trim() || "Autres") === name),
    })).filter((group) => group.items.length > 0),
    [availableCategories, templates],
  );

  async function api(init?: RequestInit) {
    const token = localStorage.getItem("token");
    return fetch(
      `${process.env.NEXT_PUBLIC_API_URL}/api/admin/parametres/modeles-documents`,
      {
        ...init,
        cache: "no-store",
        headers: {
          Authorization: `Bearer ${token}`,
          ...init?.headers,
        },
      },
    );
  }

  // Signataires : réutilise l'API générique "Tables et valeurs"
  // (super_admin), pas de route dédiée.
  async function tablesApi(path: string, init?: RequestInit) {
    const token = localStorage.getItem("token");
    return fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/tables/ref_signataires${path}`, {
      ...init,
      cache: "no-store",
      headers: {
        Authorization: `Bearer ${token}`,
        ...(init?.body ? { "Content-Type": "application/json" } : {}),
        ...init?.headers,
      },
    });
  }

  async function loadSignataires() {
    setSignatairesLoading(true);
    setSignatairesError(null);
    try {
      const response = await tablesApi("?limit=100");
      const data = await response.json().catch(() => null);
      if (!response.ok) throw new Error(data?.message ?? "Chargement impossible.");
      setSignataires((data?.rows ?? []) as Signataire[]);
    } catch (reason) {
      setSignatairesError(reason instanceof Error ? reason.message : "Chargement impossible.");
    } finally {
      setSignatairesLoading(false);
    }
  }

  async function toggleSignataire(signataire: Signataire) {
    setSignatairesError(null);
    try {
      const response = await tablesApi(`/${signataire.id}`, {
        method: "PATCH",
        body: JSON.stringify({ actif: !signataire.actif }),
      });
      const data = await response.json().catch(() => null);
      if (!response.ok) throw new Error(data?.message ?? "Modification impossible.");
      await loadSignataires();
    } catch (reason) {
      setSignatairesError(reason instanceof Error ? reason.message : "Modification impossible.");
    }
  }

  async function addSignataire() {
    if (!newSignataire.prenomnom.trim() || !newSignataire.fonction.trim()) {
      setSignatairesError("Renseignez le nom et la fonction.");
      return;
    }
    setSignatairesError(null);
    try {
      const response = await tablesApi("", {
        method: "POST",
        body: JSON.stringify({ ...newSignataire, actif: true }),
      });
      const data = await response.json().catch(() => null);
      if (!response.ok) throw new Error(data?.message ?? "Ajout impossible.");
      setNewSignataire({ prenomnom: "", fonction: "" });
      await loadSignataires();
    } catch (reason) {
      setSignatairesError(reason instanceof Error ? reason.message : "Ajout impossible.");
    }
  }

  async function load() {
    setLoading(true);
    setError(null);
    try {
      const response = await api();
      const data = await response.json().catch(() => null);
      if (!response.ok) throw new Error(messageFor(data?.message));
      setTemplates(data?.items ?? []);
    } catch (reason) {
      setError(reason instanceof Error ? reason.message : "Chargement impossible.");
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    void load();
    void loadSignataires();
  }, []);

  function selectTemplate(template: DocumentTemplate) {
    setSelectedId(template.id);
    setLibelle(template.libelle);
    setCode(template.code);
    setDescription(template.description ?? "");
    setCategorie(template.categorie);
    setEtablissement(template.etablissement ?? "");
    setFile(null);
    setError(null);
    setSuccess(null);
  }

  function newTemplate() {
    setSelectedId(null);
    setLibelle("");
    setCode("");
    setDescription("");
    setCategorie(CATEGORIES[0]);
    setEtablissement("");
    setFile(null);
    setError(null);
    setSuccess(null);
  }

  async function upload() {
    if (!file || !libelle.trim() || !code.trim()) {
      setError("Renseignez le libellé, le code et sélectionnez un fichier DOCX.");
      return;
    }

    const body = new FormData();
    if (selectedId) body.set("templateId", String(selectedId));
    body.set("code", code);
    body.set("libelle", libelle);
    body.set("description", description);
    body.set("categorie", categorie);
    body.set("etablissement", etablissement);
    body.set("file", file);

    setSaving(true);
    setError(null);
    setSuccess(null);
    try {
      const response = await api({ method: "POST", body });
      const data = await response.json().catch(() => null);
      if (!response.ok) throw new Error(messageFor(data?.message));
      setSuccess(selectedId ? "Une nouvelle version a été importée." : "Le modèle a été importé en brouillon.");
      await load();
      if (!selectedId && data?.item?.template?.id) setSelectedId(data.item.template.id);
      setFile(null);
    } catch (reason) {
      setError(reason instanceof Error ? reason.message : "Import impossible.");
    } finally {
      setSaving(false);
    }
  }

  async function importBatch() {
    if (!batchFile) {
      setError("Sélectionnez un fichier ZIP contenant le manifeste et les DOCX.");
      return;
    }
    const token = localStorage.getItem("token");
    const body = new FormData();
    body.set("file", batchFile);
    setBatchImporting(true);
    setBatchReport(null);
    setError(null);
    setSuccess(null);
    try {
      const response = await fetch(
        `${process.env.NEXT_PUBLIC_API_URL}/api/admin/parametres/modeles-documents/bulk`,
        {
          method: "POST",
          body,
          cache: "no-store",
          headers: { Authorization: `Bearer ${token}` },
        },
      );
      const data = await response.json().catch(() => null);
      if (!response.ok) throw new Error(messageFor(data?.message));
      const report = data as BatchImportReport;
      setBatchReport(report);
      setSuccess(
        `${report.summary.imported} modèle(s) importé(s) en brouillon, ` +
        `${report.summary.skipped} ignoré(s), ${report.summary.failed} en erreur.`,
      );
      setBatchFile(null);
      await load();
    } catch (reason) {
      setError(reason instanceof Error ? reason.message : "Import du lot impossible.");
    } finally {
      setBatchImporting(false);
    }
  }

  async function setActive(template: DocumentTemplate, actif: boolean) {
    const version = template.versions[0]?.numero;
    if (!version) return;
    const previewKey = `${template.id}:${version}`;
    if (actif && !previewed.has(previewKey)) {
      setError("Ouvrez et contrôlez d'abord l'aperçu de cette version.");
      return;
    }
    if (actif && !window.confirm("Confirmez-vous que l'aperçu est correct et que cette version peut être activée ?")) return;
    setSaving(true);
    setError(null);
    try {
      const response = await api({
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          templateId: template.id,
          version,
          actif,
          previewConfirmed: actif,
        }),
      });
      const data = await response.json().catch(() => null);
      if (!response.ok) throw new Error(messageFor(data?.message));
      setSuccess(actif ? "Le modèle est actif." : "Le modèle a été désactivé.");
      await load();
    } catch (reason) {
      setError(reason instanceof Error ? reason.message : "Modification impossible.");
    } finally {
      setSaving(false);
    }
  }

  async function preview(template: DocumentTemplate, version: number) {
    const token = localStorage.getItem("token");
    const previewWindow = window.open("", "_blank");
    setPreviewing(true);
    setError(null);
    try {
      const query = new URLSearchParams({
        templateId: String(template.id),
        version: String(version),
      });
      const response = await fetch(
        `${process.env.NEXT_PUBLIC_API_URL}/api/admin/parametres/modeles-documents/apercu?${query}`,
        { headers: { Authorization: `Bearer ${token}` }, cache: "no-store" },
      );
      if (!response.ok) {
        const data = await response.json().catch(() => null);
        throw new Error(messageFor(data?.message));
      }
      const url = URL.createObjectURL(await response.blob());
      if (previewWindow) previewWindow.location.href = url;
      else throw new Error("Le navigateur a bloqué l'ouverture de l'aperçu.");
      window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
      setPreviewed((current) => new Set(current).add(`${template.id}:${version}`));
      setSuccess("L'aperçu a été généré. Contrôlez-le avant activation.");
    } catch (reason) {
      previewWindow?.close();
      setError(reason instanceof Error ? reason.message : "Aperçu impossible.");
    } finally {
      setPreviewing(false);
    }
  }

  return (
    <section className="rounded-lg bg-white p-4 shadow">
      <div className="mb-4 flex flex-wrap items-start justify-between gap-3">
        <div>
          <h2 className="text-xl font-bold">Modèles de documents</h2>
          <p className="text-sm text-slate-500">
            Importez uniquement des DOCX sans macro. Chaque remplacement crée une nouvelle version.
          </p>
        </div>
        <button onClick={newTemplate} className="rounded bg-blue-600 px-4 py-2 font-semibold text-white">
          Nouveau modèle
        </button>
      </div>

      {error && <p className="mb-3 rounded bg-red-50 p-3 text-red-700">{error}</p>}
      {success && <p className="mb-3 rounded bg-green-50 p-3 text-green-700">{success}</p>}

      <div className="mb-4 rounded border border-blue-200 bg-blue-50 p-4">
        <div className="flex flex-wrap items-end gap-3">
          <label className="min-w-[280px] flex-1 text-sm font-semibold text-slate-800">
            Importer un lot ZIP
            <span className="mt-1 block text-xs font-normal text-slate-600">
              Le ZIP doit contenir un manifest.csv ou manifest.json et jusqu'à 100 DOCX.
            </span>
            <input
              type="file"
              accept=".zip,application/zip"
              disabled={batchImporting}
              onChange={(event) => {
                setBatchFile(event.target.files?.[0] ?? null);
                setBatchReport(null);
              }}
              className="mt-2 block w-full rounded border bg-white px-3 py-2 font-normal"
            />
          </label>
          <button
            disabled={!batchFile || batchImporting}
            onClick={() => void importBatch()}
            className="rounded bg-indigo-600 px-4 py-2 font-semibold text-white disabled:opacity-50"
          >
            {batchImporting ? "Import du lot…" : "Importer en brouillon"}
          </button>
        </div>
        {batchReport && (
          <div className="mt-4 overflow-x-auto rounded border bg-white">
            <table className="w-full text-left text-sm">
              <thead className="bg-slate-100 text-xs uppercase text-slate-600">
                <tr>
                  <th className="px-3 py-2">Modèle</th>
                  <th className="px-3 py-2">Code</th>
                  <th className="px-3 py-2">Résultat</th>
                  <th className="px-3 py-2">Détail</th>
                </tr>
              </thead>
              <tbody>
                {batchReport.results.map((item, index) => (
                  <tr key={`${item.code}:${index}`} className="border-t">
                    <td className="px-3 py-2">{item.libelle}</td>
                    <td className="px-3 py-2 font-mono text-xs">{item.code}</td>
                    <td className="px-3 py-2">
                      <span className={`rounded-full px-2 py-1 text-xs font-semibold ${
                        item.status === "imported"
                          ? "bg-green-100 text-green-700"
                          : item.status === "skipped"
                            ? "bg-amber-100 text-amber-800"
                            : "bg-red-100 text-red-700"
                      }`}>
                        {item.status === "imported" ? "Importé" : item.status === "skipped" ? "Ignoré" : "Erreur"}
                      </span>
                    </td>
                    <td className="px-3 py-2 text-slate-600">
                      {item.message ? messageFor(item.message) : "Créé en brouillon"}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </div>

      <div className="grid gap-5 xl:grid-cols-[360px_minmax(0,1fr)]">
        <aside className="max-h-[72vh] overflow-y-auto rounded border">
          {loading ? (
            <p className="p-4 text-sm text-slate-500">Chargement…</p>
          ) : templates.length === 0 ? (
            <p className="p-4 text-sm text-slate-500">Aucun modèle importé.</p>
          ) : grouped.map((group) => (
            <div key={group.name}>
              <h3 className="sticky top-0 bg-slate-200 px-3 py-2 text-xs font-bold uppercase">{group.name}</h3>
              {group.items.map((template) => (
                <button
                  key={template.id}
                  onClick={() => selectTemplate(template)}
                  className={`flex w-full items-center justify-between gap-3 border-b px-3 py-3 text-left text-sm ${selectedId === template.id ? "bg-blue-600 text-white" : "hover:bg-slate-50"}`}
                >
                  <span>
                    <strong className="block">{template.libelle}</strong>
                    <span className="text-xs opacity-75">{template.versions.length} version(s)</span>
                  </span>
                  <span className={`rounded-full px-2 py-1 text-xs ${template.actif ? "bg-green-100 text-green-700" : "bg-amber-100 text-amber-800"}`}>
                    {template.actif ? "Actif" : "Brouillon"}
                  </span>
                </button>
              ))}
            </div>
          ))}
        </aside>

        <div className="space-y-4">
          <div className="grid gap-3 md:grid-cols-2">
            <label className="text-sm font-semibold">Libellé
              <input value={libelle} onChange={(event) => { setLibelle(event.target.value); if (!selectedId) setCode(slug(event.target.value)); }} className="mt-1 w-full rounded border px-3 py-2 font-normal" />
            </label>
            <label className="text-sm font-semibold">Code technique
              <input value={code} disabled={selectedId !== null} onChange={(event) => setCode(slug(event.target.value))} className="mt-1 w-full rounded border px-3 py-2 font-mono font-normal disabled:bg-slate-100" />
            </label>
            <label className="text-sm font-semibold">Catégorie
              <select value={categorie} onChange={(event) => setCategorie(event.target.value)} className="mt-1 w-full rounded border px-3 py-2 font-normal">
                {availableCategories.map((item) => <option key={item}>{item}</option>)}
              </select>
            </label>
            <label className="text-sm font-semibold">Établissement éventuel
              <input value={etablissement} onChange={(event) => setEtablissement(event.target.value)} className="mt-1 w-full rounded border px-3 py-2 font-normal" placeholder="Tous les établissements" />
            </label>
          </div>
          <label className="block text-sm font-semibold">Description
            <textarea value={description} onChange={(event) => setDescription(event.target.value)} rows={3} className="mt-1 w-full rounded border px-3 py-2 font-normal" />
          </label>
          <label className="block rounded border-2 border-dashed border-slate-300 p-5 text-sm">
            <strong>{selected ? "Importer une nouvelle version DOCX" : "Fichier DOCX"}</strong>
            <input type="file" accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document" onChange={(event) => setFile(event.target.files?.[0] ?? null)} className="mt-3 block w-full" />
          </label>
          <div className="flex flex-wrap gap-3">
            <button disabled={saving || !file} onClick={() => void upload()} className="rounded bg-green-600 px-4 py-2 font-semibold text-white disabled:opacity-50">
              {saving ? "Traitement…" : selected ? "Créer la nouvelle version" : "Importer en brouillon"}
            </button>
            {selected && selected.versions.length > 0 && (
              <>
                <button disabled={previewing} onClick={() => void preview(selected, selected.versions[0].numero)} className="rounded bg-slate-700 px-4 py-2 font-semibold text-white disabled:opacity-50">
                  {previewing ? "Génération…" : "Ouvrir l'aperçu de la dernière version"}
                </button>
                <button disabled={saving || (!selected.actif && !previewed.has(`${selected.id}:${selected.versions[0].numero}`))} onClick={() => void setActive(selected, !selected.actif)} className={`rounded px-4 py-2 font-semibold text-white disabled:opacity-50 ${selected.actif ? "bg-amber-600" : "bg-blue-600"}`}>
                  {selected.actif ? "Désactiver" : "Activer la dernière version"}
                </button>
              </>
            )}
          </div>

          {selected?.versions.map((version) => (
            <article key={version.id} className="rounded border p-4">
              <div className="flex flex-wrap items-center justify-between gap-2">
                <strong>Version {version.numero} — {version.nom_fichier_original}</strong>
                <span className="rounded bg-slate-100 px-2 py-1 text-xs">{version.statut}</span>
              </div>
              <p className="mt-1 text-xs text-slate-500">{formatBytes(version.taille_octets)} · {version.champs.length} champ(s) détecté(s)</p>
              <div className="mt-3 flex flex-wrap gap-2">
                {version.champs.map((field) => (
                  <span key={field.key} className="rounded bg-blue-50 px-2 py-1 font-mono text-xs text-blue-800">{`{${field.key}}`}</span>
                ))}
              </div>
            </article>
          ))}
        </div>
      </div>

      <div className="mt-6 border-t pt-4">
        <h3 className="text-lg font-bold">Signataires</h3>
        <p className="mb-3 text-sm text-slate-500">
          Personnes proposées comme signataire lors de la génération d'un document.
        </p>

        {signatairesError && (
          <p className="mb-3 rounded bg-red-50 p-3 text-red-700">{signatairesError}</p>
        )}

        <div className="mb-3 flex flex-wrap items-end gap-2">
          <label className="text-sm font-semibold">Nom
            <input value={newSignataire.prenomnom} onChange={(event) => setNewSignataire((current) => ({ ...current, prenomnom: event.target.value }))} className="mt-1 block rounded border px-3 py-2 font-normal" />
          </label>
          <label className="text-sm font-semibold">Fonction
            <input value={newSignataire.fonction} onChange={(event) => setNewSignataire((current) => ({ ...current, fonction: event.target.value }))} className="mt-1 block rounded border px-3 py-2 font-normal" />
          </label>
          <button onClick={() => void addSignataire()} className="rounded bg-green-600 px-4 py-2 font-semibold text-white">
            Ajouter
          </button>
        </div>

        <div className="overflow-x-auto rounded border">
          <table className="w-full border-collapse text-sm">
            <thead>
              <tr className="bg-sky-100 text-left">
                <th className="border p-2">Nom</th>
                <th className="border p-2">Fonction</th>
                <th className="border p-2">Statut</th>
                <th className="border p-2">Action</th>
              </tr>
            </thead>
            <tbody>
              {signatairesLoading ? (
                <tr><td colSpan={4} className="p-4 text-center text-slate-500">Chargement…</td></tr>
              ) : signataires.length === 0 ? (
                <tr><td colSpan={4} className="p-4 text-center text-slate-500">Aucun signataire.</td></tr>
              ) : signataires.map((signataire) => (
                <tr key={signataire.id} className={signataire.actif ? "even:bg-slate-50" : "bg-slate-100 text-slate-400"}>
                  <td className="border p-2">{signataire.prenomnom}</td>
                  <td className="border p-2">{signataire.fonction}</td>
                  <td className="border p-2">
                    <span className={`rounded-full px-2 py-1 text-xs ${signataire.actif ? "bg-green-100 text-green-700" : "bg-amber-100 text-amber-800"}`}>
                      {signataire.actif ? "Actif" : "Inactif"}
                    </span>
                  </td>
                  <td className="border p-2">
                    <button onClick={() => void toggleSignataire(signataire)} className="rounded bg-slate-700 px-2 py-1 font-semibold text-white">
                      {signataire.actif ? "Désactiver" : "Activer"}
                    </button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </section>
  );
}
