/* global React, Icon, Spreadsheet */
// ============================================================================
// AUDIT SYSTÈME S&E — SAT REFT Africa methodology (8 domains × 100 norms)
// ----------------------------------------------------------------------------
// One evaluation per (project, cycle). Each evaluation is scored against the
// 100 reference norms (seeded from the REFT Africa template). Standard
// rating: N/A / 0 / 1 / 2  (Ne répond pas / Partiellement / Pleinement).
// Domain E has a heavier 0/5/10/N/A scale (data verification weight).
// ============================================================================
const { useState: useStateAU, useMemo: useMemoAU, useEffect: useEffectAU } = React;

// -----------------------------------------------------------------------------
// Scoring rules
// -----------------------------------------------------------------------------
const STD_RATINGS = [
  { v: "na",       fr: "N/A",                       en: "N/A",                       value: null },
  { v: "none",     fr: "Ne répond pas du tout",     en: "Does not meet",             value: 0 },
  { v: "partial",  fr: "Répond partiellement",      en: "Partially meets",           value: 1 },
  { v: "full",     fr: "Répond pleinement",         en: "Fully meets",               value: 2 },
];
const E_RATINGS = [
  { v: "na",       fr: "N/A",                                          en: "N/A",                                  value: null },
  { v: "above_10", fr: "Plus de 10% des données rapportées",            en: "More than 10% of reported data",       value: 0 },
  { v: "5_to_10",  fr: "Entre 5 et 10% des données rapportées",         en: "Between 5 and 10% of reported data",   value: 5 },
  { v: "under_5",  fr: "Moins de 5% des données rapportées",            en: "Less than 5% of reported data",        value: 10 },
];
function ratingsFor(domain) { return domain === "E" ? E_RATINGS : STD_RATINGS; }
function ratingLabel(domain, code, lang) {
  const rs = ratingsFor(domain).find((r) => r.v === code);
  return rs ? (lang === "fr" ? rs.fr : rs.en) : "—";
}
function ratingValue(domain, code) {
  const rs = ratingsFor(domain).find((r) => r.v === code);
  return rs ? rs.value : null;
}

// Compute per-domain + total scores from raw responses array.
function computeScores(norms, responses) {
  // Index responses by norm_id for O(1) lookup
  const respByNorm = new Map();
  (responses || []).forEach((r) => { respByNorm.set(r.norm_id, r); });
  const perDomain = {};
  (norms || []).forEach((n) => {
    const r = respByNorm.get(n.id);
    const d = n.domain_code;
    if (!perDomain[d]) perDomain[d] = { total: 0, max: 0, answered: 0, count: 0, normsTotal: 0 };
    perDomain[d].normsTotal++;
    if (r && r.rating && r.rating !== "na" && r.value != null) {
      perDomain[d].total += Number(r.value) || 0;
      perDomain[d].max   += Number(n.max_value) || 0;
      perDomain[d].answered++;
    } else if (r && r.rating === "na") {
      // counted as answered but excluded from max
      perDomain[d].answered++;
    }
    perDomain[d].count = perDomain[d].normsTotal;
  });
  // Totals
  let total = 0, max = 0, answered = 0, count = 0;
  Object.values(perDomain).forEach((d) => {
    total += d.total; max += d.max; answered += d.answered; count += d.count;
  });
  Object.keys(perDomain).forEach((k) => {
    perDomain[k].pct = perDomain[k].max > 0 ? perDomain[k].total / perDomain[k].max : 0;
  });
  return {
    perDomain,
    total: { total, max, pct: max > 0 ? total / max : 0, answered, count },
  };
}

