"use client";

/* eslint-disable @next/next/no-img-element */

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ENRGETIC_FORMS } from "@/lib/documents/enrgetic-forms";
import {
  DAACT_PRESENTATION,
  getClientChecklist,
  type ClientChecklistItem,
  type DaactCode,
} from "@/lib/client-dossier";

type UploadRecord = {
  checklistItemId: string;
  fileName: string;
  uploadedAt: string;
  status: string;
};

type ClientDossier = {
  reference: string;
  firstName: string;
  lastName: string;
  city: string;
  projectType: string;
  projectDetail: string;
  createdSurface: number;
  daactCode: DaactCode;
  status: string;
  piecesReceived: number;
  piecesExpected: number;
  lastUpdate: string | null;
  uploads: UploadRecord[];
};

type ItemStatus = "accepted" | "review" | "missing" | "optional";

type DisplayItem = ClientChecklistItem & {
  status: ItemStatus;
  statusLabel: string;
  uploadCount: number;
};

function UiIcon({ name }: { name: string }) {
  const common = {
    width: 24,
    height: 24,
    viewBox: "0 0 24 24",
    fill: "none",
    stroke: "currentColor",
    strokeWidth: 1.8,
    strokeLinecap: "round" as const,
    strokeLinejoin: "round" as const,
    "aria-hidden": true,
  };

  if (name === "camera") return <svg {...common}><path d="M4 7.5h3l1.4-2h7.2l1.4 2h3v11H4z" /><circle cx="12" cy="13" r="3.4" /></svg>;
  if (name === "study") return <svg {...common}><path d="M5 3.5h11a2 2 0 0 1 2 2v15H7a2 2 0 0 1-2-2z" /><path d="M8 8h7M8 12h7M8 16h4" /></svg>;
  if (name === "upload") return <svg {...common}><path d="M12 16V4M7.5 8.5 12 4l4.5 4.5M5 14v5h14v-5" /></svg>;
  if (name === "download") return <svg {...common}><path d="M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19h14" /></svg>;
  if (name === "shield") return <svg {...common}><path d="M12 3 5.5 5.7v5.8c0 4.1 2.5 7.4 6.5 9.5 4-2.1 6.5-5.4 6.5-9.5V5.7z" /><path d="m9 12 2 2 4-4" /></svg>;
  if (name === "clock") return <svg {...common}><circle cx="12" cy="12" r="8.5" /><path d="M12 7.5V12l3 2" /></svg>;
  if (name === "location") return <svg {...common}><path d="M12 21s6-5.3 6-11a6 6 0 1 0-12 0c0 5.7 6 11 6 11Z" /><circle cx="12" cy="10" r="2" /></svg>;
  if (name === "user") return <svg {...common}><circle cx="12" cy="8" r="3.5" /><path d="M5.5 20c.5-4 2.8-6 6.5-6s6 2 6.5 6" /></svg>;
  return <svg {...common}><path d="M6 3.5h8l4 4V20H6z" /><path d="M14 3.5V8h4M9 12h6M9 16h6" /></svg>;
}

function fileToBase64(file: File) {
  return new Promise<string>((resolve, reject) => {
    const reader = new FileReader();
    reader.onerror = () => reject(new Error(`Impossible de lire ${file.name}.`));
    reader.onload = () => resolve(String(reader.result || "").split(",")[1] || "");
    reader.readAsDataURL(file);
  });
}

function formatDate(value: string | null) {
  if (!value) return "Aucun dépôt pour le moment";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return new Intl.DateTimeFormat("fr-FR", { dateStyle: "medium", timeStyle: "short" }).format(date);
}

async function loadClientDossier(token: string) {
  const response = await fetch(`/api/dossiers/${token}`, { cache: "no-store" });
  const payload = await response.json();
  if (response.status === 401 && payload.verificationRequired) throw new AccessRequiredError();
  if (!response.ok || !payload.ok) throw new Error(payload.error || "Dossier inaccessible.");
  return payload.data as ClientDossier;
}

class AccessRequiredError extends Error {
  constructor() {
    super("Vérification requise.");
    this.name = "AccessRequiredError";
  }
}

