/* global React, Icon */
const { useState: useStateNT } = React;

// ==================== NOTIFICATIONS & SCHEDULED REPORTS ====================
function Notifications({ t, lang, setRoute, isSuperAdmin, isAdmin, hasPerm }) {
  // "Nouvelle règle" creates an alert_rules row — admin action by RLS
  // (organization_modules_write requires is_super_admin_user(), alert_
  // rules requires admin or super-admin). Hide the button + Ajouter
  // inline link for non-admin users.
  const canManageRules = !!isSuperAdmin || !!isAdmin
    || (hasPerm && hasPerm("users.manage"));
  const { data: liveNotifs, realtime, refresh: refreshNotifs } = window.melr.useNotifications();
  const { data: liveRules, loading: rulesLoading, refresh: refreshRules } = window.melr.useAlertRules();
  const [ruleEditing, setRuleEditing] = useStateNT(null); // null | 'new' | row object
  const [ruleBusy, setRuleBusy] = useStateNT(false);
  const useFixture = !liveNotifs || liveNotifs.length === 0;
  const useRulesFixture = !liveRules || liveRules.length === 0;

  const onMarkAllRead = async () => {
    if (useFixture) return;
    try { await window.melr.notificationsCrud.markAllRead(); }
    catch (e) { alert((lang === "fr" ? "Erreur : " : "Error: ") + e.message); }
  };
  const onMarkRead = async (id) => {
    if (useFixture) return;
    try { await window.melr.notificationsCrud.markRead(id); }
    catch (e) { console.error(e); }
  };
  const onOpenLink = (notif) => {
    if (notif.id != null && !useFixture) onMarkRead(notif.id);
    if (notif.link && setRoute) {
      // Strip leading slash for the route
      const route = notif.link.replace(/^\//, "");
      if (route) setRoute(route);
    }
  };
  const FIXTURE_NOTIFS = [
    { id: 1, t: "alert", title: lang === "fr" ? "SLA dépassé — VAL-2026-111 Matrice MPR P-462" : "SLA breached — VAL-2026-111 MPR Matrix P-462", body: lang === "fr" ? "Approbation en attente depuis 4 jours (objectif: 3 j). Karim Bensaad doit valider." : "Approval pending 4 days (target: 3 d). Karim Bensaad must validate.", who: lang === "fr" ? "Workflow" : "Workflow", when: lang === "fr" ? "il y a 12 min" : "12 min ago", unread: true, tone: "red", project: "P-462" },
    { id: 2, t: "indicator", title: lang === "fr" ? "Indicateur en alerte — Mortalité maternelle P-302" : "Alerting indicator — Maternal mortality P-302", body: lang === "fr" ? "Valeur Q1 (487/100k) en deçà de la cible Q1 (425/100k). Écart -14.6%." : "Q1 value (487/100k) below Q1 target (425/100k). Gap -14.6%.", who: lang === "fr" ? "Système d'alerte" : "Alerting system", when: lang === "fr" ? "il y a 1 h" : "1 h ago", unread: true, tone: "amber", project: "P-302" },
    { id: 3, t: "mention", title: lang === "fr" ? "Mentionné par Karim Bensaad" : "Mentioned by Karim Bensaad", body: lang === "fr" ? "« @Aïssata pourrais-tu joindre la fiche stock vaccin avant validation finale ? »" : "“@Aïssata can you attach the vaccine stock sheet before final validation?”", who: "Karim Bensaad", when: lang === "fr" ? "il y a 1 h 47" : "1 h 47 ago", unread: true, tone: "accent", project: "P-241" },
    { id: 4, t: "report", title: lang === "fr" ? "Rapport Q1 P-241 généré" : "Q1 P-241 report generated", body: lang === "fr" ? "Le rapport périodique trimestriel est disponible (38 pages, 24 indicateurs). Format AFD-RP." : "Quarterly report ready (38 pages, 24 indicators). AFD-RP format.", who: lang === "fr" ? "Reporting auto" : "Auto reporting", when: lang === "fr" ? "il y a 3 h" : "3 h ago", unread: false, tone: "accent", project: "P-241" },
    { id: 5, t: "sync", title: lang === "fr" ? "Synchronisation terrain — Tombouctou" : "Field sync — Timbuktu", body: lang === "fr" ? "87 fiches reçues, 3 en attente de revue, 2 conflits à résoudre." : "87 forms received, 3 awaiting review, 2 conflicts to resolve.", who: "Bintou Tall", when: lang === "fr" ? "il y a 5 h" : "5 h ago", unread: false, tone: "accent", project: "P-241" },
    { id: 6, t: "approved", title: lang === "fr" ? "Validation accordée — VAL-2026-108" : "Validation granted — VAL-2026-108", body: lang === "fr" ? "Mise à jour Penta-3 acceptée par S. Touré et publiée sur le dashboard public." : "Penta-3 update approved by S. Touré, published on public dashboard.", who: "Souleymane Touré", when: lang === "fr" ? "hier, 16:42" : "yesterday, 4:42 pm", unread: false, tone: "green", project: "P-241" },
    { id: 7, t: "dqa", title: lang === "fr" ? "DQA — 4 sites en écart > 10%" : "DQA — 4 sites > 10% gap", body: lang === "fr" ? "Échantillonnage trimestriel P-188 : 4 sites présentent des écarts significatifs avec leurs registres." : "P-188 quarterly sampling: 4 sites have significant gaps with their registers.", who: lang === "fr" ? "Audit DQA" : "DQA audit", when: lang === "fr" ? "hier, 11:18" : "yesterday, 11:18 am", unread: false, tone: "amber", project: "P-188" },
    { id: 8, t: "deadline", title: lang === "fr" ? "Échéance dans 3 jours — Rapport bailleur" : "Due in 3 days — Donor report", body: lang === "fr" ? "Rapport semestriel AFD pour le portefeuille (9 projets) — collecte terminée." : "Semi-annual AFD report for portfolio (9 projects) — collection complete.", who: lang === "fr" ? "Calendrier" : "Calendar", when: "2 j", unread: false, tone: "amber", project: "—" },
  ];

  // Use database rows when available, otherwise the fixtures so the
  // demo UI stays populated until notifications are seeded.
  const TONE_BY_SEV = { critical: "red", high: "red", warn: "amber", info: "accent" };
  // Resolve a kind to a display group: alerts (red/amber), workflow (approvals),
  // audits (sat_*, dvt_*), indicators, mentions, system.
  const groupForKind = (kind) => {
    if (!kind) return "system";
    if (kind === "mention") return "mention";
    if (kind === "indicator") return "indicator";
    if (kind === "approval" || kind === "workflow" || kind === "sat_workflow") return "workflow";
    if (kind.startsWith("sat_") || kind.startsWith("dvt_") || kind === "dqa") return "audit";
    return kind;
  };
  const NOTIFS = (liveNotifs && liveNotifs.length > 0)
    ? liveNotifs.map((n) => ({
        id: n.id,
        t: groupForKind(n.kind),
        rawKind: n.kind || "alert",
        title: n.title || "",
        body: n.body || "",
        who: "—",
        when: new Date(n.created_at).toLocaleString(lang === "fr" ? "fr-FR" : "en-US"),
        unread: !n.read_at,
        tone: TONE_BY_SEV[n.severity] || "accent",
        project: "—",
        link: n.link_url || null,
        sev: n.severity,
      }))
    : FIXTURE_NOTIFS;

  const unreadCount = NOTIFS.filter((n) => n.unread).length;
  const alertCount  = NOTIFS.filter((n) => n.tone === "red" || n.tone === "amber").length;
  const auditCount  = NOTIFS.filter((n) => n.t === "audit").length;
  const wfCount     = NOTIFS.filter((n) => n.t === "workflow").length;
  const indCount    = NOTIFS.filter((n) => n.t === "indicator").length;

  const TAB_KEYS = [
    { k: "all",       l: lang === "fr" ? "Toutes" : "All",         c: NOTIFS.length },
    { k: "unread",    l: lang === "fr" ? "Non lues" : "Unread",    c: unreadCount },
    { k: "audit",     l: "Audits",                                 c: auditCount },
    { k: "indicator", l: lang === "fr" ? "Indicateurs" : "Indicators", c: indCount },
    { k: "workflow",  l: lang === "fr" ? "Validation" : "Workflow", c: wfCount },
    { k: "alerts",    l: lang === "fr" ? "Alertes" : "Alerts",     c: alertCount },
  ];

  // Demo fixture used until the org actually creates alert_rules. Once a
  // single live row exists, we switch entirely to live data so the user
  // doesn't get confused by demo entries they can't edit.
  const FIXTURE_RULES = [
    { id: 1, n: lang === "fr" ? "Indicateur en alerte" : "Indicator alerts", c: lang === "fr" ? "Écart cible Q vs réalisé > 10%" : "Q target vs actual gap > 10%", ch: ["email", "in", "slack"], freq: lang === "fr" ? "Temps réel" : "Real-time", on: true },
    { id: 2, n: lang === "fr" ? "SLA workflow dépassé" : "Workflow SLA breach", c: lang === "fr" ? "Action en attente > délai du modèle" : "Pending action > template SLA", ch: ["email", "in"], freq: lang === "fr" ? "Toutes les 4 h" : "Every 4 h", on: true },
    { id: 3, n: lang === "fr" ? "Échéance rapport bailleur" : "Donor report deadline", c: "T -14 j / T -3 j / T -1 j", ch: ["email", "in"], freq: lang === "fr" ? "Programmée" : "Scheduled", on: true },
    { id: 4, n: lang === "fr" ? "Synchronisation terrain" : "Field sync", c: lang === "fr" ? "Nouvelles fiches > 50 ou conflit" : "New forms > 50 or conflict", ch: ["in"], freq: lang === "fr" ? "Temps réel" : "Real-time", on: true },
    { id: 5, n: lang === "fr" ? "Mention @ utilisateur" : "@-mention", c: lang === "fr" ? "Discussions & commentaires" : "Discussions & comments", ch: ["email", "in", "slack"], freq: lang === "fr" ? "Temps réel" : "Real-time", on: true },
    { id: 6, n: lang === "fr" ? "Audit DQA — écart majeur" : "DQA — major gap", c: lang === "fr" ? "Site avec écart > 10% sur > 3 indicateurs" : "Site with gap > 10% on > 3 indicators", ch: ["email", "in"], freq: lang === "fr" ? "Hebdo (lundi 9h)" : "Weekly (Mon 9 am)", on: false },
    { id: 7, n: lang === "fr" ? "Décaissement vs plan" : "Burn rate vs plan", c: lang === "fr" ? "Écart > 15% sur 2 trimestres consécutifs" : "Gap > 15% over 2 quarters", ch: ["email"], freq: lang === "fr" ? "Mensuelle" : "Monthly", on: true },
  ];

  // Render a JSON condition into a human-readable string. Falls back to
  // raw JSON when the DSL doesn't match a known shape.
  const formatCondition = (cond) => {
    if (!cond) return "—";
    if (typeof cond === "string") return cond;
    if (cond.text) return cond.text;
    if (cond.indicator && cond.op != null && cond.target != null) {
      return `${cond.indicator} ${cond.op} ${cond.target}`;
    }
    try { return JSON.stringify(cond); } catch { return String(cond); }
  };

  // Map a live alert_rules row (DB shape: name/condition/channels/frequency/active)
  // to the table-render shape used by FIXTURE_RULES (n/c/ch/freq/on).
  const RULES = useRulesFixture ? FIXTURE_RULES : (liveRules || []).map((r) => ({
    id: r.id,
    n: r.name,
    c: formatCondition(r.condition),
    ch: Array.isArray(r.channels)
      ? r.channels.map((x) => x === "in_app" ? "in" : x)
      : [],
    freq: r.frequency || (lang === "fr" ? "Temps réel" : "Real-time"),
    on: r.active !== false,
    _raw: r,
  }));

  const REPORTS = [
    { n: lang === "fr" ? "Rapport trimestriel AFD — portefeuille" : "AFD quarterly — portfolio", f: "AFD-RP", proj: lang === "fr" ? "9 projets" : "9 projects", freq: lang === "fr" ? "Trimestriel · J+15" : "Quarterly · D+15", next: "15/04/2026", to: "donor-afd@example.org · +2", on: true, last: lang === "fr" ? "envoyé 15/01/2026" : "sent 15/01/2026" },
    { n: lang === "fr" ? "Dashboard hebdomadaire S&E" : "Weekly M&E dashboard", f: "PDF + XLSX", proj: lang === "fr" ? "Tous projets actifs" : "All active projects", freq: lang === "fr" ? "Lundi 8h" : "Mon 8 am", next: "17/03/2026", to: "me-team@example.org", on: true, last: lang === "fr" ? "envoyé lundi 8:00" : "sent Mon 8:00" },
    { n: lang === "fr" ? "Indicateurs santé — Sahel" : "Sahel health indicators", f: "Dashboard URL", proj: "P-241, P-156, P-188", freq: lang === "fr" ? "Quotidien 7h" : "Daily 7 am", next: lang === "fr" ? "demain 07:00" : "tomorrow 7:00", to: lang === "fr" ? "Espace bailleur (3 utilisateurs)" : "Donor space (3 users)", on: true, last: lang === "fr" ? "ce matin" : "this morning" },
    { n: lang === "fr" ? "Mémo direction" : "Direction memo", f: "PDF · 2 p.", proj: lang === "fr" ? "Tous" : "All", freq: lang === "fr" ? "Mensuel · 1er" : "Monthly · 1st", next: "01/04/2026", to: "direction@example.org · +4", on: true, last: "01/03/2026" },
    { n: lang === "fr" ? "Rapport semestriel UE" : "EU semi-annual", f: "EU-RP", proj: "P-241, P-302", freq: lang === "fr" ? "Semestriel · J+30" : "Semi-annual · D+30", next: "30/06/2026", to: "delegation-ue@example.org", on: true, last: "30/12/2025" },
    { n: lang === "fr" ? "Rapport annuel intégré" : "Integrated annual", f: "PDF + interactif", proj: lang === "fr" ? "Portefeuille complet" : "Full portfolio", freq: lang === "fr" ? "Annuel · 31 janv." : "Annual · Jan 31", next: "31/01/2027", to: lang === "fr" ? "Conseil + bailleurs" : "Board + donors", on: false, last: "31/01/2026" },
  ];

  const PREFS = {
    email: { l: "Email", ic: "fileText", value: "aissata.diallo@example.org" },
    in: { l: "In-app", ic: "bell", value: lang === "fr" ? "Toutes" : "All" },
    slack: { l: "Slack", ic: "message", value: "#melr-alertes" },
    sms: { l: "SMS", ic: "smartphone", value: lang === "fr" ? "Désactivé" : "Disabled" },
  };

  const [tab, setTab] = useStateNT("all");
  const filtered = tab === "unread"    ? NOTIFS.filter((n) => n.unread)
                 : tab === "audit"     ? NOTIFS.filter((n) => n.t === "audit")
                 : tab === "indicator" ? NOTIFS.filter((n) => n.t === "indicator")
                 : tab === "workflow"  ? NOTIFS.filter((n) => n.t === "workflow")
                 : tab === "alerts"    ? NOTIFS.filter((n) => n.tone === "red" || n.tone === "amber")
                 : tab === "mentions"  ? NOTIFS.filter((n) => n.t === "mention")
                 : NOTIFS;

  const TYPE_ICON = {
    alert: "alert", indicator: "trending", mention: "user",
    report: "fileText", sync: "refresh", approved: "check",
    dqa: "database", deadline: "clock",
    audit: "shieldCheck", workflow: "badgeCheck", system: "info",
  };

  return (
    <div className="page">
      <div className="page-header">
        <div className="page-eyebrow">{lang === "fr" ? "PRÉFÉRENCES" : "PREFERENCES"}</div>
        <div className="page-header-row">
          <div>
            <h1 className="page-title">
              {lang === "fr" ? "Notifications & rapports planifiés" : "Notifications & scheduled reports"}
              {realtime && (
                <span title={lang === "fr" ? "Mises à jour en temps réel" : "Real-time updates"}
                      style={{ marginLeft: 12, display: "inline-flex", alignItems: "center", gap: 6, padding: "2px 8px",
                               background: "#fee2e2", color: "#991b1b", borderRadius: 999, fontSize: 11, verticalAlign: "middle" }}>
                  <span style={{ width: 7, height: 7, borderRadius: "50%", background: "#dc2626", animation: "pulse 1.5s ease-in-out infinite" }} />
                  Live
                </span>
              )}
            </h1>
            <div className="page-sub">{lang === "fr" ? "Règles d'alertes · canaux · diffusion automatique" : "Alert rules · channels · automated distribution"}</div>
          </div>
          <div className="page-header-actions">
            <button className="btn sm" onClick={() => {
              const date = new Date().toISOString().slice(0, 10);
              window.melr.exportCSV(`notifications-${date}.csv`, NOTIFS, [
                { key: "id",      label: "ID" },
                { key: "t",       label: "Type" },
                { key: "title",   label: lang === "fr" ? "Titre" : "Title" },
                { key: "body",    label: lang === "fr" ? "Corps" : "Body" },
                { key: "who",     label: lang === "fr" ? "Qui" : "Who" },
                { key: "project", label: lang === "fr" ? "Projet" : "Project" },
                { key: "when",    label: lang === "fr" ? "Quand" : "When" },
                { key: "unread",  label: lang === "fr" ? "Non lu" : "Unread", value: (n) => n.unread ? "oui" : "non" },
                { key: "tone",    label: lang === "fr" ? "Niveau" : "Tone" },
              ]);
            }}><Icon.download /> {t("c.export")}</button>
            <button className="btn sm" onClick={onMarkAllRead} disabled={useFixture || unreadCount === 0}
              title={lang === "fr" ? "Marquer toutes les notifications comme lues" : "Mark every notification as read"}>
              <Icon.check /> {lang === "fr" ? "Tout marquer lu" : "Mark all read"}
              {unreadCount > 0 && !useFixture && <span style={{ marginLeft: 4, fontFamily: "var(--font-mono)" }}>({unreadCount})</span>}
            </button>
            {canManageRules && (
              <button className="btn sm primary" onClick={() => setRuleEditing("new")}>
                <Icon.plus /> {lang === "fr" ? "Nouvelle règle" : "New rule"}
              </button>
            )}
          </div>
        </div>
      </div>

      <div className="grid cols-4" style={{ marginBottom: 16 }}>
        {[
          { l: lang === "fr" ? "Non lues" : "Unread", v: String(unreadCount),
            s: useFixture
                ? (lang === "fr" ? "(démo)" : "(demo)")
                : (NOTIFS.filter((n) => n.unread && (n.tone === "red")).length + " " + (lang === "fr" ? "urgentes" : "urgent")),
            tone: unreadCount > 0 ? "amber" : "green" },
          { l: "Audits SAT/DVT", v: String(auditCount), s: lang === "fr" ? "alertes audit" : "audit alerts", tone: auditCount > 0 ? "amber" : undefined },
          { l: lang === "fr" ? "Indicateurs" : "Indicators", v: String(indCount), s: lang === "fr" ? "sous-cible" : "off-target", tone: indCount > 0 ? "amber" : undefined },
          { l: lang === "fr" ? "Validation" : "Workflow", v: String(wfCount), s: lang === "fr" ? "à instruire" : "to act on" },
        ].map((k, i) => (
          <div key={i} className="kpi">
            <div className="kpi-label">{k.l}</div>
            <div className="kpi-value" style={k.tone ? { color: `var(--${k.tone})` } : {}}>{k.v}</div>
            <div className="kpi-sub">{k.s}</div>
          </div>
        ))}
      </div>

      <div className="nt-split">
        {/* Notification feed */}
        <div className="card">
          <div className="card-head">
            <div className="card-title">{lang === "fr" ? "Boîte de réception" : "Inbox"}</div>
            <button className="btn xs ghost"><Icon.filter /> {lang === "fr" ? "Filtrer" : "Filter"}</button>
          </div>
          <div className="seg" style={{ borderBottom: "1px solid var(--line-faint)", borderRadius: 0, padding: "0 12px" }}>
            {TAB_KEYS.map((k) => (
              <button key={k.k} className={"seg-btn" + (tab === k.k ? " active" : "")} onClick={() => setTab(k.k)} style={{ borderRadius: 0 }}>
                {k.l} <span className="seg-count">{k.c}</span>
              </button>
            ))}
          </div>
          <div className="card-body flush nt-feed">
            {filtered.length === 0 && (
              <div style={{ padding: 22, textAlign: "center", color: "var(--text-faint)", fontSize: 13 }}>
                {lang === "fr" ? "Aucune notification dans ce filtre." : "No notification in this filter."}
              </div>
            )}
            {filtered.map((n) => {
              const Ic = Icon[TYPE_ICON[n.t]] || Icon.info;
              const isClickable = !!n.link && setRoute;
              return (
                <div key={n.id} className={"nt-item" + (n.unread ? " unread" : "")}
                  onClick={() => { if (isClickable) onOpenLink(n); }}
                  style={{ cursor: isClickable ? "pointer" : "default" }}
                  title={isClickable ? (lang === "fr" ? "Ouvrir et marquer comme lu" : "Open and mark as read") : undefined}>
                  <div className={"nt-icon tone-" + n.tone}><Ic /></div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div className="nt-h">
                      <span className="strong" style={{ fontSize: 12.5 }}>{n.title}</span>
                      {n.unread && <span className="nt-dot"></span>}
                      <span className="text-faint" style={{ fontSize: 11, marginLeft: "auto" }}>{n.when}</span>
                    </div>
                    <div className="nt-body">{n.body}</div>
                    <div className="nt-foot" style={{ alignItems: "center" }}>
                      {n.rawKind && <span className="tag-mono" style={{ fontSize: 10 }}>{n.rawKind}</span>}
                      {n.project !== "—" && <><span className="dotsep"></span><span className="mono text-faint">{n.project}</span></>}
                      <div style={{ flex: 1 }}></div>
                      {n.unread && !useFixture && (
                        <button className="btn xs ghost"
                          onClick={(e) => { e.stopPropagation(); onMarkRead(n.id); }}
                          title={lang === "fr" ? "Marquer comme lu" : "Mark as read"}
                          style={{ padding: "2px 6px", fontSize: 11 }}>
                          <Icon.check className="sm" /> {lang === "fr" ? "Lu" : "Read"}
                        </button>
                      )}
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* Channels & preferences */}
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Canaux" : "Channels"}</div></div>
          <div className="card-body" style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {Object.entries(PREFS).map(([k, p]) => {
              const Ic = Icon[p.ic];
              return (
                <div key={k} className="nt-channel">
                  <div className="nt-ch-icon"><Ic /></div>
                  <div style={{ flex: 1 }}>
                    <div className="strong">{p.l}</div>
                    <div className="text-faint mono" style={{ fontSize: 10.5 }}>{p.value}</div>
                  </div>
                  <label className="nt-switch">
                    <input type="checkbox" defaultChecked={k !== "sms"} />
                    <span></span>
                  </label>
                </div>
              );
            })}
          </div>
          <div className="card-head" style={{ borderTop: "1px solid var(--line-faint)" }}><div className="card-title">{lang === "fr" ? "Heures silencieuses" : "Quiet hours"}</div></div>
          <div className="card-body">
            <div className="row gap-sm" style={{ alignItems: "center" }}>
              <div style={{ flex: 1 }}>
                <div className="muted" style={{ fontSize: 11 }}>{lang === "fr" ? "Du" : "From"}</div>
                <div className="mono strong">20:00</div>
              </div>
              <div className="text-faint">→</div>
              <div style={{ flex: 1 }}>
                <div className="muted" style={{ fontSize: 11 }}>{lang === "fr" ? "Au" : "To"}</div>
                <div className="mono strong">07:00</div>
              </div>
              <label className="nt-switch"><input type="checkbox" defaultChecked /><span></span></label>
            </div>
            <div className="text-faint" style={{ fontSize: 11, marginTop: 8 }}>{lang === "fr" ? "Les alertes critiques (P1) contournent les heures silencieuses." : "Critical (P1) alerts bypass quiet hours."}</div>
          </div>
        </div>
      </div>

      {/* Alert rules */}
      <div className="card" style={{ marginTop: 16 }}>
        <div className="card-head">
          <div className="card-title">
            {lang === "fr" ? "Règles d'alerte" : "Alert rules"}
            <span className="muted"> · {RULES.filter((r) => r.on).length} / {RULES.length}</span>
            {useRulesFixture && (
              <span className="pill" style={{ marginLeft: 8, fontSize: 10, background: "var(--bg-sunken)", color: "var(--text-faint)" }}>
                {lang === "fr" ? "Démo" : "Demo"}
              </span>
            )}
          </div>
          {canManageRules && (
            <button className="btn xs ghost" onClick={() => setRuleEditing("new")}>
              <Icon.plus /> {lang === "fr" ? "Ajouter" : "Add"}
            </button>
          )}
        </div>
        <div className="card-body flush">
          {useRulesFixture && (
            <div style={{ padding: "8px 14px", fontSize: 11.5, color: "var(--text-faint)", borderBottom: "1px solid var(--line-faint)" }}>
              {lang === "fr"
                ? "Les règles ci-dessous sont des exemples. Cliquez « Ajouter » pour créer votre première règle réelle — elle sera enregistrée au niveau de votre organisation."
                : "Rules below are examples. Click 'Add' to create your first real rule — it will be saved at organization level."}
            </div>
          )}
          <table className="tbl">
            <thead><tr>
              <th>{lang === "fr" ? "Règle" : "Rule"}</th>
              <th>{lang === "fr" ? "Condition" : "Condition"}</th>
              <th>{lang === "fr" ? "Canaux" : "Channels"}</th>
              <th>{lang === "fr" ? "Fréquence" : "Frequency"}</th>
              <th>{lang === "fr" ? "État" : "State"}</th>
              <th></th>
            </tr></thead>
            <tbody>
              {RULES.map((r) => (
                <tr key={r.id}>
                  <td className="strong">{r.n}</td>
                  <td className="muted" style={{ fontSize: 12 }}>{r.c}</td>
                  <td><div className="row gap-xs">{r.ch.map((c) => <span key={c} className="nt-ch-chip">{c === "in" ? "in-app" : c}</span>)}</div></td>
                  <td className="muted">{r.freq}</td>
                  <td>
                    <label className="nt-switch">
                      <input type="checkbox" checked={r.on}
                        disabled={useRulesFixture || ruleBusy}
                        onChange={async (e) => {
                          if (useRulesFixture || !r._raw) return;
                          setRuleBusy(true);
                          try {
                            await window.melr.alertRulesCrud.update(r._raw.id, { active: e.target.checked });
                            await refreshRules();
                          } catch (err) { alert(err.message); }
                          finally { setRuleBusy(false); }
                        }} />
                      <span></span>
                    </label>
                  </td>
                  <td className="num">
                    <button className="iconbtn"
                      title={useRulesFixture ? (lang === "fr" ? "Démo — créez une vraie règle pour éditer" : "Demo — create a real rule to edit") : (lang === "fr" ? "Modifier" : "Edit")}
                      disabled={useRulesFixture}
                      onClick={() => r._raw && setRuleEditing(r._raw)}>
                      <Icon.edit />
                    </button>
                    {!useRulesFixture && (
                      <button className="iconbtn" title={lang === "fr" ? "Supprimer" : "Delete"}
                        disabled={ruleBusy}
                        onClick={async () => {
                          if (!r._raw) return;
                          if (!confirm(lang === "fr" ? `Supprimer la règle « ${r.n} » ?` : `Delete rule "${r.n}"?`)) return;
                          setRuleBusy(true);
                          try { await window.melr.alertRulesCrud.remove(r._raw.id); await refreshRules(); }
                          catch (err) { alert(err.message); }
                          finally { setRuleBusy(false); }
                        }}>
                        <Icon.trash />
                      </button>
                    )}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      {ruleEditing && (
        <AlertRuleModal
          lang={lang}
          row={ruleEditing === "new" ? null : ruleEditing}
          onClose={() => setRuleEditing(null)}
          onSaved={async () => { await refreshRules(); setRuleEditing(null); }}
        />
      )}

      {/* Scheduled reports */}
      <div className="card" style={{ marginTop: 16 }}>
        <div className="card-head">
          <div className="card-title">{lang === "fr" ? "Rapports planifiés" : "Scheduled reports"} <span className="muted">· {REPORTS.length}</span></div>
          <div className="row gap-sm">
            <button className="btn xs ghost"><Icon.calendar /> {lang === "fr" ? "Calendrier" : "Calendar"}</button>
            <button className="btn xs primary"><Icon.plus /> {lang === "fr" ? "Planifier" : "Schedule"}</button>
          </div>
        </div>
        <div className="card-body flush">
          <table className="tbl">
            <thead><tr>
              <th>{lang === "fr" ? "Rapport" : "Report"}</th>
              <th>{lang === "fr" ? "Format" : "Format"}</th>
              <th>{lang === "fr" ? "Portée" : "Scope"}</th>
              <th>{lang === "fr" ? "Fréquence" : "Frequency"}</th>
              <th>{lang === "fr" ? "Prochain envoi" : "Next send"}</th>
              <th>{lang === "fr" ? "Destinataires" : "Recipients"}</th>
              <th>{lang === "fr" ? "État" : "State"}</th>
            </tr></thead>
            <tbody>
              {REPORTS.map((r, i) => (
                <tr key={i}>
                  <td>
                    <div className="strong">{r.n}</div>
                    <div className="text-faint" style={{ fontSize: 11 }}>{r.last}</div>
                  </td>
                  <td><span className="pill xs" style={{ fontSize: 10 }}>{r.f}</span></td>
                  <td className="muted">{r.proj}</td>
                  <td className="muted">{r.freq}</td>
                  <td className="mono strong">{r.next}</td>
                  <td className="muted" style={{ fontSize: 12 }}>{r.to}</td>
                  <td><label className="nt-switch"><input type="checkbox" defaultChecked={r.on} /><span></span></label></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      {/* Upcoming calendar */}
      <div className="card" style={{ marginTop: 16 }}>
        <div className="card-head"><div className="card-title">{lang === "fr" ? "Prochains envois (14 jours)" : "Upcoming sends (14 days)"}</div></div>
        <div className="card-body">
          <div className="nt-cal">
            {Array.from({ length: 14 }).map((_, i) => {
              const d = new Date(); d.setDate(d.getDate() + i);
              const day = d.getDate();
              const events = [
                i === 0 && { t: "07:00", n: lang === "fr" ? "Dashboard santé" : "Health dashboard", tone: "accent" },
                i === 0 && { t: "08:00", n: lang === "fr" ? "Mémo hebdo S&E" : "Weekly M&E memo", tone: "violet" },
                i === 2 && { t: "—", n: lang === "fr" ? "Rapport bailleur (échéance)" : "Donor report (due)", tone: "amber" },
                i === 6 && { t: "08:00", n: lang === "fr" ? "Dashboard hebdo" : "Weekly dashboard", tone: "accent" },
                i === 7 && { t: "07:00", n: lang === "fr" ? "Dashboard santé" : "Health dashboard", tone: "accent" },
                i === 12 && { t: "09:00", n: lang === "fr" ? "Mémo mensuel direction" : "Monthly direction memo", tone: "green" },
              ].filter(Boolean);
              const isWk = d.getDay() === 0 || d.getDay() === 6;
              return (
                <div key={i} className={"nt-cal-day" + (i === 0 ? " today" : "") + (isWk ? " wk" : "")}>
                  <div className="nt-cal-d">{day}</div>
                  <div className="nt-cal-wd">{["DIM","LUN","MAR","MER","JEU","VEN","SAM"][d.getDay()]}</div>
                  <div className="nt-cal-events">
                    {events.map((e, j) => (
                      <div key={j} className={"nt-cal-ev tone-" + e.tone}>
                        <span className="mono" style={{ fontSize: 9.5, opacity: 0.7 }}>{e.t}</span>
                        <span>{e.n}</span>
                      </div>
                    ))}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}

// =====================================================================
// AlertRuleModal — create / edit a row in the alert_rules table.
// Uses the unified <Modal> shell. Condition can be entered either as
// JSON DSL (advanced) or via three simple fields (indicator / op / target)
// — the JSON path always wins if it parses, otherwise we synthesize one
// from the simple fields. This way users coming from the demo fixtures
// can express the same conditions ("écart > 10%") without learning JSON.
// =====================================================================
function AlertRuleModal({ lang, row, onClose, onSaved }) {
  const { useState, useMemo } = React;
  const isEdit = !!row;

  // Best-effort split: if the existing condition has indicator/op/target,
  // show the simple form; otherwise fall through to the JSON editor.
  const initialSimple = useMemo(() => {
    const c = row && row.condition;
    if (c && typeof c === "object" && c.indicator != null && c.op != null && c.target != null) {
      return { indicator: String(c.indicator), op: String(c.op), target: String(c.target) };
    }
    return { indicator: "", op: ">", target: "" };
  }, [row]);
  const initialMode = useMemo(() => {
    const c = row && row.condition;
    if (!c) return "simple";
    if (c.indicator != null && c.op != null && c.target != null) return "simple";
    return "json";
  }, [row]);

  const [name, setName] = useState(row ? (row.name || "") : "");
  const [frequency, setFrequency] = useState(row ? (row.frequency || "Temps réel") : (lang === "fr" ? "Temps réel" : "Real-time"));
  const [active, setActive] = useState(row ? row.active !== false : true);
  const [channels, setChannels] = useState(() => {
    const def = row && Array.isArray(row.channels) ? row.channels : ["in_app"];
    return new Set(def);
  });
  const [mode, setMode] = useState(initialMode);
  const [simple, setSimple] = useState(initialSimple);
  const [jsonText, setJsonText] = useState(() => {
    if (row && row.condition) {
      try { return JSON.stringify(row.condition, null, 2); } catch { return ""; }
    }
    return '{\n  "indicator": "PORT.1",\n  "op": "<",\n  "target": 0.9\n}';
  });
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);

  const CHANNEL_OPTIONS = [
    { value: "in_app", fr: "In-app", en: "In-app" },
    { value: "email",  fr: "Email",  en: "Email" },
    { value: "slack",  fr: "Slack",  en: "Slack" },
    { value: "sms",    fr: "SMS",    en: "SMS" },
  ];
  const FREQ_OPTIONS = lang === "fr"
    ? ["Temps réel", "Toutes les heures", "Toutes les 4 h", "Quotidien", "Hebdomadaire", "Mensuelle", "Programmée"]
    : ["Real-time", "Hourly", "Every 4 h", "Daily", "Weekly", "Monthly", "Scheduled"];
  const OP_OPTIONS = [">", ">=", "<", "<=", "==", "!="];

  const toggleChannel = (v) => {
    const next = new Set(channels);
    if (next.has(v)) next.delete(v); else next.add(v);
    setChannels(next);
  };

  const onSubmit = async () => {
    setErr(null);
    if (!name.trim()) { setErr(lang === "fr" ? "Nom requis." : "Name required."); return; }
    if (channels.size === 0) { setErr(lang === "fr" ? "Au moins un canal." : "At least one channel."); return; }

    // Build the condition payload.
    let condition;
    if (mode === "json") {
      try { condition = JSON.parse(jsonText); }
      catch (e) { setErr((lang === "fr" ? "JSON invalide : " : "Invalid JSON: ") + e.message); return; }
    } else {
      if (!simple.indicator.trim() || simple.target === "") {
        setErr(lang === "fr" ? "Indicateur et cible requis." : "Indicator and target required.");
        return;
      }
      const targetNum = Number(simple.target);
      condition = {
        indicator: simple.indicator.trim(),
        op: simple.op,
        target: Number.isFinite(targetNum) ? targetNum : simple.target,
      };
    }

    setBusy(true);
    try {
      const payload = {
        name: name.trim(),
        condition,
        channels: Array.from(channels),
        frequency: frequency.trim() || null,
        active: !!active,
      };
      if (isEdit) await window.melr.alertRulesCrud.update(row.id, payload);
      else        await window.melr.alertRulesCrud.create(payload);
      await onSaved();
    } catch (e) { setErr(e.message); }
    finally { setBusy(false); }
  };

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

  const Modal = window.Modal;
  return (
    <Modal
      title={isEdit
        ? (lang === "fr" ? "Modifier la règle" : "Edit rule")
        : (lang === "fr" ? "Nouvelle règle d'alerte" : "New alert rule")}
      onClose={busy ? null : onClose}
      size="md"
      footer={
        <>
          <button className="btn sm" onClick={onClose} disabled={busy}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button className="btn sm primary" onClick={onSubmit} disabled={busy}>
            {busy ? "…" : (lang === "fr" ? "Enregistrer" : "Save")}
          </button>
        </>
      }
    >
      <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Nom" : "Name"} *</label>
      <input style={inp} value={name} onChange={(e) => setName(e.target.value)}
        placeholder={lang === "fr" ? "ex : Indicateur en alerte" : "e.g. Indicator alerts"} />

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
        <div>
          <label style={lbl}>{lang === "fr" ? "Fréquence" : "Frequency"}</label>
          <select style={inp} value={frequency} onChange={(e) => setFrequency(e.target.value)}>
            {FREQ_OPTIONS.map((f) => <option key={f} value={f}>{f}</option>)}
          </select>
        </div>
        <div>
          <label style={lbl}>{lang === "fr" ? "Statut" : "Status"}</label>
          <label style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 8, fontSize: 12.5 }}>
            <input type="checkbox" checked={active} onChange={(e) => setActive(e.target.checked)} />
            <span>{lang === "fr" ? "Active" : "Active"}</span>
          </label>
        </div>
      </div>

      <label style={lbl}>{lang === "fr" ? "Canaux" : "Channels"} *</label>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
        {CHANNEL_OPTIONS.map((c) => {
          const checked = channels.has(c.value);
          return (
            <label key={c.value} style={{
              display: "flex", alignItems: "center", gap: 6,
              padding: "5px 10px", border: "1px solid var(--line)", borderRadius: 6,
              background: checked ? "var(--brand-tint, #eff6ff)" : "transparent",
              cursor: "pointer", fontSize: 12.5,
            }}>
              <input type="checkbox" checked={checked} onChange={() => toggleChannel(c.value)} />
              <span>{lang === "fr" ? c.fr : c.en}</span>
            </label>
          );
        })}
      </div>

      <label style={lbl}>{lang === "fr" ? "Condition" : "Condition"} *</label>
      <div className="seg" style={{ marginBottom: 8 }}>
        <button className={"seg-btn" + (mode === "simple" ? " active" : "")} onClick={() => setMode("simple")} type="button">
          {lang === "fr" ? "Simple" : "Simple"}
        </button>
        <button className={"seg-btn" + (mode === "json" ? " active" : "")} onClick={() => setMode("json")} type="button">
          JSON
        </button>
      </div>

      {mode === "simple" ? (
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr", gap: 8 }}>
          <div>
            <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Indicateur" : "Indicator"}</label>
            <input style={inp} value={simple.indicator}
              placeholder="ex: PORT.1"
              onChange={(e) => setSimple({ ...simple, indicator: e.target.value })} />
          </div>
          <div>
            <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Opérateur" : "Operator"}</label>
            <select style={inp} value={simple.op} onChange={(e) => setSimple({ ...simple, op: e.target.value })}>
              {OP_OPTIONS.map((o) => <option key={o} value={o}>{o}</option>)}
            </select>
          </div>
          <div>
            <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Cible" : "Target"}</label>
            <input style={inp} value={simple.target}
              placeholder="0.9"
              onChange={(e) => setSimple({ ...simple, target: e.target.value })} />
          </div>
        </div>
      ) : (
        <textarea style={{ ...inp, minHeight: 110, fontFamily: "var(--font-mono, monospace)" }}
          value={jsonText} onChange={(e) => setJsonText(e.target.value)}
          spellCheck={false} />
      )}

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

window.Notifications = Notifications;