// -----------------------------------------------------------------------------
// Main screen
// -----------------------------------------------------------------------------
function AuditSystem({ t, lang, isSuperAdmin, actingOrgId, activeOrgId, myOrgId }) {
  const { projects: allProjects } = window.melr.useProjects();
  const { domains, norms, loading: catLoading } = window.melr.useSatCatalog();
  const LiveBadge = window.melr.LiveBadge;
  // Effective org for filtering: super-admin acting > active multi-org choice
  // > home org. Project list is narrowed so the dropdown shows only what
  // belongs to the user's current context, and the auto-pick is predictable.
  const effOrg = (isSuperAdmin && actingOrgId) ? actingOrgId : (activeOrgId || myOrgId);
  const projects = (effOrg && allProjects)
    ? allProjects.filter((p) => p.organizationId === effOrg)
    : (allProjects || []);

  // Persist the user's project choice so they return to the same project
  // across reloads / screens, instead of always landing on the auto-pick.
  const [projectId, setProjectIdRaw] = useStateAU(() => {
    try { return localStorage.getItem("melr.audit-system.projectId") || ""; }
    catch (_) { return ""; }
  });
  const setProjectId = (v) => {
    setProjectIdRaw(v || "");
    try {
      if (v) localStorage.setItem("melr.audit-system.projectId", v);
      else   localStorage.removeItem("melr.audit-system.projectId");
    } catch (_) {}
  };
  const { data: evaluations, refresh: refreshEvals } = window.melr.useSatEvaluations(projectId);
  const [evaluationId, setEvaluationId] = useStateAU("");
  const [tab, setTab] = useStateAU("synthese");
  const [busy, setBusy] = useStateAU(false);

  // Auto-pick: prefer the persisted projectId if it's still in the filtered
  // list; otherwise fall back to the first available project. When the
  // effective org changes (e.g. user switches the active-org pill), the
  // persisted id may no longer be valid and we re-pick.
  useEffectAU(() => {
    if (!projects || projects.length === 0) return;
    const stillValid = projectId && projects.some((p) => p.uuid === projectId);
    if (!stillValid) setProjectId(projects[0].uuid);
  }, [projects]);
  useEffectAU(() => {
    if ((!evaluationId || !evaluations.find((e) => e.id === evaluationId)) && evaluations.length > 0) {
      setEvaluationId(evaluations[0].id);
    }
  }, [evaluations, evaluationId]);

  const activeEval = evaluations.find((e) => e.id === evaluationId) || null;
  const { data: responses, refresh: refreshResponses, realtime } = window.melr.useSatResponses(evaluationId);

  // Live scores
  const scores = useMemoAU(() => computeScores(norms, responses), [norms, responses]);

  const onCreateEvaluation = async () => {
    if (!projectId) return;
    const today = new Date();
    const cycle = today.getFullYear() + "-" + String(Math.ceil((today.getMonth() + 1) / 3)).padStart(1, "0") + "Q" + Math.ceil((today.getMonth() + 1) / 3);
    const defaultCycle = today.getFullYear() + "-Q" + Math.ceil((today.getMonth() + 1) / 3);
    const inputCycle = window.prompt(lang === "fr" ? "Cycle (ex : 2025-Q2 ou 2026-A1) :" : "Cycle (e.g. 2025-Q2 or 2026-A1):", defaultCycle);
    if (!inputCycle || !inputCycle.trim()) return;
    setBusy(true);
    try {
      const proj = (projects || []).find((p) => p.uuid === projectId);
      const created = await window.melr.satEvaluationsCrud.create(projectId, {
        cycle: inputCycle.trim(),
        state: "draft",
        country: proj ? proj.countriesFr : null,
      });
      await refreshEvals();
      setEvaluationId(created.id);
    } catch (e) {
      alert((lang === "fr" ? "Erreur : " : "Error: ") + e.message);
    } finally { setBusy(false); }
  };

  const onDeleteEvaluation = async () => {
    if (!activeEval) return;
    if (!confirm(lang === "fr"
      ? `Supprimer l'évaluation ${activeEval.cycle} ? Toutes les réponses seront perdues.`
      : `Delete evaluation ${activeEval.cycle}? All responses will be lost.`)) return;
    setBusy(true);
    try {
      await window.melr.satEvaluationsCrud.remove(activeEval.id);
      setEvaluationId("");
      await refreshEvals();
    } catch (e) { alert(e.message); }
    finally { setBusy(false); }
  };

  // Action plan needed for exports
  const { data: actionPlanItems } = window.melr.useSatActionPlan(evaluationId);

  // Submit the active SAT evaluation for validation workflow
  const onSubmitForValidation = async () => {
    if (!activeEval) return;
    setBusy(true);
    try {
      const proj = (projects || []).find((p) => p.uuid === projectId);
      const result = await window.melr.submitForValidation({
        object_type: "sat_evaluation",
        object_id: activeEval.id,
        project_id: projectId,
        title: "SAT " + activeEval.cycle + " — " + (proj ? proj.id : "?"),
        total_steps: 3,
        sla_days: 5,
        priority: "normal",
      });
      // Also bump state on the eval itself
      await window.melr.satEvaluationsCrud.update(activeEval.id, { state: "in_review" });
      await refreshEvals();
      alert(lang === "fr"
        ? (result.reused
            ? "Une validation est déjà en cours pour ce cycle (réutilisée)."
            : "Cycle soumis pour validation. Suivez-le dans le module Workflow.")
        : (result.reused
            ? "A validation is already in flight for this cycle (reused)."
            : "Cycle submitted for validation. Track it in the Workflow module."));
    } catch (e) {
      alert((lang === "fr" ? "Erreur : " : "Error: ") + e.message);
    } finally { setBusy(false); }
  };

  // Export helpers — Excel, Word, PDF
  const [pdfOpen, setPdfOpen] = useStateAU(false);
  const exportPayload = () => {
    const proj = (projects || []).find((p) => p.uuid === projectId);
    return {
      project: proj, evaluation: activeEval, domains, norms,
      responses, actionPlan: actionPlanItems, scores, lang,
    };
  };
  const onExportXlsx = () => {
    if (!activeEval) return alert(lang === "fr" ? "Aucune évaluation active." : "No active evaluation.");
    if (!window.satExporter || !window.satExporter.xlsxAvailable) return alert(lang === "fr" ? "Librairie XLSX indisponible." : "XLSX library unavailable.");
    window.satExporter.exportSatToXlsx(exportPayload());
  };
  const onExportDocx = () => {
    if (!activeEval) return alert(lang === "fr" ? "Aucune évaluation active." : "No active evaluation.");
    if (!window.satExporter || !window.satExporter.docxAvailable) return alert(lang === "fr" ? "Librairie docx indisponible." : "docx library unavailable.");
    window.satExporter.exportSatToDocx(exportPayload());
  };
  const onExportPdf = () => {
    if (!activeEval) return alert(lang === "fr" ? "Aucune évaluation active." : "No active evaluation.");
    setPdfOpen(true);
  };

  // Excel SAT import — strict mapping per the REFT Africa template
  const fileInputRef = React.useRef(null);
  const onImportClick = () => {
    if (!activeEval) {
      alert(lang === "fr"
        ? "Sélectionnez ou créez d'abord une évaluation (cycle)."
        : "Pick or create an evaluation (cycle) first.");
      return;
    }
    if (!window.satImporter || !window.satImporter.available) {
      alert(lang === "fr"
        ? "La librairie XLSX n'est pas disponible (CDN bloqué ?)."
        : "The XLSX library is not available (CDN blocked?).");
      return;
    }
    fileInputRef.current && fileInputRef.current.click();
  };
  const onFilePicked = async (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    e.target.value = "";  // reset so the same file can be re-picked
    setBusy(true);
    try {
      const parsed = await window.satImporter.importSatFromFile(file);
      if (parsed.errors.length > 0) {
        console.warn("[SAT import] warnings:", parsed.errors);
      }
      const totalParsed = Object.keys(parsed.responses).length;
      const ok = confirm(
        (lang === "fr"
          ? "Import détecté : " + totalParsed + " normes lues dans le fichier.\n"
          : "Import detected: " + totalParsed + " norms read.\n") +
        (parsed.metadata.organization ? "Organisation : " + parsed.metadata.organization + "\n" : "") +
        (parsed.metadata.team_lead    ? "Team lead : " + parsed.metadata.team_lead + "\n" : "") +
        "\n" + (lang === "fr"
          ? "Appliquer à l'évaluation '" + activeEval.cycle + "' ?\n(Les réponses existantes sur les mêmes normes seront remplacées.)"
          : "Apply to evaluation '" + activeEval.cycle + "'?\n(Existing responses on the same norms will be replaced.)")
      );
      if (!ok) return;
      const result = await window.satImporter.applyImport(parsed, activeEval, norms);
      await refreshEvals();
      await refreshResponses();
      alert(lang === "fr"
        ? "Import terminé : " + result.upserted + " normes mises à jour, " + result.skipped + " ignorées."
        : "Import done: " + result.upserted + " norms upserted, " + result.skipped + " skipped.");
    } catch (e) {
      console.error("[SAT import]", e);
      alert((lang === "fr" ? "Erreur d'import : " : "Import error: ") + e.message);
    } finally { setBusy(false); }
  };

  // Tabs: synthèse + 8 domains + plan
  const tabs = [
    { k: "synthese", l: lang === "fr" ? "Synthèse" : "Summary" },
    ...(domains || []).map((d) => ({
      k: "dom-" + d.code,
      l: d.code,
      title: d.title_fr,
      n: scores.perDomain[d.code] && scores.perDomain[d.code].count,
      sc: scores.perDomain[d.code],
    })),
    { k: "plan", l: lang === "fr" ? "Plan d'action" : "Action plan" },
  ];

  const scorePill = (sc) => {
    if (!sc || sc.max === 0) return null;
    const pct = sc.pct;
    const cls = pct >= 0.7 ? "green" : pct >= 0.6 ? "amber" : "red";
    return <span className={"pill " + cls} style={{ marginLeft: 6, fontSize: 10 }}>{Math.round(pct * 100)}%</span>;
  };

  return (
    <div className="page">
      <div className="page-header">
        <div className="page-eyebrow">{lang === "fr" ? "MODULE / AUDIT SYSTÈME S&E (SAT)" : "MODULE / M&E SYSTEM AUDIT (SAT)"}</div>
        <div className="page-header-row">
          <div>
            <h1 className="page-title">{t("nav.audit_system")} <LiveBadge on={realtime} lang={lang} /></h1>
            <div className="page-sub">
              {lang === "fr"
                ? "Outil SAT REFT Africa · 8 domaines · 100 normes · notation 0/1/2 (E : 0/5/10) · auto-évaluation par projet et par cycle"
                : "REFT Africa SAT tool · 8 domains · 100 norms · 0/1/2 rating (E: 0/5/10) · self-assessment per project and cycle"}
            </div>
          </div>
          <div className="page-header-actions">
            <button className="btn sm" onClick={onImportClick} disabled={!activeEval || busy}
              title={lang === "fr"
                ? "Importer un classeur SAT rempli (template REFT Africa)"
                : "Import a filled SAT workbook (REFT Africa template)"}>
              <Icon.upload /> {lang === "fr" ? "Importer Excel" : "Import Excel"}
            </button>
            <input ref={fileInputRef} type="file" accept=".xlsx" onChange={onFilePicked} style={{ display: "none" }} />
            <button className="btn sm" onClick={onExportXlsx} disabled={!activeEval}
              title={lang === "fr" ? "Exporter au format Excel (template REFT Africa)" : "Export as Excel (REFT Africa template)"}>
              <Icon.download /> Excel
            </button>
            <button className="btn sm" onClick={onExportDocx} disabled={!activeEval}
              title={lang === "fr" ? "Exporter le rapport au format Word" : "Export report as Word"}>
              <Icon.download /> Word
            </button>
            <button className="btn sm primary" onClick={onExportPdf} disabled={!activeEval}
              title={lang === "fr" ? "Aperçu PDF imprimable" : "Printable PDF preview"}>
              <Icon.download /> PDF
            </button>
          </div>
        </div>
      </div>

      <div className="page-body">
        {/* Project + Cycle selector card */}
        <div className="card" style={{ marginBottom: 14 }}>
          <div className="card-body" style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", padding: "10px 14px" }}>
            <span className="text-faint" style={{ fontSize: 11.5, textTransform: "uppercase", letterSpacing: "0.04em" }}>
              {lang === "fr" ? "Projet" : "Project"}
            </span>
            <select value={projectId} onChange={(e) => { setProjectId(e.target.value); setEvaluationId(""); }}
              style={{ padding: "6px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, background: "var(--bg, white)", color: "var(--text)", minWidth: 280 }}>
              <option value="">{lang === "fr" ? "— choisir un projet —" : "— pick a project —"}</option>
              {(projects || []).map((p) => (
                <option key={p.uuid} value={p.uuid}>{p.id} — {lang === "fr" ? p.nameFr : p.nameEn}</option>
              ))}
            </select>

            <span className="text-faint" style={{ fontSize: 11.5, textTransform: "uppercase", letterSpacing: "0.04em", marginLeft: 8 }}>
              {lang === "fr" ? "Cycle" : "Cycle"}
            </span>
            <select value={evaluationId} onChange={(e) => setEvaluationId(e.target.value)} disabled={!projectId}
              style={{ padding: "6px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, background: "var(--bg, white)", color: "var(--text)", minWidth: 180 }}>
              <option value="">{lang === "fr" ? "— choisir un cycle —" : "— pick a cycle —"}</option>
              {(evaluations || []).map((e) => (
                <option key={e.id} value={e.id}>{e.cycle} ({e.state})</option>
              ))}
            </select>

            <button className="btn sm primary" onClick={onCreateEvaluation} disabled={!projectId || busy}>
              <Icon.plus /> {lang === "fr" ? "Nouvelle évaluation" : "New evaluation"}
            </button>
            {activeEval && (
              <button className="btn sm" onClick={onSubmitForValidation} disabled={busy || activeEval.state === "in_review" || activeEval.state === "approved"}
                title={lang === "fr"
                  ? "Soumettre ce cycle pour validation par la chaîne d'approbation"
                  : "Submit this cycle for approval workflow"}>
                <Icon.send /> {lang === "fr" ? "Soumettre pour validation" : "Submit for approval"}
              </button>
            )}
            {activeEval && (
              <button className="btn sm" onClick={onDeleteEvaluation} disabled={busy}
                title={lang === "fr" ? "Supprimer le cycle actif" : "Delete active cycle"}>
                <Icon.trash />
              </button>
            )}

            {scores.total.max > 0 && (
              <div style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 10 }}>
                <span className="text-faint" style={{ fontSize: 11 }}>
                  {lang === "fr" ? "Score global" : "Overall score"}
                </span>
                <span className="tag-mono" style={{ fontSize: 14, fontWeight: 600 }}>
                  {scores.total.total} / {scores.total.max}
                </span>
                <span className={"pill " + (scores.total.pct >= 0.7 ? "green" : scores.total.pct >= 0.6 ? "amber" : "red")}>
                  {Math.round(scores.total.pct * 100)}%
                </span>
              </div>
            )}
          </div>
        </div>

        {!evaluationId ? (
          <div className="card"><div className="card-body" style={{ padding: 50, textAlign: "center", color: "var(--text-faint)" }}>
            {projectId
              ? (lang === "fr" ? "Aucun cycle d'évaluation pour ce projet. Cliquez « Nouvelle évaluation »." : "No evaluation cycle for this project. Click 'New evaluation'.")
              : (lang === "fr" ? "Sélectionnez un projet pour commencer." : "Pick a project to start.")}
          </div></div>
        ) : (
          <>
            {/* Tabs */}
            <div className="page-tabs" style={{ marginTop: 0, overflowX: "auto", flexWrap: "nowrap" }}>
              {tabs.map((x) => (
                <div key={x.k} className={"tab" + (x.k === tab ? " active" : "")} onClick={() => setTab(x.k)}
                  title={x.title || x.l}
                  style={{ whiteSpace: "nowrap" }}>
                  {x.l}{x.n != null && <span className="tag-mono" style={{ marginLeft: 4 }}>{x.n}</span>}{scorePill(x.sc)}
                </div>
              ))}
            </div>

            {tab === "synthese" && (
              <AuditSynthese lang={lang} projects={projects} projectId={projectId}
                domains={domains} norms={norms} responses={responses}
                scores={scores} evaluations={evaluations} activeEval={activeEval} />
            )}
            {tab === "plan" && (
              <AuditActionPlan lang={lang} evaluationId={evaluationId}
                domains={domains} norms={norms} responses={responses} />
            )}
            {tab.startsWith("dom-") && (
              <AuditDomain lang={lang} domain={tab.slice(4)}
                domains={domains} norms={norms}
                responses={responses} evaluationId={evaluationId}
                scoreInfo={scores.perDomain[tab.slice(4)]} />
            )}
          </>
        )}
      </div>
      {pdfOpen && (
        <SatPdfModal lang={lang} onClose={() => setPdfOpen(false)}
          project={(projects || []).find((p) => p.uuid === projectId)}
          evaluation={activeEval}
          domains={domains} norms={norms} responses={responses}
          actionPlan={actionPlanItems} scores={scores} />
      )}
    </div>
  );
}

// -----------------------------------------------------------------------------
// Domain tab — norms list with scoring + auto-save
// -----------------------------------------------------------------------------
function AuditDomain({ lang, domain, domains, norms, responses, evaluationId, scoreInfo }) {
  const dom = (domains || []).find((d) => d.code === domain);
  const domNorms = (norms || []).filter((n) => n.domain_code === domain);
  const respByNorm = new Map();
  (responses || []).forEach((r) => respByNorm.set(r.norm_id, r));
  const ratings = ratingsFor(domain);
  const [busy, setBusy] = useStateAU(null);
  const [err, setErr]   = useStateAU(null);

  const onChange = async (normId, patch) => {
    setBusy(normId); setErr(null);
    try {
      // value derives from rating
      if (patch.rating !== undefined) {
        patch.value = ratingValue(domain, patch.rating);
      }
      await window.melr.satResponsesCrud.upsert(evaluationId, normId, patch);
    } catch (e) { setErr(e.message); }
    finally { setBusy(null); }
  };

  return (
    <div className="card">
      <div className="card-head">
        <div className="card-title">{dom ? dom.title_fr : domain}</div>
        {scoreInfo && scoreInfo.max > 0 && (
          <>
            <span className="tag-mono" style={{ marginLeft: 10 }}>
              {scoreInfo.total} / {scoreInfo.max}
            </span>
            <span className={"pill " + (scoreInfo.pct >= 0.7 ? "green" : scoreInfo.pct >= 0.6 ? "amber" : "red")}>
              {Math.round(scoreInfo.pct * 100)}%
            </span>
          </>
        )}
        <span className="text-faint" style={{ marginLeft: "auto", fontSize: 11.5 }}>
          {scoreInfo ? scoreInfo.answered : 0} / {domNorms.length} {lang === "fr" ? "renseignées" : "answered"}
        </span>
      </div>
      {err && <div style={{ padding: "8px 14px", color: "#b91c1c", fontSize: 12 }}>{err}</div>}
      <div className="card-body flush">
        {domNorms.length === 0 && (
          <div style={{ padding: 22, color: "var(--text-faint)", textAlign: "center" }}>
            {lang === "fr" ? "Aucune norme pour ce domaine." : "No norms for this domain."}
          </div>
        )}
        {domNorms.map((n) => {
          const r = respByNorm.get(n.id) || { rating: "", observation: "", is_priority: false };
          const isBusy = busy === n.id;
          // Color the row by status
          const rowBg = !r.rating || r.rating === "na" ? "transparent"
                      : (r.value != null && r.value === n.max_value) ? "#f0fdf4"
                      : (r.value != null && r.value === 0) ? "#fef2f2"
                      : "#fffbeb";
          return (
            <div key={n.id} style={{
              padding: "14px 16px", borderBottom: "1px solid var(--line-faint)",
              background: rowBg, transition: "background 0.2s",
            }}>
              <div style={{ display: "grid", gridTemplateColumns: "40px 1fr 220px", gap: 12, alignItems: "start" }}>
                <span className="tag-mono" style={{ fontWeight: 600, fontSize: 12 }}>{n.position}.</span>
                <div>
                  <div style={{ fontSize: 12.5, lineHeight: 1.4, marginBottom: 4 }}>{n.text_fr}</div>
                  {n.mov_fr && (
                    <div className="text-faint" style={{ fontSize: 10.5, marginBottom: 8 }}>
                      <strong>{lang === "fr" ? "MOV" : "MOV"} :</strong> {n.mov_fr}
                    </div>
                  )}
                  <textarea
                    placeholder={lang === "fr" ? "Observations, justificatifs et recommandations…" : "Observations, justifications, recommendations…"}
                    value={r.observation || ""}
                    onChange={(e) => {
                      const newObs = e.target.value;
                      // Debounced via blur — for now save on each change is fine for low traffic
                      respByNorm.set(n.id, { ...r, observation: newObs });
                    }}
                    onBlur={(e) => onChange(n.id, { observation: e.target.value || null, rating: r.rating || null })}
                    style={{
                      width: "100%", boxSizing: "border-box",
                      padding: "6px 8px", borderRadius: 6, border: "1px solid var(--line)",
                      fontSize: 11.5, minHeight: 40, resize: "vertical", fontFamily: "inherit",
                      background: "var(--bg, white)", color: "var(--text)",
                    }} />
                </div>
                <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                  <select
                    value={r.rating || ""}
                    disabled={isBusy}
                    onChange={(e) => onChange(n.id, { rating: e.target.value || null, observation: r.observation || null })}
                    style={{
                      padding: "6px 8px", borderRadius: 6, border: "1px solid var(--line)",
                      fontSize: 11.5, background: "var(--bg, white)", color: "var(--text)",
                    }}>
                    <option value="">{lang === "fr" ? "— non noté —" : "— unscored —"}</option>
                    {ratings.map((rt) => (
                      <option key={rt.v} value={rt.v}>
                        {lang === "fr" ? rt.fr : rt.en}{rt.value != null ? " (" + rt.value + "/" + n.max_value + ")" : ""}
                      </option>
                    ))}
                  </select>
                  <label style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 11, color: "var(--text-faint)", cursor: "pointer" }}>
                    <input type="checkbox" checked={!!r.is_priority}
                      onChange={(e) => onChange(n.id, { is_priority: e.target.checked, rating: r.rating || null, observation: r.observation || null })} />
                    {lang === "fr" ? "Recommandation prioritaire" : "Priority recommendation"}
                  </label>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// -----------------------------------------------------------------------------
// Synthèse tab — radar + per-domain bars + priority recommendations + history
// -----------------------------------------------------------------------------
function AuditSynthese({ lang, projects, projectId, domains, norms, responses, scores, evaluations, activeEval }) {
  const proj = (projects || []).find((p) => p.uuid === projectId);

  // Priority recommendations from responses
  const priorityRecs = (responses || [])
    .filter((r) => r.is_priority)
    .map((r) => {
      const n = (norms || []).find((x) => x.id === r.norm_id);
      return n ? { ...n, response: r } : null;
    })
    .filter(Boolean)
    .sort((a, b) => a.domain_code.localeCompare(b.domain_code) || a.position - b.position);

  const radarDims = (domains || []).map((d) => ({
    code: d.code,
    title: d.title_fr,
    s: scores.perDomain[d.code] ? Math.round(scores.perDomain[d.code].pct * 100) : 0,
    max: 100,
  }));

  return (
    <>
      {/* Header info: project + cycle */}
      <div className="card" style={{ marginBottom: 14 }}>
        <div className="card-body" style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 14, padding: "10px 14px" }}>
          <div>
            <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Projet" : "Project"}</div>
            <div style={{ fontSize: 13, fontWeight: 500 }}>{proj ? (proj.id + " — " + (lang === "fr" ? proj.nameFr : proj.nameEn)) : "—"}</div>
          </div>
          <div>
            <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Cycle" : "Cycle"}</div>
            <div style={{ fontSize: 13, fontWeight: 500 }}>{activeEval ? activeEval.cycle : "—"}</div>
          </div>
          <div>
            <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Normes renseignées" : "Norms answered"}</div>
            <div style={{ fontSize: 13, fontWeight: 500 }}>{scores.total.answered} / {scores.total.count}</div>
          </div>
          <div>
            <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "Recommandations prioritaires" : "Priority recommendations"}</div>
            <div style={{ fontSize: 13, fontWeight: 500 }}>{priorityRecs.length}</div>
          </div>
        </div>
      </div>

      <div className="grid cols-2">
        <div className="card">
          <div className="card-head">
            <div className="card-title">{lang === "fr" ? "Profil radar — 8 domaines SAT" : "Radar profile — 8 SAT domains"}</div>
            <span className={"pill " + (scores.total.pct >= 0.7 ? "green" : scores.total.pct >= 0.6 ? "amber" : "red")}>
              {Math.round(scores.total.pct * 100)}%
            </span>
          </div>
          <div className="card-body">
            <Radar dims={radarDims} />
          </div>
        </div>
        <div className="card">
          <div className="card-head">
            <div className="card-title">{lang === "fr" ? "Score par domaine" : "Score per domain"}</div>
          </div>
          <div className="card-body flush">
            {radarDims.map((d) => (
              <div key={d.code} style={{ padding: "10px 16px", borderBottom: "1px solid var(--line-faint)" }}>
                <div className="row" style={{ marginBottom: 4 }}>
                  <span style={{ fontWeight: 500, fontSize: 12.5 }}>{d.title}</span>
                  <span className="tag-mono" style={{ marginLeft: "auto" }}>{d.s} / 100</span>
                </div>
                <div className={"progress-bar " + (d.s >= 70 ? "green" : d.s >= 60 ? "amber" : "red")}>
                  <div className="fill" style={{ width: d.s + "%" }}></div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* Score evolution across past cycles for this project */}
      {evaluations.length > 1 && (
        <>
          <div style={{ height: 16 }} />
          <div className="card">
            <div className="card-head">
              <div className="card-title">{lang === "fr" ? "Évolution du score (cycles passés)" : "Score evolution (past cycles)"}</div>
            </div>
            <div className="card-body">
              <ScoreEvolution evaluations={evaluations} activeEvalId={activeEval ? activeEval.id : null} lang={lang} />
            </div>
          </div>
        </>
      )}

      {/* Priority recommendations summary */}
      {priorityRecs.length > 0 && (
        <>
          <div style={{ height: 16 }} />
          <div className="card">
            <div className="card-head">
              <div className="card-title">{lang === "fr" ? "Recommandations prioritaires" : "Priority recommendations"}</div>
              <span className="pill red">{priorityRecs.length}</span>
            </div>
            <div className="card-body flush">
              {priorityRecs.map((rec) => (
                <div key={rec.id} style={{ padding: "10px 14px", borderBottom: "1px solid var(--line-faint)" }}>
                  <div className="row" style={{ marginBottom: 4 }}>
                    <span className="tag-mono">{rec.domain_code}.{rec.position}</span>
                    <span style={{ fontWeight: 500, fontSize: 12.5, marginLeft: 8, flex: 1 }}>{rec.text_fr}</span>
                  </div>
                  {rec.response.observation && (
                    <div className="text-muted" style={{ fontSize: 11.5, marginTop: 4, paddingLeft: 12, borderLeft: "2px solid #fca5a5" }}>
                      {rec.response.observation}
                    </div>
                  )}
                </div>
              ))}
            </div>
          </div>
        </>
      )}
    </>
  );
}

function ScoreEvolution({ evaluations, activeEvalId, lang }) {
  const W = 600, H = 200, padL = 40, padR = 20, padT = 16, padB = 32;
  // Sort ascending by cycle string
  const sorted = [...evaluations].filter((e) => e.score_max).sort((a, b) => String(a.cycle).localeCompare(String(b.cycle)));
  if (sorted.length < 2) {
    return <div className="text-faint" style={{ padding: 20, textAlign: "center", fontSize: 12 }}>
      {lang === "fr" ? "Au moins 2 cycles enregistrés sont nécessaires pour afficher l'évolution." : "At least 2 saved cycles needed to display evolution."}
    </div>;
  }
  const xs = (i) => padL + (i / (sorted.length - 1)) * (W - padL - padR);
  const ys = (p) => padT + (1 - p) * (H - padT - padB);
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%" }}>
      {[0, 0.25, 0.5, 0.75, 1].map((g) => (
        <g key={g}>
          <line x1={padL} y1={ys(g)} x2={W - padR} y2={ys(g)} stroke="var(--line-faint)" strokeDasharray="2 3" />
          <text x={padL - 6} y={ys(g) + 3} textAnchor="end" fontSize="9" fill="var(--text-faint)">{Math.round(g * 100)}</text>
        </g>
      ))}
      <polyline
        points={sorted.map((e, i) => xs(i) + "," + ys(e.score_max > 0 ? e.score_total / e.score_max : 0)).join(" ")}
        fill="none" stroke="var(--accent)" strokeWidth="2" />
      {sorted.map((e, i) => (
        <g key={e.id}>
          <circle cx={xs(i)} cy={ys(e.score_max > 0 ? e.score_total / e.score_max : 0)} r={e.id === activeEvalId ? 5 : 3} fill="var(--accent)" />
          <text x={xs(i)} y={H - 8} textAnchor="middle" fontSize="9.5" fill="var(--text-faint)">{e.cycle}</text>
        </g>
      ))}
    </svg>
  );
}

function Radar({ dims }) {
  const cx = 180, cy = 180, R = 140;
  const N = dims.length;
  if (N === 0) return null;
  const angle = (i) => (i / N) * Math.PI * 2 - Math.PI / 2;
  const point = (i, r) => [cx + Math.cos(angle(i)) * r, cy + Math.sin(angle(i)) * r];
  const polygon = dims.map((d, i) => point(i, (d.s / 100) * R).join(",")).join(" ");
  return (
    <svg viewBox="0 0 360 360" style={{ width: "100%", maxWidth: 360, margin: "0 auto", display: "block" }}>
      {[0.25, 0.5, 0.75, 1].map((r, i) => (
        <polygon key={i} points={dims.map((_, j) => point(j, R * r).join(",")).join(" ")} fill="none" stroke="var(--line-faint)" />
      ))}
      {dims.map((_, i) => <line key={i} x1={cx} y1={cy} x2={point(i, R)[0]} y2={point(i, R)[1]} stroke="var(--line-faint)" />)}
      <polygon points={polygon} fill="var(--accent)" fillOpacity="0.15" stroke="var(--accent)" strokeWidth="1.6" />
      {dims.map((d, i) => {
        const [x, y] = point(i, R + 18);
        return <text key={i} x={x} y={y} fontSize="11" fill="var(--text)" textAnchor="middle" dominantBaseline="middle" fontWeight="600">{d.code}</text>;
      })}
      {dims.map((d, i) => {
        const [x, y] = point(i, (d.s / 100) * R);
        return <circle key={i} cx={x} cy={y} r="3" fill="var(--accent)" />;
      })}
    </svg>
  );
}

// -----------------------------------------------------------------------------
// Action plan tab — list of recommendations + CRUD
// -----------------------------------------------------------------------------
function AuditActionPlan({ lang, evaluationId, domains, norms, responses }) {
  const { data: items, refresh } = window.melr.useSatActionPlan(evaluationId);
  const [editing, setEditing]   = useStateAU(null);
  const [modalOpen, setModalOpen] = useStateAU(false);

  const onDelete = async (id) => {
    if (!confirm(lang === "fr" ? "Supprimer cette action ?" : "Delete this action?")) return;
    try { await window.melr.satActionPlanCrud.remove(id); }
    catch (e) { alert(e.message); }
  };

  // Group by domain
  const grouped = new Map();
  (items || []).forEach((it) => {
    const d = it.domain_code || "—";
    if (!grouped.has(d)) grouped.set(d, []);
    grouped.get(d).push(it);
  });

  return (
    <>
      <div className="card">
        <div className="card-head">
          <div className="card-title">{lang === "fr" ? "Plan d'action consolidé" : "Consolidated action plan"}</div>
          <span className="pill">{items.length}</span>
          <button className="btn sm primary" style={{ marginLeft: "auto" }}
            onClick={() => { setEditing(null); setModalOpen(true); }}>
            <Icon.plus /> {lang === "fr" ? "Nouvelle action" : "New action"}
          </button>
        </div>
        <div className="card-body flush">
          {items.length === 0 && (
            <div style={{ padding: 22, color: "var(--text-faint)", textAlign: "center", fontSize: 13 }}>
              {lang === "fr" ? "Aucune action enregistrée. Cochez « Recommandation prioritaire » sur les normes pertinentes, puis ajoutez ici une action correspondante." : "No actions yet. Mark norms as priority recommendation, then add corresponding actions here."}
            </div>
          )}
          {Array.from(grouped.entries()).sort().map(([dCode, list]) => {
            const dom = (domains || []).find((d) => d.code === dCode);
            return (
              <div key={dCode}>
                <div style={{ padding: "8px 16px", background: "var(--bg-sunken, #f9fafb)", fontWeight: 600, fontSize: 12.5 }}>
                  {dom ? dom.title_fr : dCode}
                </div>
                {list.sort((a, b) => (a.priority || 9) - (b.priority || 9)).map((it) => (
                  <div key={it.id} style={{
                    padding: "10px 16px", borderBottom: "1px solid var(--line-faint)",
                    display: "grid", gridTemplateColumns: "auto 1fr auto auto auto auto", gap: 10, alignItems: "center",
                  }}>
                    <span className={"pill " + (it.priority === 1 ? "red" : it.priority === 2 ? "amber" : "")}>
                      P{it.priority || 2}
                    </span>
                    <div>
                      <div style={{ fontSize: 12.5 }}>
                        {it.norm_position ? <span className="tag-mono" style={{ marginRight: 6 }}>{dCode}.{it.norm_position}{it.sub_letter ? " " + it.sub_letter : ""}</span> : null}
                        {it.text || "—"}
                      </div>
                    </div>
                    <span className="tag-mono" style={{ fontSize: 11 }}>{it.owner || "—"}</span>
                    <span className="text-faint" style={{ fontSize: 11 }}>{it.due_date || "—"}</span>
                    <span className={"pill " + (it.status === "done" ? "green" : it.status === "cancelled" ? "" : "amber")}>
                      {it.status === "done" ? (lang === "fr" ? "Fait" : "Done")
                       : it.status === "cancelled" ? (lang === "fr" ? "Annulé" : "Cancelled")
                       : (lang === "fr" ? "En cours" : "Pending")}
                    </span>
                    <div style={{ display: "flex", gap: 4 }}>
                      <button className="btn sm ghost" onClick={() => { setEditing(it); setModalOpen(true); }} title={lang === "fr" ? "Modifier" : "Edit"}><Icon.edit /></button>
                      <button className="btn sm ghost" onClick={() => onDelete(it.id)} title={lang === "fr" ? "Supprimer" : "Delete"}><Icon.trash /></button>
                    </div>
                  </div>
                ))}
              </div>
            );
          })}
        </div>
      </div>
      {modalOpen && (
        <ActionPlanModal
          lang={lang} evaluationId={evaluationId}
          domains={domains} editing={editing}
          onClose={() => setModalOpen(false)} />
      )}
    </>
  );
}

function ActionPlanModal({ lang, evaluationId, domains, editing, onClose }) {
  const [domainCode, setDomainCode] = useStateAU(editing ? (editing.domain_code || "") : "");
  const [normPosition, setNormPosition] = useStateAU(editing ? (editing.norm_position || "") : "");
  const [subLetter, setSubLetter]   = useStateAU(editing ? (editing.sub_letter || "") : "");
  const [text, setText]             = useStateAU(editing ? (editing.text || "") : "");
  const [owner, setOwner]           = useStateAU(editing ? (editing.owner || "") : "");
  const [dueDate, setDueDate]       = useStateAU(editing ? (editing.due_date || "") : "");
  const [status, setStatus]         = useStateAU(editing ? (editing.status || "pending") : "pending");
  const [priority, setPriority]     = useStateAU(editing ? (editing.priority || 2) : 2);
  const [busy, setBusy]             = useStateAU(false);
  const [err, setErr]               = useStateAU(null);

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { display: "block", fontSize: 11, color: "var(--text-faint)", marginBottom: 4, textTransform: "uppercase", letterSpacing: "0.04em", marginTop: 10 };

  const onSubmit = async (e) => {
    e.preventDefault();
    if (!text.trim()) return;
    setBusy(true); setErr(null);
    try {
      const payload = {
        domain_code: domainCode || null,
        norm_position: normPosition ? parseInt(normPosition, 10) : null,
        sub_letter: subLetter || null,
        text: text.trim(),
        owner: owner.trim() || null,
        due_date: dueDate || null,
        status,
        priority: parseInt(priority, 10) || 2,
      };
      if (editing) await window.melr.satActionPlanCrud.update(editing.id, payload);
      else          await window.melr.satActionPlanCrud.create(evaluationId, payload);
      onClose();
    } catch (e) { setErr(e.message); }
    finally { setBusy(false); }
  };

  return (
    <div onClick={() => !busy && onClose()} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.5)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={onSubmit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 560, maxWidth: "92vw",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)",
      }}>
        <div style={{ fontSize: 17, fontWeight: 600, marginBottom: 4 }}>
          {editing ? (lang === "fr" ? "Modifier l'action" : "Edit action") : (lang === "fr" ? "Nouvelle action" : "New action")}
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr", gap: 10 }}>
          <div>
            <label style={lbl}>{lang === "fr" ? "Domaine" : "Domain"}</label>
            <select style={inp} value={domainCode} onChange={(e) => setDomainCode(e.target.value)}>
              <option value="">—</option>
              {(domains || []).map((d) => <option key={d.code} value={d.code}>{d.title_fr}</option>)}
            </select>
          </div>
          <div>
            <label style={lbl}>{lang === "fr" ? "Norme n°" : "Norm #"}</label>
            <input type="number" min={1} style={inp} value={normPosition} onChange={(e) => setNormPosition(e.target.value)} />
          </div>
          <div>
            <label style={lbl}>{lang === "fr" ? "Sous-pt." : "Sub-pt."}</label>
            <select style={inp} value={subLetter} onChange={(e) => setSubLetter(e.target.value)}>
              <option value="">—</option><option value="a">a</option><option value="b">b</option><option value="c">c</option><option value="d">d</option>
            </select>
          </div>
        </div>

        <label style={lbl}>{lang === "fr" ? "Action" : "Action"} *</label>
        <textarea required style={{ ...inp, minHeight: 60, resize: "vertical", fontFamily: "inherit" }}
          value={text} onChange={(e) => setText(e.target.value)} />

        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr", gap: 10 }}>
          <div>
            <label style={lbl}>{lang === "fr" ? "Responsable" : "Owner"}</label>
            <input style={inp} value={owner} onChange={(e) => setOwner(e.target.value)} />
          </div>
          <div>
            <label style={lbl}>{lang === "fr" ? "Échéance" : "Due"}</label>
            <input type="date" style={inp} value={dueDate} onChange={(e) => setDueDate(e.target.value)} />
          </div>
          <div>
            <label style={lbl}>{lang === "fr" ? "Priorité" : "Priority"}</label>
            <select style={inp} value={priority} onChange={(e) => setPriority(e.target.value)}>
              <option value={1}>P1 — {lang === "fr" ? "Haute" : "High"}</option>
              <option value={2}>P2 — {lang === "fr" ? "Moyenne" : "Medium"}</option>
              <option value={3}>P3 — {lang === "fr" ? "Faible" : "Low"}</option>
            </select>
          </div>
        </div>

        <label style={lbl}>{lang === "fr" ? "Statut" : "Status"}</label>
        <select style={inp} value={status} onChange={(e) => setStatus(e.target.value)}>
          <option value="pending">{lang === "fr" ? "En cours" : "Pending"}</option>
          <option value="done">{lang === "fr" ? "Fait" : "Done"}</option>
          <option value="cancelled">{lang === "fr" ? "Annulé" : "Cancelled"}</option>
        </select>

        {err && <div style={{ color: "#b91c1c", fontSize: 12, marginTop: 10 }}>{err}</div>}

        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (editing ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Ajouter" : "Add"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// -----------------------------------------------------------------------------
// PDF preview modal — fullscreen overlay with print + close buttons.
// The body is SatReportPrintable; print CSS in styles.css hides the rest.
// -----------------------------------------------------------------------------
function SatPdfModal({ lang, onClose, project, evaluation, domains, norms, responses, actionPlan, scores }) {
  const onPrint = () => window.print();
  return (
    <div className="exante-report-overlay" style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.6)", zIndex: 9999,
      display: "flex", flexDirection: "column",
    }}>
      <div className="exante-report-toolbar" style={{
        padding: "10px 16px", background: "var(--bg, white)", color: "var(--text, #111)",
        borderBottom: "1px solid var(--line)", display: "flex", gap: 10, alignItems: "center",
      }}>
        <div style={{ fontWeight: 600, fontSize: 14 }}>
          {lang === "fr" ? "Aperçu SAT — impression / PDF" : "SAT preview — print / PDF"}
        </div>
        <span className="pill" style={{ marginLeft: 8 }}>
          {(project && project.id) || "—"} · {(evaluation && evaluation.cycle) || "—"}
        </span>
        <div style={{ flex: 1 }} />
        <button onClick={onPrint}
          style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
          ↓ {lang === "fr" ? "Imprimer / PDF" : "Print / PDF"}
        </button>
        <button onClick={onClose}
          style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
          {lang === "fr" ? "Fermer" : "Close"}
        </button>
      </div>
      <div className="exante-report-scroll" style={{ flex: 1, overflow: "auto", background: "#f3f4f6", padding: "24px 0" }}>
        <SatReportPrintable
          project={project} evaluation={evaluation}
          domains={domains} norms={norms} responses={responses}
          actionPlan={actionPlan} scores={scores} lang={lang} />
      </div>
    </div>
  );
}

// Printable HTML — id "sat-report-printable" is targeted by the print CSS.
function SatReportPrintable({ project, evaluation, domains, norms, responses, actionPlan, scores, lang }) {
  const today = new Date().toLocaleDateString(lang === "fr" ? "fr-FR" : "en-US");
  // Group responses by domain for rendering
  const respByNorm = new Map();
  (responses || []).forEach((r) => respByNorm.set(r.norm_id, r));
  const SHADE = {
    ok:     { bg: "#dcfce7", fg: "#15803d", border: "#86efac" },
    amber:  { bg: "#fef3c7", fg: "#a16207", border: "#fcd34d" },
    bad:    { bg: "#fee2e2", fg: "#b91c1c", border: "#fca5a5" },
    neutral:{ bg: "#f3f4f6", fg: "#374151", border: "#d1d5db" },
    accent: { bg: "#e0e7ff", fg: "#1e3a8a", border: "#a5b4fc" },
  };
  const statusForPct = (p) => (p >= 0.7 ? "ok" : p >= 0.6 ? "amber" : "bad");
  const fmtPct = (v) => (v == null || !isFinite(v) ? "—" : Math.round(v * 100) + "%");

  const ratingFRLabel = (domain, code) => {
    if (!code) return "—";
    if (domain === "E") {
      return { na: "N/A", above_10: "Plus de 10%", "5_to_10": "Entre 5 et 10%", under_5: "Moins de 5%" }[code] || "—";
    }
    return { na: "N/A", none: "Ne répond pas", partial: "Partiellement", full: "Pleinement" }[code] || "—";
  };

  const Tile = ({ label, value, sub, status }) => {
    const c = SHADE[status || "neutral"];
    return (
      <div style={{ border: "1px solid " + c.border, background: c.bg, color: c.fg, borderRadius: 6, padding: "10px 12px", minHeight: 70 }}>
        <div style={{ fontSize: 9.5, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em", opacity: 0.8 }}>{label}</div>
        <div style={{ fontSize: 20, fontWeight: 700, fontFamily: "Consolas, monospace", marginTop: 4 }}>{value}</div>
        {sub && <div style={{ fontSize: 10, opacity: 0.75, marginTop: 2 }}>{sub}</div>}
      </div>
    );
  };

  const styles = {
    body: { fontFamily: "Calibri, Arial, sans-serif", color: "#111", padding: 32, maxWidth: 900, margin: "0 auto", background: "white", fontSize: 11.5 },
    h1: { fontSize: 18, fontWeight: 700, color: "#1f2937", marginTop: 28, marginBottom: 10, borderBottom: "2px solid #1f2937", paddingBottom: 6 },
    h2: { fontSize: 13.5, fontWeight: 600, color: "#374151", marginTop: 16, marginBottom: 8 },
    table: { width: "100%", borderCollapse: "collapse", marginBottom: 12, fontSize: 10.5 },
    th: { borderBottom: "2px solid #1f2937", textAlign: "left", padding: "5px 6px", fontWeight: 700, background: "#f9fafb", fontSize: 10 },
    td: { borderBottom: "1px solid #e5e7eb", padding: "4px 6px", verticalAlign: "top" },
    pill: (s) => ({ display: "inline-block", padding: "1px 6px", borderRadius: 999, background: SHADE[s].bg, color: SHADE[s].fg, border: "1px solid " + SHADE[s].border, fontSize: 9.5, fontWeight: 700 }),
  };

  return (
    <div id="sat-report-printable" style={styles.body}>
      {/* Cover */}
      <div style={{ textAlign: "center", marginBottom: 24, paddingTop: 30 }}>
        <div style={{ fontSize: 10, color: "#666", marginBottom: 8, textTransform: "uppercase", letterSpacing: "0.1em" }}>
          Évaluation du système de suivi-évaluation (SAT) — REFT Africa
        </div>
        <h1 style={{ fontSize: 22, fontWeight: 700, margin: "4px 0" }}>
          {project ? (project.id + " — " + (project.nameFr || project.nameEn)) : "—"}
        </h1>
        <div style={{ fontSize: 12, color: "#555", marginTop: 12, lineHeight: 1.6 }}>
          <strong>Cycle :</strong> {(evaluation && evaluation.cycle) || "—"}  ·  <strong>Date :</strong> {today}<br />
          <strong>Pays :</strong> {(evaluation && evaluation.country) || "—"}  ·  <strong>Organisation :</strong> {(evaluation && evaluation.organization) || "—"}<br />
          <strong>Team Lead :</strong> {(evaluation && evaluation.team_lead) || "—"}  ·  <strong>Organisation Lead :</strong> {(evaluation && evaluation.organization_lead) || "—"}
        </div>
      </div>

      {/* Synthèse */}
      <h1 style={styles.h1}>I. Synthèse exécutive</h1>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 10, marginBottom: 16 }}>
        <Tile label="Score total"
          value={(scores.total.total || 0) + " / " + (scores.total.max || 0)}
          sub={fmtPct(scores.total.pct)} status={statusForPct(scores.total.pct || 0)} />
        <Tile label="Normes" value={scores.total.count || 0}
          sub={(scores.total.answered || 0) + " renseignées"} status="accent" />
        <Tile label="% atteint" value={Math.round((scores.total.pct || 0) * 100) + "%"}
          sub="≥ 70% pour conformité" status={statusForPct(scores.total.pct || 0)} />
        <Tile label="Recos prioritaires"
          value={(responses || []).filter((r) => r.is_priority).length}
          sub="à traiter" status="amber" />
      </div>

      <h2 style={styles.h2}>Score par domaine</h2>
      <table style={styles.table}>
        <thead>
          <tr>
            <th style={styles.th}>Domaine</th>
            <th style={{ ...styles.th, textAlign: "right" }}>Score</th>
            <th style={{ ...styles.th, textAlign: "right" }}>Max</th>
            <th style={{ ...styles.th, textAlign: "right" }}>%</th>
            <th style={{ ...styles.th, textAlign: "center" }}>Renseignées</th>
          </tr>
        </thead>
        <tbody>
          {(domains || []).map((d) => {
            const sc = scores.perDomain[d.code] || { total: 0, max: 0, pct: 0, answered: 0, count: 0 };
            const st = statusForPct(sc.pct);
            return (
              <tr key={d.code}>
                <td style={styles.td}>{d.title_fr}</td>
                <td style={{ ...styles.td, textAlign: "right", fontFamily: "Consolas, monospace" }}>{sc.total}</td>
                <td style={{ ...styles.td, textAlign: "right", fontFamily: "Consolas, monospace" }}>{sc.max}</td>
                <td style={{ ...styles.td, textAlign: "right" }}>
                  <span style={styles.pill(st)}>{fmtPct(sc.pct)}</span>
                </td>
                <td style={{ ...styles.td, textAlign: "center", fontFamily: "Consolas, monospace" }}>{sc.answered} / {sc.count}</td>
              </tr>
            );
          })}
        </tbody>
      </table>

      {/* Per-domain detail */}
      {(domains || []).map((d) => {
        const domNorms = (norms || []).filter((n) => n.domain_code === d.code);
        const sc = scores.perDomain[d.code] || { total: 0, max: 0, pct: 0 };
        return (
          <div key={d.code} style={{ pageBreakBefore: "always" }}>
            <h1 style={styles.h1}>{d.title_fr}</h1>
            <p style={{ fontStyle: "italic", color: "#6b7280", margin: "0 0 12px" }}>
              Score : <strong>{sc.total} / {sc.max}</strong>  ({fmtPct(sc.pct)})
            </p>
            <table style={styles.table}>
              <thead>
                <tr>
                  <th style={{ ...styles.th, width: 30 }}>N°</th>
                  <th style={styles.th}>Liste de contrôle</th>
                  <th style={{ ...styles.th, width: 110 }}>MOV</th>
                  <th style={{ ...styles.th, width: 120 }}>Notation</th>
                  <th style={{ ...styles.th, width: 60, textAlign: "right" }}>Val.</th>
                  <th style={styles.th}>Observations</th>
                </tr>
              </thead>
              <tbody>
                {domNorms.map((n) => {
                  const r = respByNorm.get(n.id) || {};
                  const valStr = r.rating === "na" ? "n/a" : (r.value != null ? r.value + " / " + (n.max_value || 2) : "—");
                  const cellStatus = r.rating === "na" ? "neutral"
                    : r.value == null ? "neutral"
                    : r.value === Number(n.max_value) ? "ok"
                    : r.value === 0 ? "bad" : "amber";
                  const cellBg = SHADE[cellStatus];
                  return (
                    <tr key={n.id}>
                      <td style={{ ...styles.td, fontFamily: "Consolas, monospace", fontWeight: 600 }}>{n.position}</td>
                      <td style={styles.td}>
                        {n.text_fr}
                        {r.is_priority && <span style={{ color: "#dc2626", fontWeight: 700 }}>  ★</span>}
                      </td>
                      <td style={{ ...styles.td, color: "#6b7280" }}>{n.mov_fr || "—"}</td>
                      <td style={{ ...styles.td, background: cellBg.bg, color: cellBg.fg, fontWeight: 600 }}>
                        {ratingFRLabel(d.code, r.rating)}
                      </td>
                      <td style={{ ...styles.td, textAlign: "right", background: cellBg.bg, color: cellBg.fg, fontWeight: 700, fontFamily: "Consolas, monospace" }}>
                        {valStr}
                      </td>
                      <td style={{ ...styles.td, fontSize: 10 }}>{r.observation || "—"}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        );
      })}

      {/* Action plan */}
      {actionPlan && actionPlan.length > 0 && (
        <div style={{ pageBreakBefore: "always" }}>
          <h1 style={styles.h1}>Plan d'action</h1>
          <table style={styles.table}>
            <thead>
              <tr>
                <th style={{ ...styles.th, width: 40 }}>P</th>
                <th style={{ ...styles.th, width: 60 }}>Domaine</th>
                <th style={{ ...styles.th, width: 50 }}>Norme</th>
                <th style={styles.th}>Action</th>
                <th style={{ ...styles.th, width: 90 }}>Responsable</th>
                <th style={{ ...styles.th, width: 80, textAlign: "center" }}>Échéance</th>
                <th style={{ ...styles.th, width: 60, textAlign: "center" }}>Statut</th>
              </tr>
            </thead>
            <tbody>
              {actionPlan.map((it) => {
                const prSt = it.priority === 1 ? "bad" : it.priority === 2 ? "amber" : "neutral";
                const stSt = it.status === "done" ? "ok" : it.status === "cancelled" ? "neutral" : "amber";
                const stLabel = it.status === "done" ? "Fait" : it.status === "cancelled" ? "Annulé" : "En cours";
                return (
                  <tr key={it.id}>
                    <td style={{ ...styles.td, textAlign: "center" }}>
                      <span style={styles.pill(prSt)}>P{it.priority || 2}</span>
                    </td>
                    <td style={{ ...styles.td, fontFamily: "Consolas, monospace" }}>{it.domain_code || "—"}</td>
                    <td style={{ ...styles.td, fontFamily: "Consolas, monospace" }}>{it.norm_position || "—"}{it.sub_letter ? " " + it.sub_letter : ""}</td>
                    <td style={styles.td}>{it.text || "—"}</td>
                    <td style={styles.td}>{it.owner || "—"}</td>
                    <td style={{ ...styles.td, textAlign: "center" }}>{it.due_date || "—"}</td>
                    <td style={{ ...styles.td, textAlign: "center" }}>
                      <span style={styles.pill(stSt)}>{stLabel}</span>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}

      {/* Footer */}
      <div style={{ marginTop: 40, paddingTop: 16, borderTop: "1px solid #e5e7eb", fontSize: 10, color: "#6b7280", textAlign: "center" }}>
        Document généré automatiquement par MELR — {today}<br />
        Conformité : Outil SAT REFT Africa — Évaluation participative de système S&E
      </div>
    </div>
  );
}

window.AuditSystem = AuditSystem;

// ============================================================================
// AUDIT DES DONNÉES — DVT (Outil de Vérification & d'Amélioration de Données)
// ----------------------------------------------------------------------------
// REFT Africa methodology. One audit per (project, cycle, site).
// Partie A: per-indicator, 3 months of (verified vs reported), variance %.
// Partie B: for indicators with |avg variance| > 5%, root cause + corrective.
// ============================================================================

// Variance % = (reported - verified) / verified × 100. Undefined when
// verified is 0 or null.
function variancePct(verified, reported) {
  const v = Number(verified), r = Number(reported);
  if (!isFinite(v) || !isFinite(r) || v === 0) return null;
  return ((r - v) / v) * 100;
}
function avgVariance(row) {
  const vals = [
    variancePct(row.m1_verified, row.m1_reported),
    variancePct(row.m2_verified, row.m2_reported),
    variancePct(row.m3_verified, row.m3_reported),
  ].filter((v) => v != null);
  if (vals.length === 0) return null;
  return vals.reduce((s, x) => s + x, 0) / vals.length;
}
function dvtStatus(v) {
  if (v == null) return "neutral";
  if (Math.abs(v) <= 5) return "ok";
  if (Math.abs(v) <= 10) return "amber";
  return "bad";
}
function fmtVar(v) {
  if (v == null || !isFinite(v)) return "—";
  return (v > 0 ? "+" : "") + v.toFixed(1) + "%";
}
const DVT_STATUS_COLORS = {
  ok:      { fg: "#15803d", bg: "#dcfce7", border: "#86efac" },
  amber:   { fg: "#a16207", bg: "#fef3c7", border: "#fcd34d" },
  bad:     { fg: "#b91c1c", bg: "#fee2e2", border: "#fca5a5" },
  neutral: { fg: "#374151", bg: "#f3f4f6", border: "#d1d5db" },
  accent:  { fg: "#1e3a8a", bg: "#e0e7ff", border: "#a5b4fc" },
};

function AuditData({ t, lang, isSuperAdmin, actingOrgId, activeOrgId, myOrgId }) {
  const { projects: allProjects } = window.melr.useProjects();
  const LiveBadge = window.melr.LiveBadge;
  // Same effective-org pattern as AuditSystem — narrow the project list
  // so the auto-pick is deterministic and matches the user's current org
  // context (acting-as super-admin > active multi-org > home).
  const effOrg = (isSuperAdmin && actingOrgId) ? actingOrgId : (activeOrgId || myOrgId);
  const projects = (effOrg && allProjects)
    ? allProjects.filter((p) => p.organizationId === effOrg)
    : (allProjects || []);
  const [projectId, setProjectIdRaw] = useStateAU(() => {
    try { return localStorage.getItem("melr.audit-data.projectId") || ""; }
    catch (_) { return ""; }
  });
  const setProjectId = (v) => {
    setProjectIdRaw(v || "");
    try {
      if (v) localStorage.setItem("melr.audit-data.projectId", v);
      else   localStorage.removeItem("melr.audit-data.projectId");
    } catch (_) {}
  };
  const { data: audits, refresh: refreshAudits, realtime } = window.melr.useDvtAudits(projectId);
  const [auditId, setAuditId] = useStateAU("");
  const [tab, setTab] = useStateAU("verification");
  const [busy, setBusy] = useStateAU(false);

  // Auto-pick: prefer persisted projectId if still in filtered list, else
  // fall back to the first available project.
  useEffectAU(() => {
    if (!projects || projects.length === 0) return;
    const stillValid = projectId && projects.some((p) => p.uuid === projectId);
    if (!stillValid) setProjectId(projects[0].uuid);
  }, [projects]);
  useEffectAU(() => {
    if ((!auditId || !audits.find((a) => a.id === auditId)) && audits.length > 0) {
      setAuditId(audits[0].id);
    }
  }, [audits, auditId]);

  const activeAudit = audits.find((a) => a.id === auditId) || null;
  const { data: verifications, refresh: refreshVerif }   = window.melr.useDvtVerifications(auditId);
  const { data: improvements,  refresh: refreshImprov } = window.melr.useDvtImprovements(auditId);
  const { data: sites } = window.melr.useSites(projectId);

  const onCreateAudit = async () => {
    if (!projectId) return;
    const today = new Date();
    const defaultCycle = today.getFullYear() + "-Q" + Math.ceil((today.getMonth() + 1) / 3);
    const inputCycle = window.prompt(lang === "fr" ? "Cycle (ex : 2025-Q2) :" : "Cycle (e.g. 2025-Q2):", defaultCycle);
    if (!inputCycle || !inputCycle.trim()) return;
    const siteList = sites || [];
    const siteName = window.prompt(lang === "fr"
      ? "Site (laisser vide pour saisir le nom librement) :"
      : "Site (leave blank to type name freely):", siteList[0] ? (siteList[0].code + " " + siteList[0].name) : "");
    setBusy(true);
    try {
      const proj = (projects || []).find((p) => p.uuid === projectId);
      // Match the site by name prefix if user typed something
      const matched = siteName ? siteList.find((s) => (s.code + " " + s.name).startsWith(siteName.split(" ")[0])) : null;
      const created = await window.melr.dvtAuditsCrud.create(projectId, {
        cycle: inputCycle.trim(),
        state: "draft",
        site_id: matched ? matched.id : null,
        site_name: matched ? null : (siteName || null),
        country: proj ? proj.countriesFr : null,
        programme_name: proj ? proj.id : null,
      });
      await refreshAudits();
      setAuditId(created.id);
    } catch (e) {
      alert((lang === "fr" ? "Erreur : " : "Error: ") + e.message);
    } finally { setBusy(false); }
  };

  const onDeleteAudit = async () => {
    if (!activeAudit) return;
    const sn = (activeAudit.sites && activeAudit.sites.code) || activeAudit.site_name || "";
    if (!confirm(lang === "fr"
      ? `Supprimer l'audit ${activeAudit.cycle} (${sn}) ? Toutes les données seront perdues.`
      : `Delete audit ${activeAudit.cycle} (${sn})? All data will be lost.`)) return;
    setBusy(true);
    try {
      await window.melr.dvtAuditsCrud.remove(activeAudit.id);
      setAuditId("");
      await refreshAudits();
    } catch (e) { alert(e.message); }
    finally { setBusy(false); }
  };

  // Submit the active DVT audit for validation workflow
  const onSubmitDvtForValidation = async () => {
    if (!activeAudit) return;
    setBusy(true);
    try {
      const proj = (projects || []).find((p) => p.uuid === projectId);
      const siteLbl = activeAudit.sites && activeAudit.sites.code
        ? activeAudit.sites.code
        : (activeAudit.site_name || "—");
      const result = await window.melr.submitForValidation({
        object_type: "dvt_audit",
        object_id: activeAudit.id,
        project_id: projectId,
        title: "DVT " + activeAudit.cycle + " · " + siteLbl + " — " + (proj ? proj.id : "?"),
        total_steps: 3,
        sla_days: 3,
        priority: "normal",
      });
      await window.melr.dvtAuditsCrud.update(activeAudit.id, { state: "in_review" });
      await refreshAudits();
      alert(lang === "fr"
        ? (result.reused
            ? "Une validation est déjà en cours pour cet audit (réutilisée)."
            : "Audit soumis pour validation. Suivez-le dans le module Workflow.")
        : (result.reused
            ? "A validation is already in flight (reused)."
            : "Audit submitted for validation."));
    } catch (e) {
      alert((lang === "fr" ? "Erreur : " : "Error: ") + e.message);
    } finally { setBusy(false); }
  };

  // Compute live aggregates
  const enriched = (verifications || []).map((r) => ({
    ...r,
    m1_var: variancePct(r.m1_verified, r.m1_reported),
    m2_var: variancePct(r.m2_verified, r.m2_reported),
    m3_var: variancePct(r.m3_verified, r.m3_reported),
    avg_var: avgVariance(r),
  }));
  const measured = enriched.filter((r) => r.avg_var != null);
  const over5    = measured.filter((r) => Math.abs(r.avg_var) > 5);
  const meanAbsVar = measured.length > 0
    ? measured.reduce((s, r) => s + Math.abs(r.avg_var), 0) / measured.length
    : 0;

  // Excel DVT import + export refs
  const fileInputRef = React.useRef(null);
  const onImportClick = () => {
    if (!activeAudit) return alert(lang === "fr" ? "Sélectionnez ou créez d'abord un audit." : "Pick or create an audit first.");
    if (!window.dvtImporter || !window.dvtImporter.available) return alert(lang === "fr" ? "Librairie XLSX indisponible." : "XLSX library unavailable.");
    fileInputRef.current && fileInputRef.current.click();
  };
  const onFilePicked = async (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    e.target.value = "";
    setBusy(true);
    try {
      const parsed = await window.dvtImporter.importDvtFromFile(file);
      const totalV = parsed.verifications.length;
      const totalI = parsed.improvements.length;
      const ok = confirm(
        (lang === "fr"
          ? "Import détecté : " + totalV + " indicateurs (vérification) · " + totalI + " items (amélioration).\n"
          : "Import detected: " + totalV + " indicators · " + totalI + " improvements.\n") +
        "\n" + (lang === "fr"
          ? "Appliquer à l'audit '" + activeAudit.cycle + "' ?\n(Les entrées existantes aux mêmes positions seront remplacées.)"
          : "Apply to audit '" + activeAudit.cycle + "'?\n(Existing entries at the same positions will be replaced.)")
      );
      if (!ok) return;
      const result = await window.dvtImporter.applyImport(parsed, activeAudit);
      await refreshAudits();
      await refreshVerif();
      await refreshImprov();
      alert(lang === "fr"
        ? "Import terminé : " + result.verif + " vérifications · " + result.improv + " améliorations."
        : "Import done: " + result.verif + " verifications · " + result.improv + " improvements.");
    } catch (e) {
      console.error("[DVT import]", e); alert(e.message);
    } finally { setBusy(false); }
  };

  const [pdfOpen, setPdfOpen] = useStateAU(false);
  const exportPayload = () => ({
    project: (projects || []).find((p) => p.uuid === projectId),
    audit: activeAudit,
    verifications: enriched,
    improvements,
    aggregates: { measuredCount: measured.length, over5pctCount: over5.length, meanAbsVariance: meanAbsVar },
    lang,
  });
  const onExportXlsx = () => {
    if (!activeAudit) return alert(lang === "fr" ? "Aucun audit actif." : "No active audit.");
    if (!window.dvtExporter || !window.dvtExporter.xlsxAvailable) return alert(lang === "fr" ? "Librairie XLSX indisponible." : "XLSX library unavailable.");
    window.dvtExporter.exportDvtToXlsx(exportPayload());
  };
  const onExportDocx = () => {
    if (!activeAudit) return alert(lang === "fr" ? "Aucun audit actif." : "No active audit.");
    if (!window.dvtExporter || !window.dvtExporter.docxAvailable) return alert(lang === "fr" ? "Librairie docx indisponible." : "docx library unavailable.");
    window.dvtExporter.exportDvtToDocx(exportPayload());
  };

  const siteLabel = (a) => {
    if (!a) return "—";
    if (a.sites && a.sites.code) return a.sites.code + " — " + (a.sites.name || "");
    if (a.site_name) return a.site_name;
    return lang === "fr" ? "(site non lié)" : "(unlinked site)";
  };

  return (
    <div className="page">
      <div className="page-header">
        <div className="page-eyebrow">{lang === "fr" ? "MODULE / AUDIT DES DONNÉES (DVT)" : "MODULE / DATA AUDIT (DVT)"}</div>
        <div className="page-header-row">
          <div>
            <h1 className="page-title">{t("nav.audit_data")} <LiveBadge on={realtime} lang={lang} /></h1>
            <div className="page-sub">
              {lang === "fr"
                ? "Outil DVT REFT Africa · vérification (recalcul source ↔ rapport sur 3 mois) + amélioration (causes racines ; actions correctives) · seuil ±5%"
                : "REFT Africa DVT tool · verification (source ↔ report on 3 months) + improvement (root causes ; corrective actions) · ±5% threshold"}
            </div>
          </div>
          <div className="page-header-actions">
            <button className="btn sm" onClick={onImportClick} disabled={!activeAudit || busy}
              title={lang === "fr" ? "Importer un classeur DVT rempli (template REFT Africa)" : "Import a filled DVT workbook"}>
              <Icon.upload /> {lang === "fr" ? "Importer Excel" : "Import Excel"}
            </button>
            <input ref={fileInputRef} type="file" accept=".xlsx" onChange={onFilePicked} style={{ display: "none" }} />
            <button className="btn sm" onClick={onExportXlsx} disabled={!activeAudit}
              title={lang === "fr" ? "Exporter au format Excel (template REFT Africa)" : "Export as Excel"}>
              <Icon.download /> Excel
            </button>
            <button className="btn sm" onClick={onExportDocx} disabled={!activeAudit}
              title={lang === "fr" ? "Exporter au format Word" : "Export as Word"}>
              <Icon.download /> Word
            </button>
            <button className="btn sm primary" onClick={() => setPdfOpen(true)} disabled={!activeAudit}
              title={lang === "fr" ? "Aperçu PDF imprimable" : "Printable PDF preview"}>
              <Icon.download /> PDF
            </button>
          </div>
        </div>
      </div>

      <div className="page-body">
        {/* Project + cycle + site selector */}
        <div className="card" style={{ marginBottom: 14 }}>
          <div className="card-body" style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", padding: "10px 14px" }}>
            <span className="text-faint" style={{ fontSize: 11.5, textTransform: "uppercase", letterSpacing: "0.04em" }}>
              {lang === "fr" ? "Projet" : "Project"}
            </span>
            <select value={projectId} onChange={(e) => { setProjectId(e.target.value); setAuditId(""); }}
              style={{ padding: "6px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, background: "var(--bg, white)", color: "var(--text)", minWidth: 260 }}>
              <option value="">{lang === "fr" ? "— choisir —" : "— pick —"}</option>
              {(projects || []).map((p) => (
                <option key={p.uuid} value={p.uuid}>{p.id} — {lang === "fr" ? p.nameFr : p.nameEn}</option>
              ))}
            </select>

            <span className="text-faint" style={{ fontSize: 11.5, textTransform: "uppercase", letterSpacing: "0.04em", marginLeft: 8 }}>
              {lang === "fr" ? "Audit (cycle / site)" : "Audit (cycle / site)"}
            </span>
            <select value={auditId} onChange={(e) => setAuditId(e.target.value)} disabled={!projectId}
              style={{ padding: "6px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, background: "var(--bg, white)", color: "var(--text)", minWidth: 240 }}>
              <option value="">{lang === "fr" ? "— choisir —" : "— pick —"}</option>
              {(audits || []).map((a) => (
                <option key={a.id} value={a.id}>{a.cycle} · {siteLabel(a)}</option>
              ))}
            </select>

            <button className="btn sm primary" onClick={onCreateAudit} disabled={!projectId || busy}>
              <Icon.plus /> {lang === "fr" ? "Nouvel audit" : "New audit"}
            </button>
            {activeAudit && (
              <button className="btn sm" onClick={onSubmitDvtForValidation}
                disabled={busy || activeAudit.state === "in_review" || activeAudit.state === "approved"}
                title={lang === "fr"
                  ? "Soumettre cet audit pour validation"
                  : "Submit this audit for approval"}>
                <Icon.send /> {lang === "fr" ? "Soumettre" : "Submit"}
              </button>
            )}
            {activeAudit && (
              <button className="btn sm" onClick={onDeleteAudit} disabled={busy}
                title={lang === "fr" ? "Supprimer l'audit actif" : "Delete active audit"}>
                <Icon.trash />
              </button>
            )}

            {activeAudit && measured.length > 0 && (
              <div style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 10 }}>
                <span className="text-faint" style={{ fontSize: 11 }}>
                  {lang === "fr" ? "Variance moyenne |%|" : "Mean |variance %|"}
                </span>
                <span className="tag-mono" style={{ fontSize: 14, fontWeight: 600 }}>
                  {meanAbsVar.toFixed(1)}%
                </span>
                <span className={"pill " + dvtStatus(meanAbsVar)}>
                  {over5.length} {lang === "fr" ? "hors seuil" : "off-target"}
                </span>
              </div>
            )}
          </div>
        </div>

        {!auditId ? (
          <div className="card"><div className="card-body" style={{ padding: 50, textAlign: "center", color: "var(--text-faint)" }}>
            {projectId
              ? (lang === "fr" ? "Aucun audit pour ce projet. Cliquez « Nouvel audit »." : "No audit for this project. Click 'New audit'.")
              : (lang === "fr" ? "Sélectionnez un projet pour commencer." : "Pick a project to start.")}
          </div></div>
        ) : (
          <>
            {/* Tabs */}
            <div className="page-tabs" style={{ marginTop: 0 }}>
              {[
                { k: "verification", l: lang === "fr" ? "A. Vérification" : "A. Verification", n: enriched.length },
                { k: "improvement",  l: lang === "fr" ? "B. Amélioration" : "B. Improvement",  n: improvements.length },
                { k: "synthese",     l: lang === "fr" ? "Synthèse" : "Summary" },
                { k: "meta",         l: lang === "fr" ? "Métadonnées" : "Metadata" },
              ].map((x) => (
                <div key={x.k} className={"tab" + (x.k === tab ? " active" : "")} onClick={() => setTab(x.k)}>
                  {x.l}{x.n != null && <span className="tag-mono" style={{ marginLeft: 4 }}>{x.n}</span>}
                </div>
              ))}
            </div>

            {tab === "verification" && (
              <DvtVerificationTab lang={lang} audit={activeAudit}
                rows={enriched} refreshVerif={refreshVerif} />
            )}
            {tab === "improvement" && (
              <DvtImprovementTab lang={lang} audit={activeAudit}
                rows={improvements} verifications={enriched} refreshImprov={refreshImprov} />
            )}
            {tab === "synthese" && (
              <DvtSyntheseTab lang={lang} audit={activeAudit}
                verifications={enriched} improvements={improvements}
                aggregates={{ measuredCount: measured.length, over5pctCount: over5.length, meanAbsVariance: meanAbsVar }} />
            )}
            {tab === "meta" && (
              <DvtMetaTab lang={lang} audit={activeAudit} sites={sites || []}
                onRefresh={refreshAudits} />
            )}
          </>
        )}
      </div>

      {pdfOpen && (
        <DvtPdfModal lang={lang} onClose={() => setPdfOpen(false)}
          {...exportPayload()} />
      )}
    </div>
  );
}

// ------------------------------------------------------------------------
// VERIFICATION TAB — 20 editable rows for indicators × 3 months
// ------------------------------------------------------------------------
function DvtVerificationTab({ lang, audit, rows, refreshVerif }) {
  const MAX = 20;
  const [busyRow, setBusyRow] = useStateAU(null);
  const [err, setErr] = useStateAU(null);

  // Build a display array of MAX rows; existing rows + empty slots
  const byPos = new Map();
  (rows || []).forEach((r) => byPos.set(r.position, r));
  const display = [];
  for (let i = 1; i <= MAX; i++) {
    const r = byPos.get(i);
    display.push(r || { position: i, indicator_name: "", doc_source: "" });
  }

  const onChange = async (position, patch) => {
    setBusyRow(position); setErr(null);
    try {
      await window.melr.dvtVerificationsCrud.upsert(audit.id, position, patch);
    } catch (e) { setErr(e.message); }
    finally { setBusyRow(null); }
  };

  const onDeleteRow = async (id) => {
    if (!confirm(lang === "fr" ? "Effacer cette ligne ?" : "Clear this row?")) return;
    try { await window.melr.dvtVerificationsCrud.remove(id); }
    catch (e) { setErr(e.message); }
  };

  const inp = { width: "100%", padding: "4px 6px", borderRadius: 4, border: "1px solid var(--line)", fontSize: 11, background: "var(--bg, white)", color: "var(--text)", boxSizing: "border-box" };

  return (
    <div className="card">
      <div className="card-head">
        <div className="card-title">{lang === "fr" ? "A. Vérification des données" : "A. Data verification"}</div>
        <span className="pill">{rows.length} {lang === "fr" ? "indicateurs renseignés" : "indicators filled"}</span>
        <span className="text-faint" style={{ marginLeft: "auto", fontSize: 11 }}>
          {lang === "fr" ? "Variance % = (Rapportée − Vérifiée) / Vérifiée × 100  ·  seuil ±5%" : "Variance % = (Reported − Verified) / Verified × 100  ·  ±5% threshold"}
        </span>
      </div>
      {err && <div style={{ padding: "8px 14px", color: "#b91c1c", fontSize: 12 }}>{err}</div>}
      <div className="card-body flush" style={{ overflowX: "auto" }}>
        <table className="tbl" style={{ minWidth: 1200, fontSize: 11 }}>
          <thead>
            <tr>
              <th rowSpan={2} style={{ width: 30 }}>#</th>
              <th rowSpan={2} style={{ minWidth: 200 }}>{lang === "fr" ? "Indicateur" : "Indicator"}</th>
              <th rowSpan={2} style={{ minWidth: 150 }}>{lang === "fr" ? "Doc-source" : "Doc-source"}</th>
              <th colSpan={3} style={{ textAlign: "center", background: "#f0f9ff" }}>{lang === "fr" ? "Mois 1" : "Month 1"}</th>
              <th colSpan={3} style={{ textAlign: "center", background: "#f0fdf4" }}>{lang === "fr" ? "Mois 2" : "Month 2"}</th>
              <th colSpan={3} style={{ textAlign: "center", background: "#fef3c7" }}>{lang === "fr" ? "Mois 3" : "Month 3"}</th>
              <th rowSpan={2} className="num" style={{ minWidth: 80 }}>{lang === "fr" ? "Moy. Var" : "Avg Var"}</th>
              <th rowSpan={2} style={{ width: 40 }}></th>
            </tr>
            <tr>
              <th className="num">{lang === "fr" ? "Vérif." : "Verified"}</th>
              <th className="num">{lang === "fr" ? "Rapp." : "Reported"}</th>
              <th className="num">Var %</th>
              <th className="num">{lang === "fr" ? "Vérif." : "Verified"}</th>
              <th className="num">{lang === "fr" ? "Rapp." : "Reported"}</th>
              <th className="num">Var %</th>
              <th className="num">{lang === "fr" ? "Vérif." : "Verified"}</th>
              <th className="num">{lang === "fr" ? "Rapp." : "Reported"}</th>
              <th className="num">Var %</th>
            </tr>
          </thead>
          <tbody>
            {display.map((r) => {
              const isBusy = busyRow === r.position;
              const m1var = variancePct(r.m1_verified, r.m1_reported);
              const m2var = variancePct(r.m2_verified, r.m2_reported);
              const m3var = variancePct(r.m3_verified, r.m3_reported);
              const av = avgVariance(r);
              const av_status = dvtStatus(av);
              return (
                <tr key={r.position}>
                  <td className="tag-mono" style={{ textAlign: "center" }}>{r.position}</td>
                  <td>
                    <input style={inp} type="text" defaultValue={r.indicator_name || ""}
                      disabled={isBusy}
                      onBlur={(e) => { if (e.target.value !== (r.indicator_name || "")) onChange(r.position, { indicator_name: e.target.value || null }); }} />
                  </td>
                  <td>
                    <input style={inp} type="text" defaultValue={r.doc_source || ""}
                      disabled={isBusy}
                      onBlur={(e) => { if (e.target.value !== (r.doc_source || "")) onChange(r.position, { doc_source: e.target.value || null }); }} />
                  </td>
                  <td><input style={inp} type="number" step="any" defaultValue={r.m1_verified == null ? "" : r.m1_verified}
                    disabled={isBusy}
                    onBlur={(e) => onChange(r.position, { m1_verified: e.target.value === "" ? null : parseFloat(e.target.value) })} /></td>
                  <td><input style={inp} type="number" step="any" defaultValue={r.m1_reported == null ? "" : r.m1_reported}
                    disabled={isBusy}
                    onBlur={(e) => onChange(r.position, { m1_reported: e.target.value === "" ? null : parseFloat(e.target.value) })} /></td>
                  <td className="num" style={{ ...(m1var != null ? { background: DVT_STATUS_COLORS[dvtStatus(m1var)].bg, color: DVT_STATUS_COLORS[dvtStatus(m1var)].fg, fontWeight: 600 } : {}) }}>{fmtVar(m1var)}</td>
                  <td><input style={inp} type="number" step="any" defaultValue={r.m2_verified == null ? "" : r.m2_verified}
                    disabled={isBusy}
                    onBlur={(e) => onChange(r.position, { m2_verified: e.target.value === "" ? null : parseFloat(e.target.value) })} /></td>
                  <td><input style={inp} type="number" step="any" defaultValue={r.m2_reported == null ? "" : r.m2_reported}
                    disabled={isBusy}
                    onBlur={(e) => onChange(r.position, { m2_reported: e.target.value === "" ? null : parseFloat(e.target.value) })} /></td>
                  <td className="num" style={{ ...(m2var != null ? { background: DVT_STATUS_COLORS[dvtStatus(m2var)].bg, color: DVT_STATUS_COLORS[dvtStatus(m2var)].fg, fontWeight: 600 } : {}) }}>{fmtVar(m2var)}</td>
                  <td><input style={inp} type="number" step="any" defaultValue={r.m3_verified == null ? "" : r.m3_verified}
                    disabled={isBusy}
                    onBlur={(e) => onChange(r.position, { m3_verified: e.target.value === "" ? null : parseFloat(e.target.value) })} /></td>
                  <td><input style={inp} type="number" step="any" defaultValue={r.m3_reported == null ? "" : r.m3_reported}
                    disabled={isBusy}
                    onBlur={(e) => onChange(r.position, { m3_reported: e.target.value === "" ? null : parseFloat(e.target.value) })} /></td>
                  <td className="num" style={{ ...(m3var != null ? { background: DVT_STATUS_COLORS[dvtStatus(m3var)].bg, color: DVT_STATUS_COLORS[dvtStatus(m3var)].fg, fontWeight: 600 } : {}) }}>{fmtVar(m3var)}</td>
                  <td className="num" style={{ ...(av != null ? { background: DVT_STATUS_COLORS[av_status].bg, color: DVT_STATUS_COLORS[av_status].fg, fontWeight: 700 } : {}) }}>{fmtVar(av)}</td>
                  <td>
                    {r.id && <button className="btn sm ghost" onClick={() => onDeleteRow(r.id)} title={lang === "fr" ? "Effacer" : "Clear"}><Icon.trash /></button>}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      <div className="text-faint" style={{ padding: "8px 14px", fontSize: 11, borderTop: "1px solid var(--line-faint)" }}>
        {lang === "fr" ? "Astuce : la valeur d'une cellule s'enregistre à la sortie du champ (Tab ou clic ailleurs). Cellules colorées vertes ≤5%, ambre 5-10%, rouge >10%." : "Tip: cell values save on blur (Tab or click elsewhere). Cells green ≤5%, amber 5-10%, red >10%."}
      </div>
    </div>
  );
}

// ------------------------------------------------------------------------
// IMPROVEMENT TAB — list of indicators with variance > 5% + corrective actions
// ------------------------------------------------------------------------
function DvtImprovementTab({ lang, audit, rows, verifications, refreshImprov }) {
  const [busyRow, setBusyRow] = useStateAU(null);
  const [err, setErr] = useStateAU(null);
  const MAX = 16;
  const byPos = new Map();
  (rows || []).forEach((r) => byPos.set(r.position, r));
  const display = [];
  for (let i = 1; i <= MAX; i++) {
    display.push(byPos.get(i) || { position: i });
  }

  // Suggestions: indicators in verification with |avg variance| > 5%
  const suggestions = verifications.filter((v) => v.avg_var != null && Math.abs(v.avg_var) > 5);

  const onChange = async (position, patch) => {
    setBusyRow(position); setErr(null);
    try {
      await window.melr.dvtImprovementsCrud.upsert(audit.id, position, patch);
    } catch (e) { setErr(e.message); }
    finally { setBusyRow(null); }
  };
  const onDeleteRow = async (id) => {
    if (!confirm(lang === "fr" ? "Effacer cette ligne ?" : "Clear this row?")) return;
    try { await window.melr.dvtImprovementsCrud.remove(id); }
    catch (e) { setErr(e.message); }
  };
  const onSuggestionAdd = async (sug) => {
    // Find next empty slot
    const next = display.find((d) => !d.indicator_name);
    if (!next) return alert(lang === "fr" ? "Toutes les lignes sont déjà occupées." : "All rows already filled.");
    try {
      await window.melr.dvtImprovementsCrud.upsert(audit.id, next.position, {
        indicator_name: sug.indicator_name || "Indicateur " + sug.position,
        problem: lang === "fr"
          ? "Variance moyenne " + fmtVar(sug.avg_var) + " (seuil ±5%)"
          : "Mean variance " + fmtVar(sug.avg_var) + " (±5% threshold)",
      });
    } catch (e) { setErr(e.message); }
  };

  const inp = { width: "100%", padding: "4px 6px", borderRadius: 4, border: "1px solid var(--line)", fontSize: 11, background: "var(--bg, white)", color: "var(--text)", boxSizing: "border-box", fontFamily: "inherit" };

  return (
    <>
      {suggestions.length > 0 && (
        <div className="card" style={{ marginBottom: 12, background: "#fef3c7", borderColor: "#d97706" }}>
          <div className="card-head" style={{ gap: 8 }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: "#d97706" }}></span>
            <div className="card-title" style={{ color: "#92400e" }}>
              {lang === "fr"
                ? suggestions.length + " indicateur(s) hors seuil ±5%"
                : suggestions.length + " indicator(s) off-threshold ±5%"}
            </div>
          </div>
          <div className="card-body" style={{ paddingTop: 4, paddingBottom: 10, display: "flex", flexWrap: "wrap", gap: 6 }}>
            {suggestions.map((s) => (
              <button key={s.id || s.position} className="btn sm" onClick={() => onSuggestionAdd(s)}
                style={{ background: "white" }}
                title={lang === "fr" ? "Ajouter cet indicateur au plan d'amélioration" : "Add this indicator to the improvement plan"}>
                <Icon.plus className="sm" /> #{s.position} · {s.indicator_name ? s.indicator_name.slice(0, 24) : (lang === "fr" ? "(sans nom)" : "(unnamed)")} ({fmtVar(s.avg_var)})
              </button>
            ))}
          </div>
        </div>
      )}

      <div className="card">
        <div className="card-head">
          <div className="card-title">{lang === "fr" ? "B. Amélioration des données" : "B. Data improvement"}</div>
          <span className="pill">{rows.length} {lang === "fr" ? "entrées" : "entries"}</span>
        </div>
        {err && <div style={{ padding: "8px 14px", color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div className="card-body flush" style={{ overflowX: "auto" }}>
          <table className="tbl" style={{ minWidth: 1100, fontSize: 11 }}>
            <thead>
              <tr>
                <th style={{ width: 30 }}>#</th>
                <th style={{ minWidth: 180 }}>{lang === "fr" ? "Indicateur (variance > ±5%)" : "Indicator (variance > ±5%)"}</th>
                <th style={{ minWidth: 200 }}>{lang === "fr" ? "Problème identifié" : "Problem identified"}</th>
                <th style={{ minWidth: 200 }}>{lang === "fr" ? "Mesure corrective" : "Corrective action"}</th>
                <th style={{ minWidth: 180 }}>{lang === "fr" ? "Étapes de suivi" : "Follow-up steps"}</th>
                <th style={{ width: 110 }}>{lang === "fr" ? "Échéance" : "Due"}</th>
                <th style={{ minWidth: 120 }}>{lang === "fr" ? "Responsable" : "Responsible"}</th>
                <th style={{ width: 40 }}></th>
              </tr>
            </thead>
            <tbody>
              {display.map((r) => {
                const isBusy = busyRow === r.position;
                return (
                  <tr key={r.position}>
                    <td className="tag-mono" style={{ textAlign: "center" }}>{r.position}</td>
                    <td>
                      <input style={inp} type="text" defaultValue={r.indicator_name || ""}
                        disabled={isBusy}
                        onBlur={(e) => { if (e.target.value !== (r.indicator_name || "")) onChange(r.position, { indicator_name: e.target.value || null }); }} />
                    </td>
                    <td>
                      <textarea style={{ ...inp, minHeight: 28, resize: "vertical" }} defaultValue={r.problem || ""}
                        disabled={isBusy}
                        onBlur={(e) => { if (e.target.value !== (r.problem || "")) onChange(r.position, { problem: e.target.value || null }); }} />
                    </td>
                    <td>
                      <textarea style={{ ...inp, minHeight: 28, resize: "vertical" }} defaultValue={r.corrective_action || ""}
                        disabled={isBusy}
                        onBlur={(e) => { if (e.target.value !== (r.corrective_action || "")) onChange(r.position, { corrective_action: e.target.value || null }); }} />
                    </td>
                    <td>
                      <textarea style={{ ...inp, minHeight: 28, resize: "vertical" }} defaultValue={r.followup_steps || ""}
                        disabled={isBusy}
                        onBlur={(e) => { if (e.target.value !== (r.followup_steps || "")) onChange(r.position, { followup_steps: e.target.value || null }); }} />
                    </td>
                    <td>
                      <input style={inp} type="date" defaultValue={r.due_date || ""}
                        disabled={isBusy}
                        onBlur={(e) => onChange(r.position, { due_date: e.target.value || null })} />
                    </td>
                    <td>
                      <input style={inp} type="text" defaultValue={r.responsible || ""}
                        disabled={isBusy}
                        onBlur={(e) => { if (e.target.value !== (r.responsible || "")) onChange(r.position, { responsible: e.target.value || null }); }} />
                    </td>
                    <td>
                      {r.id && <button className="btn sm ghost" onClick={() => onDeleteRow(r.id)}><Icon.trash /></button>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </>
  );
}

// ------------------------------------------------------------------------
// SYNTHESE TAB
// ------------------------------------------------------------------------
function DvtSyntheseTab({ lang, audit, verifications, improvements, aggregates }) {
  const measured = verifications.filter((v) => v.avg_var != null);
  const sorted = [...measured].sort((a, b) => Math.abs(b.avg_var || 0) - Math.abs(a.avg_var || 0));
  const overall = aggregates.measuredCount > 0
    ? Math.max(0, 1 - aggregates.over5pctCount / aggregates.measuredCount)
    : 0;
  const Tile = ({ label, value, sub, status }) => {
    const c = DVT_STATUS_COLORS[status || "neutral"];
    return (
      <div style={{ border: "1px solid " + c.border, background: c.bg, color: c.fg, borderRadius: 6, padding: "10px 12px", minHeight: 70 }}>
        <div style={{ fontSize: 9.5, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em", opacity: 0.8 }}>{label}</div>
        <div style={{ fontSize: 20, fontWeight: 700, fontFamily: "Consolas, monospace", marginTop: 4 }}>{value}</div>
        {sub && <div style={{ fontSize: 10, opacity: 0.75, marginTop: 2 }}>{sub}</div>}
      </div>
    );
  };
  return (
    <>
      <div className="grid" style={{ gridTemplateColumns: "repeat(4, 1fr)", gap: 10, marginBottom: 14, display: "grid" }}>
        <Tile label={lang === "fr" ? "Indicateurs vérifiés" : "Verified indicators"} value={aggregates.measuredCount} sub={lang === "fr" ? "sur 20 slots" : "of 20 slots"} status="accent" />
        <Tile label={lang === "fr" ? "Variance moyenne |%|" : "Mean |variance %|"} value={aggregates.meanAbsVariance.toFixed(1) + "%"} sub={lang === "fr" ? "seuil ±5%" : "±5% threshold"} status={dvtStatus(aggregates.meanAbsVariance)} />
        <Tile label={lang === "fr" ? "Hors seuil" : "Off-threshold"} value={aggregates.over5pctCount} sub={aggregates.measuredCount > 0 ? Math.round(aggregates.over5pctCount / aggregates.measuredCount * 100) + "%" : "—"} status={aggregates.over5pctCount > 0 ? "bad" : "ok"} />
        <Tile label={lang === "fr" ? "Plan d'amélioration" : "Improvement plan"} value={improvements.length} sub={lang === "fr" ? "actions saisies" : "actions filled"} status="amber" />
      </div>

      <div className="card">
        <div className="card-head">
          <div className="card-title">{lang === "fr" ? "Indicateurs classés par variance absolue" : "Indicators ranked by absolute variance"}</div>
        </div>
        <div className="card-body flush">
          {sorted.length === 0 && (
            <div style={{ padding: 22, textAlign: "center", color: "var(--text-faint)", fontSize: 13 }}>
              {lang === "fr" ? "Aucun indicateur vérifié pour cet audit." : "No indicator verified for this audit."}
            </div>
          )}
          {sorted.map((r) => {
            const st = dvtStatus(r.avg_var);
            const c = DVT_STATUS_COLORS[st];
            return (
              <div key={r.id} style={{ padding: "10px 16px", borderBottom: "1px solid var(--line-faint)", display: "grid", gridTemplateColumns: "30px 1fr auto auto auto auto", gap: 8, alignItems: "center" }}>
                <span className="tag-mono">#{r.position}</span>
                <div>
                  <div style={{ fontWeight: 500, fontSize: 12.5 }}>{r.indicator_name || (lang === "fr" ? "(sans nom)" : "(unnamed)")}</div>
                  {r.doc_source && <div className="text-faint" style={{ fontSize: 10.5 }}>{r.doc_source}</div>}
                </div>
                <span className="tag-mono">M1 {fmtVar(r.m1_var)}</span>
                <span className="tag-mono">M2 {fmtVar(r.m2_var)}</span>
                <span className="tag-mono">M3 {fmtVar(r.m3_var)}</span>
                <span style={{ padding: "3px 10px", borderRadius: 999, background: c.bg, color: c.fg, border: "1px solid " + c.border, fontSize: 11, fontWeight: 700 }}>
                  Moy. {fmtVar(r.avg_var)}
                </span>
              </div>
            );
          })}
        </div>
      </div>
    </>
  );
}

// ------------------------------------------------------------------------
// META TAB — edit audit metadata
// ------------------------------------------------------------------------
function DvtMetaTab({ lang, audit, sites, onRefresh }) {
  const [form, setForm]   = useStateAU({ ...audit });
  const [busy, setBusy]   = useStateAU(false);
  const [err, setErr]     = useStateAU(null);
  const [saved, setSaved] = useStateAU(false);

  // Sync form on audit change
  useEffectAU(() => { setForm({ ...audit }); setSaved(false); }, [audit && audit.id]);

  const f = (k) => form[k] == null ? "" : form[k];
  const setF = (k, v) => { setForm((s) => ({ ...s, [k]: v })); setSaved(false); };

  const onSave = async () => {
    setBusy(true); setErr(null);
    try {
      const patch = {};
      [
        "cycle", "country", "region", "department", "district", "site_id", "site_name",
        "programme_name", "period_audited", "team_lead", "staff_1", "staff_2", "staff_3",
        "site_team_lead", "site_staff_1", "site_staff_2", "site_staff_3",
        "other_participants", "comments_verification", "comments_improvement",
      ].forEach((k) => { patch[k] = form[k] == null || form[k] === "" ? null : form[k]; });
      await window.melr.dvtAuditsCrud.update(audit.id, patch);
      await onRefresh();
      setSaved(true);
    } catch (e) { setErr(e.message); }
    finally { setBusy(false); }
  };

  const inp = { width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 12.5, background: "var(--bg, white)", color: "var(--text)", boxSizing: "border-box" };
  const lbl = { fontSize: 10.5, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: 4 };

  return (
    <div className="card">
      <div className="card-head">
        <div className="card-title">{lang === "fr" ? "Métadonnées de l'audit" : "Audit metadata"}</div>
        <div style={{ flex: 1 }} />
        {err && <span style={{ color: "#b91c1c", fontSize: 12 }}>{err}</span>}
        <button className="btn sm primary" onClick={onSave} disabled={busy}>
          {busy ? "…" : (saved ? (lang === "fr" ? "✓ Enregistré" : "✓ Saved") : (lang === "fr" ? "Enregistrer" : "Save"))}
        </button>
      </div>
      <div className="card-body" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
        <div><div style={lbl}>{lang === "fr" ? "Cycle" : "Cycle"}</div><input style={inp} value={f("cycle")} onChange={(e) => setF("cycle", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Période auditée" : "Period audited"}</div><input style={inp} value={f("period_audited")} onChange={(e) => setF("period_audited", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Pays" : "Country"}</div><input style={inp} value={f("country")} onChange={(e) => setF("country", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Région" : "Region"}</div><input style={inp} value={f("region")} onChange={(e) => setF("region", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Département/Unité" : "Department/Unit"}</div><input style={inp} value={f("department")} onChange={(e) => setF("department", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "District sanitaire" : "Health district"}</div><input style={inp} value={f("district")} onChange={(e) => setF("district", e.target.value)} /></div>
        <div>
          <div style={lbl}>{lang === "fr" ? "Site (lié)" : "Site (linked)"}</div>
          <select style={inp} value={f("site_id")} onChange={(e) => setF("site_id", e.target.value || null)}>
            <option value="">{lang === "fr" ? "— aucun (texte libre ci-dessous) —" : "— none (free text below) —"}</option>
            {sites.map((s) => <option key={s.id} value={s.id}>{s.code} — {s.name}</option>)}
          </select>
        </div>
        <div><div style={lbl}>{lang === "fr" ? "Nom du site (texte libre)" : "Site name (free text)"}</div><input style={inp} value={f("site_name")} onChange={(e) => setF("site_name", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Programme/Projet" : "Programme/Project"}</div><input style={inp} value={f("programme_name")} onChange={(e) => setF("programme_name", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Chef d'équipe (programme)" : "Team lead (programme)"}</div><input style={inp} value={f("team_lead")} onChange={(e) => setF("team_lead", e.target.value)} /></div>
        <div><div style={lbl}>Staff #1</div><input style={inp} value={f("staff_1")} onChange={(e) => setF("staff_1", e.target.value)} /></div>
        <div><div style={lbl}>Staff #2</div><input style={inp} value={f("staff_2")} onChange={(e) => setF("staff_2", e.target.value)} /></div>
        <div><div style={lbl}>Staff #3</div><input style={inp} value={f("staff_3")} onChange={(e) => setF("staff_3", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Chef d'équipe site" : "Site team lead"}</div><input style={inp} value={f("site_team_lead")} onChange={(e) => setF("site_team_lead", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Staff du site #1" : "Site staff #1"}</div><input style={inp} value={f("site_staff_1")} onChange={(e) => setF("site_staff_1", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Staff du site #2" : "Site staff #2"}</div><input style={inp} value={f("site_staff_2")} onChange={(e) => setF("site_staff_2", e.target.value)} /></div>
        <div><div style={lbl}>{lang === "fr" ? "Staff du site #3" : "Site staff #3"}</div><input style={inp} value={f("site_staff_3")} onChange={(e) => setF("site_staff_3", e.target.value)} /></div>
        <div style={{ gridColumn: "span 2" }}><div style={lbl}>{lang === "fr" ? "Autres participants" : "Other participants"}</div><textarea style={{ ...inp, minHeight: 40, resize: "vertical", fontFamily: "inherit" }} value={f("other_participants")} onChange={(e) => setF("other_participants", e.target.value)} /></div>
        <div style={{ gridColumn: "span 2" }}><div style={lbl}>{lang === "fr" ? "Commentaires (vérification)" : "Comments (verification)"}</div><textarea style={{ ...inp, minHeight: 50, resize: "vertical", fontFamily: "inherit" }} value={f("comments_verification")} onChange={(e) => setF("comments_verification", e.target.value)} /></div>
        <div style={{ gridColumn: "span 2" }}><div style={lbl}>{lang === "fr" ? "Commentaires (amélioration)" : "Comments (improvement)"}</div><textarea style={{ ...inp, minHeight: 50, resize: "vertical", fontFamily: "inherit" }} value={f("comments_improvement")} onChange={(e) => setF("comments_improvement", e.target.value)} /></div>
      </div>
    </div>
  );
}

// PDF preview modal — uses print CSS targeting #dvt-report-printable
function DvtPdfModal({ lang, onClose, project, audit, verifications, improvements, aggregates }) {
  const onPrint = () => window.print();
  return (
    <div className="exante-report-overlay" style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.6)", zIndex: 9999,
      display: "flex", flexDirection: "column",
    }}>
      <div className="exante-report-toolbar" style={{
        padding: "10px 16px", background: "var(--bg, white)", color: "var(--text, #111)",
        borderBottom: "1px solid var(--line)", display: "flex", gap: 10, alignItems: "center",
      }}>
        <div style={{ fontWeight: 600, fontSize: 14 }}>
          {lang === "fr" ? "Aperçu DVT — impression / PDF" : "DVT preview — print / PDF"}
        </div>
        <span className="pill" style={{ marginLeft: 8 }}>
          {(project && project.id) || "—"} · {audit ? audit.cycle : "—"}
        </span>
        <div style={{ flex: 1 }} />
        <button onClick={onPrint} style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
          ↓ {lang === "fr" ? "Imprimer / PDF" : "Print / PDF"}
        </button>
        <button onClick={onClose} style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
          {lang === "fr" ? "Fermer" : "Close"}
        </button>
      </div>
      <div className="exante-report-scroll" style={{ flex: 1, overflow: "auto", background: "#f3f4f6", padding: "24px 0" }}>
        <DvtReportPrintable lang={lang} project={project} audit={audit}
          verifications={verifications} improvements={improvements} aggregates={aggregates} />
      </div>
    </div>
  );
}

function DvtReportPrintable({ lang, project, audit, verifications, improvements, aggregates }) {
  const today = new Date().toLocaleDateString(lang === "fr" ? "fr-FR" : "en-US");
  const styles = {
    body: { fontFamily: "Calibri, Arial, sans-serif", color: "#111", padding: 32, maxWidth: 900, margin: "0 auto", background: "white", fontSize: 11.5 },
    h1: { fontSize: 18, fontWeight: 700, color: "#1f2937", marginTop: 28, marginBottom: 10, borderBottom: "2px solid #1f2937", paddingBottom: 6 },
    table: { width: "100%", borderCollapse: "collapse", marginBottom: 12, fontSize: 10.5 },
    th: { borderBottom: "2px solid #1f2937", textAlign: "left", padding: "5px 6px", fontWeight: 700, background: "#f9fafb", fontSize: 10 },
    td: { borderBottom: "1px solid #e5e7eb", padding: "4px 6px", verticalAlign: "top" },
  };
  const siteLbl = audit && audit.sites && audit.sites.code ? (audit.sites.code + " — " + (audit.sites.name || "")) : (audit && audit.site_name) || "—";
  const sorted = [...verifications].filter((r) => r.avg_var != null).sort((a, b) => Math.abs(b.avg_var) - Math.abs(a.avg_var));

  return (
    <div id="dvt-report-printable" style={styles.body}>
      <div style={{ textAlign: "center", marginBottom: 24, paddingTop: 30 }}>
        <div style={{ fontSize: 10, color: "#666", marginBottom: 8, textTransform: "uppercase", letterSpacing: "0.1em" }}>
          Outil de Vérification & d'Amélioration de Données (DVT) — REFT Africa
        </div>
        <h1 style={{ fontSize: 22, fontWeight: 700, margin: "4px 0" }}>
          {project ? (project.id + " — " + (project.nameFr || project.nameEn)) : "—"}
        </h1>
        <div style={{ fontSize: 12, color: "#555", marginTop: 12, lineHeight: 1.6 }}>
          <strong>Cycle :</strong> {audit ? audit.cycle : "—"}  ·  <strong>Site :</strong> {siteLbl}  ·  <strong>Date :</strong> {today}<br />
          <strong>Pays :</strong> {(audit && audit.country) || "—"}  ·  <strong>Région :</strong> {(audit && audit.region) || "—"}  ·  <strong>District :</strong> {(audit && audit.district) || "—"}<br />
          <strong>Chef d'équipe :</strong> {(audit && audit.team_lead) || "—"}  ·  <strong>Chef équipe site :</strong> {(audit && audit.site_team_lead) || "—"}
        </div>
      </div>

      <h1 style={styles.h1}>I. Synthèse</h1>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 10, marginBottom: 16 }}>
        {[
          { label: "Indicateurs vérifiés", value: aggregates.measuredCount, status: "accent" },
          { label: "Variance moy. |%|", value: aggregates.meanAbsVariance.toFixed(1) + "%", status: dvtStatus(aggregates.meanAbsVariance) },
          { label: "Hors seuil ±5%", value: aggregates.over5pctCount, status: aggregates.over5pctCount > 0 ? "bad" : "ok" },
          { label: "Actions plan", value: improvements.length, status: "amber" },
        ].map((tile, i) => {
          const c = DVT_STATUS_COLORS[tile.status];
          return (
            <div key={i} style={{ border: "1px solid " + c.border, background: c.bg, color: c.fg, borderRadius: 6, padding: "10px 12px" }}>
              <div style={{ fontSize: 9.5, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em" }}>{tile.label}</div>
              <div style={{ fontSize: 20, fontWeight: 700, fontFamily: "Consolas, monospace", marginTop: 4 }}>{tile.value}</div>
            </div>
          );
        })}
      </div>

      <h1 style={styles.h1}>II. Vérification des données (Partie A)</h1>
      <table style={styles.table}>
        <thead>
          <tr>
            <th style={{ ...styles.th, width: 24 }}>#</th>
            <th style={styles.th}>Indicateur</th>
            <th style={styles.th}>Doc-Source</th>
            <th style={{ ...styles.th, textAlign: "right" }}>M1 V/R</th>
            <th style={{ ...styles.th, textAlign: "right" }}>M2 V/R</th>
            <th style={{ ...styles.th, textAlign: "right" }}>M3 V/R</th>
            <th style={{ ...styles.th, textAlign: "right" }}>Moy. Var</th>
          </tr>
        </thead>
        <tbody>
          {verifications.filter((r) => r.indicator_name || r.m1_verified != null).map((r) => {
            const c = DVT_STATUS_COLORS[dvtStatus(r.avg_var)];
            return (
              <tr key={r.position}>
                <td style={{ ...styles.td, fontFamily: "Consolas, monospace" }}>{r.position}</td>
                <td style={styles.td}>{r.indicator_name || "—"}</td>
                <td style={{ ...styles.td, color: "#6b7280" }}>{r.doc_source || "—"}</td>
                <td style={{ ...styles.td, textAlign: "right", fontFamily: "Consolas, monospace" }}>{r.m1_verified ?? "—"}/{r.m1_reported ?? "—"}<br /><span style={{ color: DVT_STATUS_COLORS[dvtStatus(r.m1_var)].fg }}>{fmtVar(r.m1_var)}</span></td>
                <td style={{ ...styles.td, textAlign: "right", fontFamily: "Consolas, monospace" }}>{r.m2_verified ?? "—"}/{r.m2_reported ?? "—"}<br /><span style={{ color: DVT_STATUS_COLORS[dvtStatus(r.m2_var)].fg }}>{fmtVar(r.m2_var)}</span></td>
                <td style={{ ...styles.td, textAlign: "right", fontFamily: "Consolas, monospace" }}>{r.m3_verified ?? "—"}/{r.m3_reported ?? "—"}<br /><span style={{ color: DVT_STATUS_COLORS[dvtStatus(r.m3_var)].fg }}>{fmtVar(r.m3_var)}</span></td>
                <td style={{ ...styles.td, textAlign: "right", background: c.bg, color: c.fg, fontWeight: 700, fontFamily: "Consolas, monospace" }}>{fmtVar(r.avg_var)}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
      {audit && audit.comments_verification && (
        <div style={{ background: "#f9fafb", padding: 10, borderRadius: 6, marginBottom: 16, fontSize: 11 }}>
          <strong>Commentaires :</strong> {audit.comments_verification}
        </div>
      )}

      {improvements.length > 0 && (
        <>
          <h1 style={{ ...styles.h1, pageBreakBefore: "always" }}>III. Amélioration des données (Partie B)</h1>
          <table style={styles.table}>
            <thead>
              <tr>
                <th style={{ ...styles.th, width: 24 }}>#</th>
                <th style={styles.th}>Indicateur</th>
                <th style={styles.th}>Problème</th>
                <th style={styles.th}>Mesure corrective</th>
                <th style={styles.th}>Suivi</th>
                <th style={{ ...styles.th, width: 80 }}>Échéance</th>
                <th style={{ ...styles.th, width: 100 }}>Responsable</th>
              </tr>
            </thead>
            <tbody>
              {improvements.filter((i) => i.indicator_name || i.problem).map((i) => (
                <tr key={i.position}>
                  <td style={{ ...styles.td, fontFamily: "Consolas, monospace" }}>{i.position}</td>
                  <td style={styles.td}>{i.indicator_name || "—"}</td>
                  <td style={styles.td}>{i.problem || "—"}</td>
                  <td style={styles.td}>{i.corrective_action || "—"}</td>
                  <td style={styles.td}>{i.followup_steps || "—"}</td>
                  <td style={styles.td}>{i.due_date || "—"}</td>
                  <td style={styles.td}>{i.responsible || "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
          {audit && audit.comments_improvement && (
            <div style={{ background: "#f9fafb", padding: 10, borderRadius: 6, marginBottom: 16, fontSize: 11 }}>
              <strong>Commentaires :</strong> {audit.comments_improvement}
            </div>
          )}
        </>
      )}

      <div style={{ marginTop: 40, paddingTop: 16, borderTop: "1px solid #e5e7eb", fontSize: 10, color: "#6b7280", textAlign: "center" }}>
        Document généré automatiquement par MELR — {today}<br />
        Conformité : Outil DVT REFT Africa — Vérification & Amélioration de Données
      </div>
    </div>
  );
}

window.AuditData = AuditData;