function PortalState({ title, message }: { title: string; message: string }) {
  return (
    <main className="site-shell">
      <header className="topbar"><span className="brand"><img src="/brand/enrgetic-logo.png" alt="ENRGETIC" /></span></header>
      <div className="portal-state page">
        <span className="modal-icon"><UiIcon name="shield" /></span>
        <p className="eyebrow">Espace client sécurisé</p>
        <h1>{title}</h1>
        <p>{message}</p>
        <a href="mailto:admin@enrgetic.fr">Nous contacter</a>
      </div>
    </main>
  );
}

function AccessGate({
  code,
  maskedEmail,
  pending,
  error,
  onCodeChange,
  onRequest,
  onVerify,
}: {
  code: string;
  maskedEmail: string;
  pending: boolean;
  error: string | null;
  onCodeChange: (code: string) => void;
  onRequest: () => void;
  onVerify: () => void;
}) {
  const codeSent = Boolean(maskedEmail);
  return (
    <main className="site-shell">
      <header className="topbar"><span className="brand"><img src="/brand/enrgetic-logo.png" alt="ENRGETIC" /></span></header>
      <div className="access-page page">
        <section className="access-card" aria-labelledby="access-title">
          <span className="modal-icon"><UiIcon name="shield" /></span>
          <p className="eyebrow">Espace client sécurisé</p>
          <h1 id="access-title">Vérifions votre accès</h1>
          {!codeSent ? (
            <>
              <p>Pour protéger les documents de votre chantier, nous allons envoyer un code à l’adresse e-mail enregistrée pour ce dossier.</p>
              <button className="primary-button" type="button" onClick={onRequest} disabled={pending}>
                {pending ? "Envoi en cours…" : "Recevoir mon code"}
              </button>
            </>
          ) : (
            <>
              <p>Un code à 6 chiffres a été envoyé à <strong>{maskedEmail}</strong>. Il est valable pendant 10 minutes.</p>
              <label className="access-code-label" htmlFor="access-code">Code d’accès</label>
              <input
                id="access-code"
                className="access-code-input"
                type="text"
                inputMode="numeric"
                autoComplete="one-time-code"
                maxLength={6}
                value={code}
                onChange={(event) => onCodeChange(event.target.value.replace(/\D/g, "").slice(0, 6))}
                onKeyDown={(event) => { if (event.key === "Enter" && code.length === 6 && !pending) onVerify(); }}
                aria-describedby={error ? "access-error" : undefined}
                autoFocus
              />
              {error && <p className="access-error" id="access-error" role="alert">{error}</p>}
              <div className="access-actions">
                <button className="primary-button compact" type="button" onClick={onVerify} disabled={pending || code.length !== 6}>
                  {pending ? "Vérification…" : "Ouvrir mon dossier"}
                </button>
                <button className="access-resend" type="button" onClick={onRequest} disabled={pending}>Renvoyer un code</button>
              </div>
            </>
          )}
          {!codeSent && error && <p className="access-error" role="alert">{error}</p>}
          <small className="access-note">Cet appareil restera autorisé pendant 30 jours. Vous pourrez révoquer son accès en demandant un nouveau lien à ENRGETIC.</small>
        </section>
        <aside className="support-card"><div><span className="support-mark">i</span><p><strong>Besoin d’aide ?</strong><br />Notre équipe reste à votre disposition.</p></div><a href="mailto:admin@enrgetic.fr">Nous contacter</a></aside>
      </div>
    </main>
  );
}

