/* global React, Icon, Modal */
// ============================================================================
// Chantier K.1 · Journal d'audit — UI viewer
// ----------------------------------------------------------------------------
// Tableau filtrable des entrées audit_log, accessible aux admins d'org et aux
// super-admins. Filtres : table, opération, dates, acteur, recherche libre.
// Clic sur une ligne → diff old_data vs new_data en JSON pretty-printed.
//
// La RLS impose déjà le périmètre (super-admin = tout, admin = son org).
// Le filtre Organisation n'est exposé qu'aux super-admins.
// ============================================================================

const { useState: useStateAL, useEffect: useEffectAL, useMemo: useMemoAL } = React;

function AuditLogScreen({ t, lang, isSuperAdmin, actingOrgId, activeOrgId, myOrgId }) {
  const { has, loading: permsLoading } = window.melr.useCurrentUserPermissions();
  // Périmètre par défaut : super-admin acting > active multi-org > home org.
  const effOrg = (isSuperAdmin && actingOrgId) ? actingOrgId : (activeOrgId || myOrgId);

  // Filtres
  const [filterOrg, setFilterOrg]         = useStateAL(isSuperAdmin ? "" : (effOrg || ""));
  const [filterTable, setFilterTable]     = useStateAL("");
  const [filterOp, setFilterOp]           = useStateAL("");
  const [filterFrom, setFilterFrom]       = useStateAL("");  // YYYY-MM-DD
  const [filterTo, setFilterTo]           = useStateAL("");
  const [filterSearch, setFilterSearch]   = useStateAL("");
  const [filterRecordId, setFilterRecordId] = useStateAL("");
  const [limitN, setLimitN] = useStateAL(200);

  // Liste des tables auditées (pour peupler la dropdown)
  const [tableList, setTableList] = useStateAL([]);
  useEffectAL(() => {
    let cancelled = false;
    (async () => {
      try {
        const list = await window.melr.listAuditedTables();
        if (!cancelled) setTableList(list || []);
      } catch (_) {}
    })();
    return () => { cancelled = true; };
  }, []);

  // Pour le super-admin : liste des orgs pour filtrer
  const allOrgsHook = window.melr.useAllOrganizations
    ? window.melr.useAllOrganizations()
    : { data: [], loading: false };
  const allOrgs = allOrgsHook.data || [];

  // Construit le filtre à passer au hook. Mémorise pour éviter refetch inutile.
  const queryFilters = useMemoAL(() => {
    const f = {};
    if (filterOrg)        f.orgId       = filterOrg;
    if (filterTable)      f.tableName   = filterTable;
    if (filterOp)         f.operation   = filterOp;
    if (filterRecordId)   f.recordId    = filterRecordId.trim();
    if (filterSearch)     f.search      = filterSearch.trim();
    if (filterFrom)       f.dateFrom    = new Date(filterFrom + "T00:00:00").toISOString();
    if (filterTo)         f.dateTo      = new Date(filterTo   + "T23:59:59").toISOString();
    f.limit = Number(limitN) || 200;
    return f;
  }, [filterOrg, filterTable, filterOp, filterRecordId, filterSearch, filterFrom, filterTo, limitN]);

  const { data: rows, loading, error, refresh } = window.melr.useAuditLog(queryFilters);

  // Détail / expand row : on stocke la ligne sélectionnée
  const [selected, setSelected] = useStateAL(null);

  // Gating accès : si pas super-admin et pas admin → refusé
  if (permsLoading) {
    return <div className="page"><div style={{ padding: 28 }}>{window.L("Chargement…", "Loading…", "Cargando…")}</div></div>;
  }
  // private.is_admin_user() côté SQL contrôle déjà via RLS. Côté UI on vérifie
  // 'users.manage' ou 'orgs.manage' comme proxy raisonnable.
  const canView = isSuperAdmin || (has && (has("users.manage") || has("orgs.manage")));
  if (!canView) {
    return (
      <div className="page">
        <div className="page-header">
          <div className="page-eyebrow">{window.L("ADMINISTRATION", "ADMINISTRATION", "ADMINISTRACIÓN")}</div>
          <h1 className="page-title">{window.L("Journal d'audit", "Audit log", "Registro de auditoría")}</h1>
        </div>
        <div className="card" style={{ marginTop: 16, padding: 24 }}>
          <div className="text-faint" style={{ textAlign: "center" }}>
            {window.L("Accès réservé aux administrateurs d'organisation et aux super-administrateurs.", "Restricted to organization admins and super-administrators.", "Acceso reservado a los administradores de organización y a los superadministradores.")}
          </div>
        </div>
      </div>
    );
  }

  const resetFilters = () => {
    setFilterOrg(isSuperAdmin ? "" : (effOrg || ""));
    setFilterTable(""); setFilterOp("");
    setFilterFrom(""); setFilterTo("");
    setFilterSearch(""); setFilterRecordId("");
    setLimitN(200);
  };

  // Export CSV — utile pour les audits RGPD / RFP institutionnels
  const exportCsv = () => {
    if (!rows || !rows.length) return;
    const headers = ["occurred_at","actor_email","organization_id","table_name","record_id","operation","changed_fields"];
    const escape = (s) => {
      const v = s == null ? "" : String(s);
      if (/[",\n;]/.test(v)) return '"' + v.replace(/"/g, '""') + '"';
      return v;
    };
    const lines = [headers.join(";")];
    rows.forEach((r) => {
      lines.push(headers.map((h) => {
        const v = h === "changed_fields"
          ? (Array.isArray(r.changed_fields) ? r.changed_fields.join("|") : "")
          : r[h];
        return escape(v);
      }).join(";"));
    });
    const csv = "﻿" + lines.join("\n");  // BOM pour Excel
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `audit-log-${new Date().toISOString().slice(0,10)}.csv`;
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  };

  // Helper UI pour le libellé d'opération
  const opLabel = (op) => {
    if (op === "INSERT") return window.L("Création", "Create", "Creación");
    if (op === "UPDATE") return window.L("Modification", "Update", "Modificación");
    if (op === "DELETE") return window.L("Suppression", "Delete", "Eliminación");
    return op;
  };
  const opHue = (op) => {
    if (op === "INSERT") return 145;  // vert
    if (op === "UPDATE") return 210;  // bleu
    if (op === "DELETE") return 0;    // rouge
    return 230;
  };

  const fmtDate = (iso) => {
    if (!iso) return "—";
    try {
      const d = new Date(iso);
      return d.toLocaleString(window.L("fr-FR", "en-GB", "es-ES"), {
        year: "numeric", month: "2-digit", day: "2-digit",
        hour: "2-digit", minute: "2-digit", second: "2-digit",
      });
    } catch (_) { return iso; }
  };

  return (
    <div className="page" data-screen-label="App / audit_log">
      <div className="page-header">
        <div className="page-eyebrow">{window.L("CONFORMITÉ", "COMPLIANCE", "CUMPLIMIENTO")}</div>
        <div className="page-header-row" style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
          <h1 className="page-title">{window.L("Journal d'audit", "Audit log", "Registro de auditoría")}</h1>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="btn-secondary" onClick={refresh} title={window.L("Rafraîchir", "Refresh", "Actualizar")}>
              {Icon && Icon.refresh ? <Icon.refresh className="sm" /> : "↻"} {window.L("Rafraîchir", "Refresh", "Actualizar")}
            </button>
            <button className="btn-secondary" onClick={exportCsv} disabled={!rows || !rows.length}
                    style={{ color: "var(--green, #16a34a)" }}>
              {Icon && Icon.download ? <Icon.download className="sm" /> : "⬇"} CSV
            </button>
          </div>
        </div>
        <div className="page-sub" style={{ fontSize: 13, color: "var(--text-faint)", marginTop: 4 }}>
          {window.L("Trace immuable des opérations INSERT, UPDATE et DELETE sur les tables sensibles. Append-only via triggers.", "Immutable trail of INSERT, UPDATE and DELETE operations on sensitive tables. Append-only via triggers.", "Rastro inmutable de las operaciones INSERT, UPDATE y DELETE en las tablas sensibles. Solo adición mediante triggers.")}
        </div>
      </div>

      {/* ── Filtres ──────────────────────────────────────────────── */}
      <div className="card" style={{ marginTop: 14, padding: 14 }}>
        <div style={{
          display: "grid",
          gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
          gap: 10,
        }}>
          {isSuperAdmin && (
            <label style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
              <span style={{ color: "var(--text-faint)" }}>{window.L("Organisation", "Organization", "Organización")}</span>
              <select value={filterOrg} onChange={(e) => setFilterOrg(e.target.value)}>
                <option value="">{window.L("— Toutes —", "— All —", "— Todas —")}</option>
                {allOrgs.filter((o) => !o.archived_at).map((o) => (
                  <option key={o.id} value={o.id}>{o.name_fr || o.name_en || o.code || o.id}</option>
                ))}
              </select>
            </label>
          )}
          <label style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
            <span style={{ color: "var(--text-faint)" }}>{window.L("Table", "Table", "Tabla")}</span>
            <select value={filterTable} onChange={(e) => setFilterTable(e.target.value)}>
              <option value="">{window.L("— Toutes —", "— All —", "— Todas —")}</option>
              {tableList.map((tn) => (
                <option key={tn} value={tn}>{tn}</option>
              ))}
            </select>
          </label>
          <label style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
            <span style={{ color: "var(--text-faint)" }}>{window.L("Opération", "Operation", "Operación")}</span>
            <select value={filterOp} onChange={(e) => setFilterOp(e.target.value)}>
              <option value="">{window.L("— Toutes —", "— All —", "— Todas —")}</option>
              <option value="INSERT">{window.L("Création", "Create", "Creación")}</option>
              <option value="UPDATE">{window.L("Modification", "Update", "Modificación")}</option>
              <option value="DELETE">{window.L("Suppression", "Delete", "Eliminación")}</option>
            </select>
          </label>
          <label style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
            <span style={{ color: "var(--text-faint)" }}>{window.L("Du", "From", "Desde")}</span>
            <input type="date" value={filterFrom} onChange={(e) => setFilterFrom(e.target.value)} />
          </label>
          <label style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
            <span style={{ color: "var(--text-faint)" }}>{window.L("Au", "To", "Hasta")}</span>
            <input type="date" value={filterTo} onChange={(e) => setFilterTo(e.target.value)} />
          </label>
          <label style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
            <span style={{ color: "var(--text-faint)" }}>{window.L("Recherche (email/table)", "Search (email/table)", "Búsqueda (correo/tabla)")}</span>
            <input type="text" value={filterSearch} onChange={(e) => setFilterSearch(e.target.value)}
                   placeholder={window.L("ex. alice@...", "e.g. alice@...", "p. ej. alice@...")} />
          </label>
          <label style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
            <span style={{ color: "var(--text-faint)" }}>{window.L("ID enregistrement", "Record ID", "ID de registro")}</span>
            <input type="text" value={filterRecordId} onChange={(e) => setFilterRecordId(e.target.value)}
                   placeholder="uuid…" />
          </label>
          <label style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
            <span style={{ color: "var(--text-faint)" }}>{window.L("Limite", "Limit", "Límite")}</span>
            <select value={limitN} onChange={(e) => setLimitN(Number(e.target.value))}>
              <option value={50}>50</option>
              <option value={200}>200</option>
              <option value={500}>500</option>
              <option value={1000}>1000</option>
            </select>
          </label>
        </div>
        <div style={{ marginTop: 10, display: "flex", gap: 8, justifyContent: "flex-end" }}>
          <button className="btn-secondary" onClick={resetFilters} style={{ fontSize: 12 }}>
            {window.L("Réinitialiser", "Reset filters", "Restablecer filtros")}
          </button>
        </div>
      </div>

      {/* ── Tableau ──────────────────────────────────────────────── */}
      <div className="card" style={{ marginTop: 14 }}>
        <div style={{ padding: "10px 14px", display: "flex", alignItems: "center", justifyContent: "space-between", borderBottom: "1px solid var(--border, #e5e7eb)" }}>
          <div style={{ fontSize: 13, fontWeight: 600 }}>
            {loading
              ? (window.L("Chargement…", "Loading…", "Cargando…"))
              : (window.L(`${rows.length} entrée${rows.length > 1 ? "s" : ""}`, `${rows.length} entr${rows.length === 1 ? "y" : "ies"}`, `${rows.length} entrada${rows.length === 1 ? "" : "s"}`))}
            {rows && rows.length === (Number(limitN) || 200) && (
              <span style={{ marginLeft: 8, fontSize: 11, color: "var(--text-faint)" }}>
                {window.L("(limite atteinte — affinez les filtres)", "(limit reached — refine filters)", "(límite alcanzado — afine los filtros)")}
              </span>
            )}
          </div>
          {error && <div style={{ color: "var(--red, #dc2626)", fontSize: 12 }}>{String(error)}</div>}
        </div>
        <div style={{ overflowX: "auto" }}>
          <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
            <thead>
              <tr style={{ background: "var(--bg-soft, #f9fafb)", textAlign: "left" }}>
                <th style={thStyle}>{window.L("Quand", "When", "Cuándo")}</th>
                <th style={thStyle}>{window.L("Acteur", "Actor", "Actor")}</th>
                <th style={thStyle}>{window.L("Table", "Table", "Tabla")}</th>
                <th style={thStyle}>{window.L("Opération", "Operation", "Operación")}</th>
                <th style={thStyle}>{window.L("ID enregistrement", "Record ID", "ID de registro")}</th>
                <th style={thStyle}>{window.L("Champs modifiés", "Changed fields", "Campos modificados")}</th>
                <th style={{ ...thStyle, width: 40 }}></th>
              </tr>
            </thead>
            <tbody>
              {(rows || []).map((r) => (
                <tr key={r.id}
                    style={{ borderTop: "1px solid var(--border-soft, #f1f5f9)", cursor: "pointer" }}
                    onClick={() => setSelected(r)}>
                  <td style={tdStyle}>{fmtDate(r.occurred_at)}</td>
                  <td style={tdStyle}>{r.actor_email || <span className="text-faint">—</span>}</td>
                  <td style={tdStyle}><code style={codeStyle}>{r.table_name}</code></td>
                  <td style={tdStyle}>
                    <span style={{
                      display: "inline-block", padding: "2px 8px", borderRadius: 10,
                      background: `oklch(95% 0.04 ${opHue(r.operation)})`,
                      color:      `oklch(40% 0.18 ${opHue(r.operation)})`,
                      fontSize: 11, fontWeight: 600,
                    }}>{opLabel(r.operation)}</span>
                  </td>
                  <td style={tdStyle}>
                    {r.record_id ? <code style={codeStyle} title={r.record_id}>{String(r.record_id).slice(0, 8)}…</code> : <span className="text-faint">—</span>}
                  </td>
                  <td style={tdStyle}>
                    {r.changed_fields_label
                      ? <span style={{ fontSize: 12 }}>{r.changed_fields_label}</span>
                      : <span className="text-faint">—</span>}
                  </td>
                  <td style={tdStyle}>
                    <span style={{ color: "var(--text-faint)" }}>›</span>
                  </td>
                </tr>
              ))}
              {(!loading && (!rows || rows.length === 0)) && (
                <tr><td colSpan={7} style={{ padding: 28, textAlign: "center", color: "var(--text-faint)" }}>
                  {window.L("Aucune entrée pour ces critères.", "No entries for these filters.", "No hay entradas para estos criterios.")}
                </td></tr>
              )}
            </tbody>
          </table>
        </div>
      </div>

      {/* ── Modal détail ─────────────────────────────────────────── */}
      {selected && (
        <AuditDetailModal entry={selected} lang={lang} onClose={() => setSelected(null)} />
      )}
    </div>
  );
}

// Styles utilisés en inline (cohérents avec le reste de l'app)
const thStyle = {
  padding: "8px 12px",
  fontSize: 11,
  fontWeight: 600,
  color: "var(--text-faint)",
  textTransform: "uppercase",
  letterSpacing: 0.4,
};
const tdStyle = {
  padding: "10px 12px",
  fontSize: 13,
  verticalAlign: "top",
};
const codeStyle = {
  fontFamily: "ui-monospace, SFMono-Regular, monospace",
  fontSize: 11,
  background: "var(--bg-soft, #f1f5f9)",
  padding: "1px 6px",
  borderRadius: 4,
};

// ─────────────────────────────────────────────────────────────────────────────
// Modal : diff old_data vs new_data
// ─────────────────────────────────────────────────────────────────────────────
function AuditDetailModal({ entry, lang, onClose }) {
  if (!entry) return null;

  // Construit la liste pretty des changements pour les UPDATE
  const isUpdate = entry.operation === "UPDATE";
  const isInsert = entry.operation === "INSERT";
  const isDelete = entry.operation === "DELETE";

  const renderJson = (obj) => {
    if (!obj) return <span className="text-faint">null</span>;
    try {
      const s = JSON.stringify(obj, null, 2);
      return (
        <pre style={{
          fontSize: 11.5, fontFamily: "ui-monospace, SFMono-Regular, monospace",
          background: "var(--bg-soft, #f8fafc)", border: "1px solid var(--border, #e5e7eb)",
          borderRadius: 6, padding: 10, overflow: "auto", maxHeight: 320,
          whiteSpace: "pre-wrap", wordBreak: "break-word",
        }}>{s}</pre>
      );
    } catch (_) {
      return <pre>{String(obj)}</pre>;
    }
  };

  // Pour les UPDATE on construit un tableau old vs new par champ modifié
  const diffRows = isUpdate && Array.isArray(entry.changed_fields)
    ? entry.changed_fields.map((field) => ({
        field,
        oldV: entry.old_data ? entry.old_data[field] : undefined,
        newV: entry.new_data ? entry.new_data[field] : undefined,
      }))
    : [];

  const Body = (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <div style={{ display: "grid", gridTemplateColumns: "120px 1fr", gap: 8, fontSize: 13 }}>
        <div className="text-faint">{window.L("Quand", "When", "Cuándo")}</div>
        <div>{new Date(entry.occurred_at).toLocaleString(window.L("fr-FR", "en-GB", "es-ES"))}</div>

        <div className="text-faint">{window.L("Acteur", "Actor", "Actor")}</div>
        <div>{entry.actor_email || <span className="text-faint">—</span>}</div>

        <div className="text-faint">{window.L("Table", "Table", "Tabla")}</div>
        <div><code style={codeStyle}>{entry.table_name}</code></div>

        <div className="text-faint">{window.L("ID", "ID", "ID")}</div>
        <div>{entry.record_id ? <code style={codeStyle}>{entry.record_id}</code> : "—"}</div>

        <div className="text-faint">{window.L("Opération", "Operation", "Operación")}</div>
        <div><strong>{entry.operation}</strong></div>
      </div>

      {isUpdate && diffRows.length > 0 && (
        <div>
          <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6 }}>
            {window.L("Champs modifiés", "Changed fields", "Campos modificados")}
          </div>
          <div style={{ border: "1px solid var(--border, #e5e7eb)", borderRadius: 6, overflow: "hidden" }}>
            <table style={{ width: "100%", fontSize: 12, borderCollapse: "collapse" }}>
              <thead>
                <tr style={{ background: "var(--bg-soft, #f9fafb)" }}>
                  <th style={{ ...thStyle, padding: "6px 10px" }}>{window.L("Champ", "Field", "Campo")}</th>
                  <th style={{ ...thStyle, padding: "6px 10px" }}>{window.L("Avant", "Before", "Antes")}</th>
                  <th style={{ ...thStyle, padding: "6px 10px" }}>{window.L("Après", "After", "Después")}</th>
                </tr>
              </thead>
              <tbody>
                {diffRows.map((d, i) => (
                  <tr key={i} style={{ borderTop: "1px solid var(--border-soft, #f1f5f9)" }}>
                    <td style={{ padding: "6px 10px" }}><code style={codeStyle}>{d.field}</code></td>
                    <td style={{ padding: "6px 10px", color: "var(--red, #dc2626)" }}>
                      <pre style={{ margin: 0, fontFamily: "inherit", whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
                        {d.oldV === undefined ? "—" : JSON.stringify(d.oldV)}
                      </pre>
                    </td>
                    <td style={{ padding: "6px 10px", color: "var(--green, #16a34a)" }}>
                      <pre style={{ margin: 0, fontFamily: "inherit", whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
                        {d.newV === undefined ? "—" : JSON.stringify(d.newV)}
                      </pre>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}

      {isInsert && (
        <div>
          <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6, color: "var(--green, #16a34a)" }}>
            {window.L("Données créées", "Created data", "Datos creados")}
          </div>
          {renderJson(entry.new_data)}
        </div>
      )}

      {isDelete && (
        <div>
          <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6, color: "var(--red, #dc2626)" }}>
            {window.L("Données supprimées", "Deleted data", "Datos eliminados")}
          </div>
          {renderJson(entry.old_data)}
        </div>
      )}

      {/* Raw JSON repliable pour debug */}
      <details style={{ fontSize: 11.5 }}>
        <summary style={{ cursor: "pointer", color: "var(--text-faint)", padding: "4px 0" }}>
          {window.L("Voir le JSON brut", "View raw JSON", "Ver el JSON sin procesar")}
        </summary>
        <div style={{ marginTop: 8, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <div>
            <div style={{ fontSize: 11, color: "var(--text-faint)", marginBottom: 4 }}>old_data</div>
            {renderJson(entry.old_data)}
          </div>
          <div>
            <div style={{ fontSize: 11, color: "var(--text-faint)", marginBottom: 4 }}>new_data</div>
            {renderJson(entry.new_data)}
          </div>
        </div>
      </details>
    </div>
  );

  // Utilise window.Modal si disponible (cohérent avec le reste de l'app),
  // sinon fallback à un overlay inline.
  if (window.Modal) {
    return (
      <window.Modal
        open={true}
        onClose={onClose}
        title={window.L("Détail de l'entrée d'audit", "Audit entry detail", "Detalle de la entrada de auditoría")}
        size="lg"
      >
        {Body}
      </window.Modal>
    );
  }
  // Fallback inline overlay
  return (
    <div style={{
      position: "fixed", inset: 0, background: "rgba(15,23,42,0.55)",
      display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1000,
      padding: 20,
    }} onClick={onClose}>
      <div style={{
        background: "var(--bg, #fff)", borderRadius: 10, maxWidth: 900, width: "100%",
        maxHeight: "85vh", overflow: "auto", padding: 20,
        boxShadow: "0 20px 50px rgba(0,0,0,0.25)",
      }} onClick={(e) => e.stopPropagation()}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
          <h3 style={{ margin: 0, fontSize: 16 }}>
            {window.L("Détail de l'entrée d'audit", "Audit entry detail", "Detalle de la entrada de auditoría")}
          </h3>
          <button onClick={onClose} className="btn-secondary" style={{ fontSize: 12 }}>×</button>
        </div>
        {Body}
      </div>
    </div>
  );
}

// Expose globalement (convention MELR)
window.AuditLogScreen = AuditLogScreen;