export default function ClientPortal({ token }: { token: string }) {
  const [dossier, setDossier] = useState<ClientDossier | null>(null);
  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState<string | null>(null);
  const [accessRequired, setAccessRequired] = useState(false);
  const [accessCode, setAccessCode] = useState("");
  const [maskedEmail, setMaskedEmail] = useState("");
  const [accessPending, setAccessPending] = useState(false);
  const [accessError, setAccessError] = useState<string | null>(null);
  const [uploadOpen, setUploadOpen] = useState(false);
  const [selectedItemId, setSelectedItemId] = useState("");
  const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
  const [uploadPending, setUploadPending] = useState(false);
  const [uploadProgress, setUploadProgress] = useState("");
  const [uploadError, setUploadError] = useState<string | null>(null);
  const [uploadSuccess, setUploadSuccess] = useState<string | null>(null);
  const [filter, setFilter] = useState<"all" | "missing" | "review" | "accepted">("all");
  const inputRef = useRef<HTMLInputElement>(null);

  const refresh = useCallback(async () => {
    try {
      setDossier(await loadClientDossier(token));
      setAccessRequired(false);
      setLoadError(null);
    } catch (error) {
      if (error instanceof AccessRequiredError) {
        setAccessRequired(true);
        setLoadError(null);
      } else {
        setLoadError(error instanceof Error ? error.message : "Dossier inaccessible.");
      }
    } finally {
      setLoading(false);
    }
  }, [token]);

  useEffect(() => {
    let cancelled = false;
    loadClientDossier(token)
      .then((data) => {
        if (!cancelled) {
          setDossier(data);
          setLoadError(null);
        }
      })
      .catch((error) => {
        if (!cancelled) {
          if (error instanceof AccessRequiredError) {
            setAccessRequired(true);
            setLoadError(null);
          } else {
            setLoadError(error instanceof Error ? error.message : "Dossier inaccessible.");
          }
        }
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => { cancelled = true; };
  }, [token]);

  const requestAccessCode = async () => {
    setAccessPending(true);
    setAccessError(null);
    try {
      const response = await fetch(`/api/dossiers/${token}/access/request`, { method: "POST" });
      const payload = await response.json();
      if (!response.ok || !payload.ok) throw new Error(payload.error || "Envoi du code impossible.");
      setMaskedEmail(payload.data.maskedEmail || "adresse enregistrée");
      setAccessCode("");
    } catch (error) {
      setAccessError(error instanceof Error ? error.message : "Envoi du code impossible.");
    } finally {
      setAccessPending(false);
    }
  };

  const verifyAccessCode = async () => {
    setAccessPending(true);
    setAccessError(null);
    try {
      const response = await fetch(`/api/dossiers/${token}/access/verify`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ code: accessCode }),
      });
      const payload = await response.json();
      if (!response.ok || !payload.ok) throw new Error(payload.error || "Code incorrect.");
      setLoading(true);
      await refresh();
      setMaskedEmail("");
      setAccessCode("");
    } catch (error) {
      setAccessError(error instanceof Error ? error.message : "Vérification impossible.");
    } finally {
      setAccessPending(false);
    }
  };

  const presentation = dossier ? DAACT_PRESENTATION[dossier.daactCode] : null;
  const sourceItems = useMemo(() => dossier ? getClientChecklist(dossier.daactCode) : [], [dossier]);

  const items = useMemo<DisplayItem[]>(() => {
    if (!dossier) return [];
    return sourceItems.map((item) => {
      const uploads = dossier.uploads.filter((upload) => upload.checklistItemId === item.id);
      const accepted = uploads.some((upload) => /accept|valid/i.test(upload.status));
      if (accepted) return { ...item, status: "accepted", statusLabel: "Accepté", uploadCount: uploads.length };
      if (uploads.length) return { ...item, status: "review", statusLabel: uploads.length > 1 ? `${uploads.length} fichiers reçus` : "En vérification", uploadCount: uploads.length };
      if (/facultatif/i.test(item.requirement)) return { ...item, status: "optional", statusLabel: "Facultatif", uploadCount: 0 };
      return { ...item, status: "missing", statusLabel: "À déposer", uploadCount: 0 };
    });
  }, [dossier, sourceItems]);

  const receivedCount = items.filter((item) => item.status === "accepted" || item.status === "review").length;
  const percent = items.length ? Math.round((receivedCount / items.length) * 100) : 0;
  const remaining = items.filter((item) => item.status === "missing").length;

  const filteredItems = useMemo(() => {
    if (filter === "all") return items;
    return items.filter((item) => item.status === filter);
  }, [filter, items]);

  const categories = useMemo(() => ([
    { id: "administratif", icon: "document", title: "Documents administratifs" },
    { id: "technique", icon: "study", title: "Justificatifs techniques" },
    { id: "photos", icon: "camera", title: "Photos du chantier" },
  ] as const).map((category) => {
    const categoryItems = items.filter((item) => item.category === category.id);
    const received = categoryItems.filter((item) => item.status === "accepted" || item.status === "review").length;
    return { ...category, received, total: categoryItems.length, percent: categoryItems.length ? Math.round(received / categoryItems.length * 100) : 0 };
  }), [items]);

  const selectedItem = items.find((item) => item.id === selectedItemId);

  const openUpload = (itemId = "") => {
    setSelectedItemId(itemId);
    setSelectedFiles([]);
    setUploadError(null);
    setUploadSuccess(null);
    setUploadProgress("");
    setUploadOpen(true);
  };

  const closeUpload = () => {
    if (uploadPending) return;
    setUploadOpen(false);
  };

  const handleUpload = async () => {
    if (!selectedItem || !selectedFiles.length) return;
    setUploadPending(true);
    setUploadError(null);

    try {
      const allowedExtensions = new Set(["pdf", "jpg", "jpeg", "png", "heic", "heif"]);
      const prepared: Array<{
        fileName: string;
        mimeType: string;
        base64: string;
        checklistItemId: string;
        checklistLabel: string;
      }> = [];

      for (let index = 0; index < selectedFiles.length; index += 1) {
        const file = selectedFiles[index];
        if (file.size > 25 * 1024 * 1024) throw new Error(`${file.name} dépasse 25 Mo.`);
        const extension = file.name.toLowerCase().split(".").pop() || "";
        if (!allowedExtensions.has(extension)) throw new Error(`${file.name} n’est pas un PDF ou une image autorisée.`);
        setUploadProgress(`Préparation ${index + 1} sur ${selectedFiles.length}…`);
        const base64 = await fileToBase64(file);
        prepared.push({
          fileName: file.name,
          mimeType: file.type || "application/octet-stream",
          base64,
          checklistItemId: selectedItem.id,
          checklistLabel: selectedItem.label,
        });
      }

      const batches: typeof prepared[] = [];
      let currentBatch: typeof prepared = [];
      let currentLength = 0;
      for (const file of prepared) {
        if (currentBatch.length && (currentBatch.length >= 10 || currentLength + file.base64.length > 32_000_000)) {
          batches.push(currentBatch);
          currentBatch = [];
          currentLength = 0;
        }
        currentBatch.push(file);
        currentLength += file.base64.length;
      }
      if (currentBatch.length) batches.push(currentBatch);

      for (let index = 0; index < batches.length; index += 1) {
        const batch = batches[index];
        setUploadProgress(batches.length > 1 ? `Envoi du lot ${index + 1} sur ${batches.length}…` : "Envoi sécurisé en cours…");
        const response = await fetch(`/api/dossiers/${token}/uploads`, {
          method: "POST",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ files: batch }),
        });
        const payload = await response.json();
        if (!response.ok || !payload.ok) throw new Error(payload.error || "Échec du dépôt.");
      }
      setUploadSuccess(`${selectedFiles.length} fichier${selectedFiles.length > 1 ? "s" : ""} transmis avec succès en ${batches.length > 1 ? `${batches.length} lots` : "un seul lot"}.`);
      setUploadProgress("");
      await refresh();
      setSelectedFiles([]);
    } catch (error) {
      setUploadError(error instanceof Error ? error.message : "Dépôt impossible.");
    } finally {
      setUploadPending(false);
    }
  };

  if (loading) return <PortalState title="Ouverture de votre dossier…" message="Nous chargeons votre checklist personnalisée." />;
  if (accessRequired) return <AccessGate code={accessCode} maskedEmail={maskedEmail} pending={accessPending} error={accessError} onCodeChange={setAccessCode} onRequest={() => void requestAccessCode()} onVerify={() => void verifyAccessCode()} />;
  if (loadError || !dossier || !presentation) return <PortalState title="Lien indisponible" message={loadError || "Ce dossier n’a pas pu être ouvert."} />;

  const forms = ENRGETIC_FORMS[presentation.formsKey];
  const identity = `${dossier.firstName} ${dossier.lastName}`.trim();
  const projectLabel = dossier.projectDetail || dossier.projectType;

  return (
    <main className="site-shell">
      <header className="topbar">
        <a className="brand" href="#top" aria-label="Accueil ENRGETIC"><img src="/brand/enrgetic-logo.png" alt="ENRGETIC" /></a>
        <div className="topbar-actions"><div className="identity-button"><span className="identity-icon"><UiIcon name="user" /></span><span><strong>{identity}</strong><small>Mon espace</small></span></div></div>
      </header>

      <div className="page" id="top">
        <nav className="breadcrumb" aria-label="Fil d'Ariane"><span>Mon dossier</span><span>›</span><strong>{presentation.title}</strong></nav>

        <section className="intro">
          <div><p className="eyebrow">Espace client sécurisé</p><h1>Suivi de votre dossier</h1><div className="project-meta"><span><UiIcon name="user" /> {identity}</span><i /><span><UiIcon name="location" /> {dossier.city}</span><i /><span>{projectLabel}{dossier.createdSurface ? ` · ${dossier.createdSurface} m²` : ""}</span></div></div>
          <div className="last-update"><UiIcon name="clock" /><span>Dernière mise à jour<br /><strong>{formatDate(dossier.lastUpdate)}</strong></span></div>
        </section>

        <section className="dossier-switcher">
          <div className="dossier-current static"><img src={presentation.icon} alt="" /><span><small>Dossier suivi</small><strong>{presentation.title}</strong><em>{dossier.reference}</em></span><b>{dossier.status || "En cours"}</b></div>
        </section>

        <section className="progress-panel">
          <div className="progress-copy"><div className="progress-number"><strong>{receivedCount}</strong><span>/ {items.length}<small>éléments suivis</small></span></div><div className="progress-main"><div className="progress-heading"><strong>Votre dossier est complété à {percent} %</strong><span>{remaining} élément{remaining > 1 ? "s" : ""} à déposer</span></div><div className="progress-track"><span style={{ width: `${percent}%` }} /></div><p>Chaque dépôt est immédiatement enregistré puis vérifié par ENRGETIC.</p></div></div>
          <button className="primary-button" type="button" onClick={() => openUpload()}><UiIcon name="upload" />Déposer des documents</button>
        </section>

        {uploadSuccess && <div className="portal-notice success" role="status"><span>✓</span><p>{uploadSuccess}</p><button type="button" onClick={() => setUploadSuccess(null)} aria-label="Fermer">×</button></div>}

        <section className="category-grid" aria-label="Catégories de pièces">
          {categories.map((category) => <article className="category-card" key={category.id}><div className="category-top"><span className="category-icon"><UiIcon name={category.icon} /></span><h2>{category.title}</h2></div><div className="category-count"><strong>{category.received}/{category.total}</strong><span>éléments reçus</span></div><div className="mini-track"><span style={{ width: `${category.percent}%` }} /></div><button type="button" onClick={() => setFilter("missing")}><span>Voir les pièces à déposer</span><b>›</b></button></article>)}
        </section>

        <section className="checklist-section">
          <div className="section-heading"><div><p className="eyebrow">Checklist personnalisée</p><h2>Les pièces de votre dossier</h2></div><div className="filter-tabs" role="tablist" aria-label="Filtrer la checklist">{([['all','Toutes'],['missing','À déposer'],['review','En vérification'],['accepted','Acceptées']] as const).map(([key,label]) => <button key={key} type="button" role="tab" aria-selected={filter === key} onClick={() => setFilter(key)}>{label}</button>)}</div></div>
          <div className="checklist-list">
            {filteredItems.map((item) => <article className="checklist-row" key={item.id}><span className={`status-dot ${item.status}`} aria-hidden="true">{item.status === "accepted" ? "✓" : item.status === "review" ? "…" : item.status === "optional" ? "i" : "!"}</span><div><h3>{item.label}</h3><p>{item.minimum && item.minimum !== "0" ? `Minimum : ${item.minimum}. ` : ""}{item.condition && item.condition !== "Toujours" ? item.condition : item.notes || "Document à rattacher à ce chantier."}</p></div><span className={`status-pill ${item.status}`}>{item.statusLabel}</span>{item.status !== "accepted" && <button className="row-action" type="button" onClick={() => openUpload(item.id)}>Déposer</button>}</article>)}
            {!filteredItems.length && <div className="empty-checklist"><strong>Aucun élément dans ce filtre.</strong><span>Vous pouvez consulter la checklist complète.</span></div>}
          </div>
        </section>

        <section className="models-section" id="modeles-utiles"><div className="models-heading"><div><p className="eyebrow">Documents à votre disposition</p><h2>Modèles utiles à télécharger</h2><p>Complétez le modèle correspondant, faites-le signer si nécessaire, puis redéposez-le dans votre dossier.</p></div><span className="models-secure"><UiIcon name="shield" /> Modèles ENRGETIC</span></div><div className={`models-grid ${forms.length === 1 ? "single" : ""}`}>{forms.map((model) => <article className="model-card" key={model.fileName}><span className={`model-code ${model.code === "DI" ? "discharge" : ""}`}>{model.code}</span><div><h3>{model.title}</h3><p>{model.description}</p><small>Document PDF à compléter</small></div><a href={model.downloadUrl} target="_blank" rel="noreferrer" aria-label={`Télécharger ${model.code} ${model.title}`}><UiIcon name="download" /><span>Télécharger</span></a></article>)}</div></section>

        <aside className="support-card"><div><span className="support-mark">i</span><p><strong>Une question sur une pièce ?</strong><br />Notre équipe reste à votre disposition pendant toute la durée du chantier.</p></div><a href="mailto:admin@enrgetic.fr">Nous contacter</a></aside>
        <footer><span>© 2026 ENRGETIC</span><span>Espace sécurisé · Données confidentielles</span></footer>
      </div>

      {uploadOpen && <div className="modal-backdrop" role="presentation" onMouseDown={closeUpload}><section className="upload-modal" role="dialog" aria-modal="true" aria-labelledby="upload-title" onMouseDown={(event) => event.stopPropagation()}><button className="modal-close" type="button" onClick={closeUpload} aria-label="Fermer" disabled={uploadPending}>×</button><span className="modal-icon"><UiIcon name="upload" /></span><p className="eyebrow">Nouveau dépôt</p><h2 id="upload-title">Ajouter des documents</h2><p>Choisissez d’abord la pièce concernée. Les fichiers seront conservés dans les dépôts originaux de <strong>{dossier.reference}</strong>.</p><label className="upload-select">Pièce concernée<select value={selectedItemId} onChange={(event) => setSelectedItemId(event.target.value)} disabled={uploadPending}><option value="">Sélectionner une pièce…</option>{items.map((item) => <option value={item.id} key={item.id}>{item.label}</option>)}</select></label><button className="drop-zone" type="button" onClick={() => inputRef.current?.click()} disabled={uploadPending}><UiIcon name="upload" /><strong>{selectedFiles.length ? `${selectedFiles.length} fichier${selectedFiles.length > 1 ? "s" : ""} sélectionné${selectedFiles.length > 1 ? "s" : ""}` : "Choisir un ou plusieurs fichiers"}</strong><span>PDF, JPG, PNG ou HEIC · 25 Mo maximum par fichier</span></button><input ref={inputRef} className="sr-only" type="file" multiple accept=".pdf,.jpg,.jpeg,.png,.heic,.heif,application/pdf,image/jpeg,image/png,image/heic,image/heif" onChange={(event) => setSelectedFiles(Array.from(event.target.files || []))} />{selectedFiles.length > 0 && <ul className="selected-files">{selectedFiles.map((file) => <li key={`${file.name}-${file.size}`}><span>{file.name}</span><small>{(file.size / 1024 / 1024).toFixed(1)} Mo</small></li>)}</ul>}{uploadProgress && <p className="upload-feedback">{uploadProgress}</p>}{uploadError && <p className="upload-feedback error">{uploadError}</p>}{uploadSuccess && <p className="upload-feedback success">{uploadSuccess}</p>}<div className="modal-actions"><button type="button" className="secondary-button" onClick={closeUpload} disabled={uploadPending}>{uploadSuccess ? "Fermer" : "Annuler"}</button><button type="button" className="primary-button compact" disabled={!selectedItemId || !selectedFiles.length || uploadPending} onClick={() => void handleUpload()}>{uploadPending ? "Envoi en cours…" : "Confirmer le dépôt"}</button></div></section></div>}
    </main>
  );
}
