/* global React, window, Icon */
// ============================================================================
// Chantier I · Page Facturation (in-app, sous Paramètres)
// ----------------------------------------------------------------------------
// Visible par l'admin org + super-admin. Affiche :
//   - Plan actuel (carte d'identité : nom, prix, cycle, renouvellement)
//   - Barres d'usage par ressource (projets, users, indicateurs, sites,
//     stockage) avec code couleur vert/ambre/rouge selon seuil
//   - Bouton « Changer de plan » → page de tarification publique
//   - Bouton « Gérer le paiement » → Stripe Customer Portal (si abonné)
//   - Historique des événements de facturation (billing_events)
//
// Pour les admins non-Stripe : carte « Pour souscrire/changer, écrivez à
// it@reft-africa.org ». Pour les super-admins : possibilité d'override
// manuel du plan d'une organisation depuis la page Organisations
// (cette UI viendra dans un sous-chantier ultérieur).
// ============================================================================

(function () {
  const { useState } = React;

  function BillingScreen({ lang, isSuperAdmin, actingOrgId, setActingOrgId, effectiveOrgId, myOrgId, isAdmin, hasPerm }) {
    const orgId = effectiveOrgId || myOrgId || null;
    const { data: planData, loading: planLoading, refresh: refreshPlan } = window.melr.useOrgPlan(orgId);
    const { data: usage, loading: usageLoading } = window.melr.useUsageStats(orgId);
    const { data: events } = window.melr.useBillingEvents(orgId);

    const canManage = !!isSuperAdmin || !!isAdmin || (hasPerm && hasPerm("users.manage"));

    if (planLoading || usageLoading) {
      return <div className="page"><div style={{ padding: 28 }}>{window.L("Chargement…", "Loading…", "Cargando…")}</div></div>;
    }
    // Aucune organisation rattachée : 3 chemins selon le profil utilisateur.
    //   1) Super-admin sans home org → picker pour choisir laquelle « acter »
    //   2) Admin sans home org → message d'aide pointant vers SQL ou contact
    //   3) Utilisateur normal sans home org → contacter l'administrateur
    if (!planData || !planData.org) {
      if (isSuperAdmin) {
        return <SuperAdminOrgPicker lang={lang} setActingOrgId={setActingOrgId} />;
      }
      return (
        <div className="page">
          <div className="page-header">
            <div className="page-eyebrow">{window.L("PARAMÈTRES", "SETTINGS", "AJUSTES")}</div>
            <h1 className="page-title">{window.L("Facturation", "Billing", "Facturación")}</h1>
          </div>
          <div className="card" style={{ marginTop: 16, padding: 28, textAlign: "center" }}>
            <div style={{ fontSize: 36, marginBottom: 12 }}>🏢</div>
            <div style={{ fontSize: 16, fontWeight: 600, marginBottom: 8 }}>
              {window.L("Aucune organisation rattachée à votre compte", "No organization linked to your account", "Ninguna organización vinculada a su cuenta")}
            </div>
            <div style={{ fontSize: 13, color: "var(--text-faint)", maxWidth: 480, margin: "0 auto", lineHeight: 1.5 }}>
              {window.L("Votre profil n'est rattaché à aucune organisation. Demandez à un administrateur de vous ajouter à une org (Organisation & rôles → Membres) ou contactez ", "Your profile is not attached to any organization. Ask an administrator to add you to an org (Organization & roles → Members) or contact ", "Su perfil no está vinculado a ninguna organización. Pida a un administrador que lo añada a una organización (Organización y roles → Miembros) o póngase en contacto con ")}
              <a href="mailto:it@reft-africa.org" style={{ color: "#0f766e", fontWeight: 600 }}>it@reft-africa.org</a>.
            </div>
          </div>
        </div>
      );
    }

    const org = planData.org;
    const def = planData.definition;
    const usageRows = window.melr.usageVsLimits(usage, def);

    const fmtPrice = (cents, ccy) => {
      if (cents == null || cents === 0) return null;
      const v = ccy === "XOF" ? cents : cents / 100;
      try { return new Intl.NumberFormat(window.L("fr-FR", "en-US", "es-ES"), { style: "currency", currency: ccy, maximumFractionDigits: 0 }).format(v); }
      catch (_) { return v + " " + ccy; }
    };

    const planLabel = def ? ((lang === "es" ? (def.display_name_es != null ? def.display_name_es : def.display_name_en) : lang === "fr" ? def.display_name_fr : def.display_name_en)) : org.plan_code;
    const cycleLabel = org.plan_billing_cycle === "yearly" ? (window.L("annuel", "yearly", "anual"))
                    : org.plan_billing_cycle === "custom"  ? (window.L("personnalisé", "custom", "personalizado"))
                    : (window.L("mensuel", "monthly", "mensual"));
    const priceLabel = def && (org.plan_billing_cycle === "yearly" ? fmtPrice(def.price_yearly, org.plan_currency) : fmtPrice(def.price_monthly, org.plan_currency));

    const onChangePlan = () => {
      window.location.href = "/MELR?pricing=1";
    };

    return (
      <div className="page">
        <div className="page-header">
          <div className="page-eyebrow">{window.L("PARAMÈTRES", "SETTINGS", "AJUSTES")}</div>
          <h1 className="page-title">{window.L("Facturation", "Billing", "Facturación")}</h1>
          <div className="page-sub">
            {window.L("Plan actuel de votre organisation, utilisation et historique de facturation.", "Your organization's current plan, usage, and billing history.", "Plan actual de su organización, uso e historial de facturación.")}
          </div>
        </div>

        {/* Carte de suspension prioritaire — rouge, AVANT la carte plan */}
        {org.suspended_at && (
          <div style={{
            padding: "20px 22px", borderRadius: 10, marginBottom: 20,
            background: "linear-gradient(90deg,#fee2e2 0%,#fef2f2 100%)",
            border: "2px solid #dc2626",
            boxShadow: "0 4px 12px rgba(220,38,38,0.15)",
          }}>
            <div style={{ display: "flex", gap: 14, alignItems: "flex-start" }}>
              <span style={{ fontSize: 36 }}>🔒</span>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: "#7f1d1d", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 4 }}>
                  {window.L("ORGANISATION SUSPENDUE", "ORGANIZATION SUSPENDED", "ORGANIZACIÓN SUSPENDIDA")}
                </div>
                <div style={{ fontSize: 16, fontWeight: 700, color: "#7f1d1d", marginBottom: 6 }}>
                  {(() => {
                    const m = {
                      trial_expired:           { fr: "Votre période d'essai est terminée",         en: "Your trial period has ended", es: "Su período de prueba ha finalizado" },
                      payment_overdue_monthly: { fr: "Paiement mensuel en retard",                  en: "Monthly payment overdue", es: "Pago mensual atrasado" },
                      payment_overdue_yearly:  { fr: "Paiement annuel en retard",                   en: "Yearly payment overdue", es: "Pago anual atrasado" },
                    }[org.suspension_reason] || { fr: "Suspendue manuellement", en: "Manually suspended", es: "Suspendida manualmente" };
                    return (lang === "es" ? (m.es != null ? m.es : m.en) : lang === "fr" ? m.fr : m.en);
                  })()}
                </div>
                <div style={{ fontSize: 13, color: "#991b1b", lineHeight: 1.55 }}>
                  {window.L(
                    <>Suspendue depuis le <strong>{new Date(org.suspended_at).toLocaleDateString("fr-FR")}</strong>. Vous gardez l'accès en lecture seule à vos données ; toute saisie ou export Pro est désactivé jusqu'à régularisation. Cliquez sur « Changer de plan » ci-dessous pour souscrire à nouveau.</>,
                    <>Suspended since <strong>{new Date(org.suspended_at).toLocaleDateString("en-US")}</strong>. You still have read-only access ; any data entry or Pro export is disabled until settlement. Click "Change plan" below to subscribe again.</>,
                    <>Suspendida desde el <strong>{new Date(org.suspended_at).toLocaleDateString("es-ES")}</strong>. Conserva el acceso de solo lectura a sus datos ; toda entrada o exportación Pro está desactivada hasta la regularización. Haga clic en « Cambiar de plan » a continuación para suscribirse de nuevo.</>)}
                </div>
              </div>
            </div>
          </div>
        )}

        {/* Carte plan actuel — bandeau jaune si Free, vert si Pro, gris si Enterprise */}
        <div style={{
          padding: "20px 22px", borderRadius: 10,
          marginBottom: 20,
          background: org.plan_code === "pro" ? "linear-gradient(90deg,#dcfce7 0%,#f0fdf4 100%)"
                    : org.plan_code === "enterprise" ? "linear-gradient(90deg,#ede9fe 0%,#faf5ff 100%)"
                    : "linear-gradient(90deg,#fef3c7 0%,#fffbeb 100%)",
          border: "1px solid " + (org.plan_code === "pro" ? "#86efac" : org.plan_code === "enterprise" ? "#c4b5fd" : "#fcd34d"),
        }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16, flexWrap: "wrap" }}>
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 4 }}>
                {window.L("Plan actuel", "Current plan", "Plan actual")}
              </div>
              <div style={{ fontSize: 26, fontWeight: 800, color: "#0f172a", marginBottom: 4 }}>{planLabel}</div>
              <div style={{ fontSize: 13.5, color: "#475569" }}>
                {priceLabel
                  ? priceLabel + " / " + cycleLabel + (org.plan_currency ? " · " + org.plan_currency : "")
                  : (org.plan_code === "free" ? (window.L("Gratuit, sans engagement", "Free, no commitment", "Gratuito, sin compromiso"))
                                              : (window.L("Sur devis", "Custom quote", "Bajo presupuesto")))}
              </div>
              {org.plan_expires_at && (
                <div style={{ fontSize: 12, color: "#64748b", marginTop: 4 }}>
                  {window.L("Renouvellement : ", "Renews: ", "Renovación: ")}
                  {new Date(org.plan_expires_at).toLocaleDateString(window.L("fr-FR", "en-US", "es-ES"))}
                </div>
              )}
              {org.trial_ends_at && new Date(org.trial_ends_at) > new Date() && (
                <div style={{ marginTop: 6, padding: "4px 8px", background: "#fbbf24", color: "#78350f", borderRadius: 4, display: "inline-block", fontSize: 11, fontWeight: 700 }}>
                  {window.L("Essai jusqu'au ", "Trial until ", "Prueba hasta el ")}
                  {new Date(org.trial_ends_at).toLocaleDateString(window.L("fr-FR", "en-US", "es-ES"))}
                </div>
              )}
            </div>
            {canManage && (
              <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                <button onClick={onChangePlan}
                  style={{ padding: "10px 16px", borderRadius: 8, border: "none", background: "#0f766e", color: "white", fontWeight: 700, cursor: "pointer", fontSize: 13 }}>
                  {org.plan_code === "free" ? (window.L("Passer en Pro →", "Upgrade to Pro →", "Pasar a Pro →")) : (window.L("Changer de plan", "Change plan", "Cambiar de plan"))}
                </button>
                {org.stripe_customer_id && (
                  <a href="https://billing.stripe.com/p/login" target="_blank" rel="noopener noreferrer"
                    style={{ padding: "8px 14px", borderRadius: 8, border: "1px solid var(--line)", background: "white", color: "#0f172a", textDecoration: "none", fontSize: 12, textAlign: "center", fontWeight: 600 }}>
                    {window.L("Gérer le paiement ↗", "Manage payment ↗", "Gestionar el pago ↗")}
                  </a>
                )}
              </div>
            )}
          </div>
        </div>

        {/* Usage */}
        <div className="card" style={{ marginBottom: 20 }}>
          <div className="card-head">
            <div className="card-title">📊 {window.L("Utilisation actuelle", "Current usage", "Uso actual")}</div>
          </div>
          <div style={{ padding: 16, display: "flex", flexDirection: "column", gap: 12 }}>
            {usageRows.length === 0 ? (
              <div style={{ color: "var(--text-faint)", fontStyle: "italic" }}>
                {window.L("Pas de limite définie pour ce plan.", "No limit defined for this plan.", "No hay límite definido para este plan.")}
              </div>
            ) : usageRows.map((row) => <UsageBar key={row.resource} row={row} lang={lang} />)}
          </div>
        </div>

        {/* Historique facturation */}
        {events && events.length > 0 && (
          <div className="card" style={{ marginBottom: 20 }}>
            <div className="card-head">
              <div className="card-title">📜 {window.L("Historique de facturation", "Billing history", "Historial de facturación")}</div>
            </div>
            <div style={{ padding: 0 }}>
              <table className="tbl" style={{ width: "100%" }}>
                <thead>
                  <tr>
                    <th style={{ textAlign: "left", padding: "8px 14px" }}>{window.L("Date", "Date", "Fecha")}</th>
                    <th style={{ textAlign: "left", padding: "8px 14px" }}>{window.L("Événement", "Event", "Evento")}</th>
                    <th style={{ textAlign: "left", padding: "8px 14px" }}>{window.L("Plan", "Plan", "Plan")}</th>
                    <th style={{ textAlign: "right", padding: "8px 14px" }}>{window.L("Montant", "Amount", "Importe")}</th>
                  </tr>
                </thead>
                <tbody>
                  {events.map((ev) => (
                    <tr key={ev.id}>
                      <td style={{ padding: "6px 14px" }}>{new Date(ev.created_at).toLocaleDateString(window.L("fr-FR", "en-US", "es-ES"))}</td>
                      <td style={{ padding: "6px 14px" }}>{eventLabel(ev.event_type, lang)}</td>
                      <td style={{ padding: "6px 14px" }}>{ev.new_plan || "—"}</td>
                      <td style={{ padding: "6px 14px", textAlign: "right" }}>
                        {ev.amount_cents != null
                          ? fmtPrice(ev.amount_cents, ev.currency || "EUR")
                          : "—"}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        )}

        {/* Contact pour upgrade hors Stripe */}
        <div style={{ padding: 16, borderRadius: 8, background: "var(--bg-sunken)", border: "1px solid var(--line)", fontSize: 12.5, color: "var(--text-faint)" }}>
          {window.L(
            <>Besoin d'un plan personnalisé, d'un hébergement souverain ou d'une facturation par virement ? Écrivez à <a href="mailto:it@reft-africa.org" style={{ color: "#0f766e", fontWeight: 600 }}>it@reft-africa.org</a> — réponse sous 24 h.</>,
            <>Need a custom plan, sovereign hosting, or wire-transfer billing? Email <a href="mailto:it@reft-africa.org" style={{ color: "#0f766e", fontWeight: 600 }}>it@reft-africa.org</a> — reply within 24 h.</>,
            <>¿Necesita un plan personalizado, un alojamiento soberano o una facturación por transferencia ? Escriba a <a href="mailto:it@reft-africa.org" style={{ color: "#0f766e", fontWeight: 600 }}>it@reft-africa.org</a> — respuesta en 24 h.</>)}
        </div>

        {/* ─────────────────────────────────────────────────────────────────
            CONFIGURATION PLATEFORME (super-admin uniquement)
            Permet d'éditer les limites/prix des plans + activer les moyens
            de paiement (PayPal, Orange Money, Wave, …).
            ───────────────────────────────────────────────────────────── */}
        {isSuperAdmin && <PlatformAdminPanel lang={lang} />}
      </div>
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // PlatformAdminPanel : 2 cards super-admin pour configurer le SaaS
  //   - Plans tarifaires (limites, prix, features par plan × devise)
  //   - Moyens de paiement (enable/disable, kind, instructions)
  // Défense : guards autour des hooks window.melr.* (au cas où melr-data
  // arrive en retard), type="button" sur le toggle pour éviter qu'un parent
  // form ne l'intercepte, panneau d'erreur visible si quelque chose foire.
  // ──────────────────────────────────────────────────────────────────────
  function PlatformAdminPanel({ lang }) {
    const [open, setOpen] = useState(false);
    // Vérifie que la data layer est chargée avant d'autoriser le déploi.
    // Si l'utilisateur clique trop tôt après reload, on affiche un message
    // au lieu d'un dropdown vide qui prête à confusion.
    const ready = !!(window.melr
      && window.melr.usePlanDefinitions
      && window.melr.usePaymentMethods
      && window.melr.planDefinitionsCrud);
    return (
      <div style={{ marginTop: 28 }}>
        <button type="button"
          onClick={() => setOpen(!open)}
          style={{
            width: "100%", padding: "14px 18px",
            display: "flex", alignItems: "center", justifyContent: "space-between",
            background: open
              ? "linear-gradient(90deg,#c4b5fd 0%,#ddd6fe 100%)"
              : "linear-gradient(90deg,#ede9fe 0%,#faf5ff 100%)",
            border: "1px solid #c4b5fd", borderRadius: 10, cursor: "pointer",
            fontWeight: 700, fontSize: 14, color: "#5b21b6",
            transition: "background 120ms",
          }}>
          <span>👑 {window.L("Configuration plateforme (super-admin)", "Platform configuration (super-admin)", "Configuración de la plataforma (superadmin)")}</span>
          <span style={{ fontSize: 16, fontWeight: 800 }}>{open ? "▲" : "▼"}</span>
        </button>
        {open && !ready && (
          <div style={{ marginTop: 14, padding: 18, borderRadius: 8, background: "#fef3c7", border: "1px solid #fcd34d", color: "#78350f", fontSize: 13.5 }}>
            ⚠️ {window.L(
              <>La couche de données n'est pas encore chargée. Forcez le rafraîchissement (<strong>Ctrl+Shift+R</strong>) ou attendez 5 secondes puis re-cliquez.</>,
              <>Data layer not yet ready. Hard-refresh (<strong>Ctrl+Shift+R</strong>) or wait 5 seconds and re-click.</>,
              <>La capa de datos aún no está cargada. Fuerce la actualización (<strong>Ctrl+Shift+R</strong>) o espere 5 segundos y vuelva a hacer clic.</>)}
            <div style={{ marginTop: 10, fontSize: 11, color: "#92400e", fontFamily: "monospace" }}>
              usePlanDefinitions: {String(!!(window.melr && window.melr.usePlanDefinitions))} ·
              usePaymentMethods: {String(!!(window.melr && window.melr.usePaymentMethods))} ·
              planDefinitionsCrud: {String(!!(window.melr && window.melr.planDefinitionsCrud))}
            </div>
          </div>
        )}
        {open && ready && (
          <div style={{ marginTop: 14, display: "flex", flexDirection: "column", gap: 16 }}>
            <PlansAdminCard lang={lang} />
            <PaymentMethodsAdminCard lang={lang} />
          </div>
        )}
      </div>
    );
  }

  // ── Card 1 · Éditeur des plans (limites + prix + features) ──────────
  function PlansAdminCard({ lang }) {
    const [code, setCode] = useState("free");
    const [currency, setCurrency] = useState("EUR");
    const { data: plans, loading } = window.melr.usePlanDefinitions(currency);
    const current = (plans || []).find((p) => p.code === code) || null;
    const [draft, setDraft] = useState(null);
    const [saving, setSaving] = useState(false);
    const [savedAt, setSavedAt] = useState(null);
    const [err, setErr] = useState(null);

    // Synchronise le brouillon dès que la plan source change
    React.useEffect(() => {
      if (!current) { setDraft(null); return; }
      setDraft({ ...current, features: Array.isArray(current.features) ? current.features.slice() : [] });
    }, [current && current.code, current && current.currency, current && current.updated_at]);

    const onChange = (k, v) => setDraft((d) => ({ ...d, [k]: v }));
    const toggleFeature = (f) => setDraft((d) => {
      const list = (d.features || []).slice();
      const i = list.indexOf(f);
      if (i >= 0) list.splice(i, 1); else list.push(f);
      return { ...d, features: list };
    });

    // Ordre = ordre d'affichage. Les features "métier" (modules MELR) en
    // premier, les features "compliance" / Enterprise après. Chaque chip
    // affiche le LIBELLÉ multilingue + le code en monospace discret.
    const FEATURE_OPTIONS = [
      // ── Pro tier ────────────────────────────────────────────────────
      { code: "exante",            fr: "Évaluation ex-ante (Prix de Référence)", en: "Ex-ante appraisal (Reference Price)", es: "Evaluación ex ante (Precio de Referencia)" },
      { code: "dhis2",             fr: "Intégration DHIS2",                       en: "DHIS2 integration", es: "Integración DHIS2" },
      { code: "msc",               fr: "Histoires de changement (MSC)",           en: "Stories of change (MSC)", es: "Historias de cambio (MSC)" },
      { code: "learning",          fr: "Apprentissage (Questions + Histoires)",   en: "Learning (Questions + Stories)", es: "Aprendizaje (Preguntas + Historias)" },
      { code: "kobo_odk",          fr: "Intégration KoboToolbox / ODK Central",  en: "KoboToolbox / ODK Central integration", es: "Integración KoboToolbox / ODK Central" },
      { code: "portfolio",         fr: "Tableau de bord portefeuille",            en: "Portfolio dashboard", es: "Panel de control de cartera" },
      { code: "logframe_export",   fr: "Cadre logique exportable (donateur)",     en: "Logframe export (donor)", es: "Marco lógico exportable (donante)" },
      // ── Enterprise tier ─────────────────────────────────────────────
      { code: "ai_assist",         fr: "Assistant IA",                            en: "AI Assistant", es: "Asistente de IA" },
      { code: "api",               fr: "API publique REST",                       en: "Public REST API", es: "API pública REST" },
      { code: "white_label",       fr: "Marque blanche (white-label)",            en: "White-label", es: "Marca blanca (white-label)" },
      { code: "dedicated_support", fr: "Support dédié",                           en: "Dedicated support", es: "Soporte dedicado" },
      { code: "sla",               fr: "SLA contractuel 99,9 %",                  en: "99.9% contractual SLA", es: "SLA contractual del 99,9 %" },
      { code: "backup_dr",         fr: "Backup & Disaster Recovery",              en: "Backup & Disaster Recovery", es: "Backup y Disaster Recovery" },
    ];

    const onSave = async () => {
      if (!draft) return;
      setSaving(true); setErr(null);
      try {
        await window.melr.planDefinitionsCrud.update(code, currency, draft);
        setSavedAt(new Date());
      } catch (e) { setErr(e.message || String(e)); }
      finally { setSaving(false); }
    };

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

    return (
      <div className="card">
        <div className="card-head">
          <div className="card-title">📊 {window.L("Plans tarifaires", "Pricing plans", "Planes de precios")}</div>
          <div style={{ flex: 1 }} />
          {savedAt && (
            <span style={{ fontSize: 11, color: "#15803d", fontWeight: 600 }}>
              ✓ {window.L("Enregistré", "Saved", "Guardado")} {new Date(savedAt).toLocaleTimeString(window.L("fr-FR", "en-US", "es-ES"), { hour: "2-digit", minute: "2-digit" })}
            </span>
          )}
        </div>
        <div style={{ padding: 16 }}>
          {/* Sélecteurs plan + devise */}
          <div style={{ display: "flex", gap: 10, marginBottom: 16, flexWrap: "wrap" }}>
            <div>
              <label style={lbl}>{window.L("Plan", "Plan", "Plan")}</label>
              <select value={code} onChange={(e) => setCode(e.target.value)} style={{ ...inp, minWidth: 140 }}>
                <option value="free">Free</option>
                <option value="pro">Pro</option>
                <option value="enterprise">Enterprise</option>
              </select>
            </div>
            <div>
              <label style={lbl}>{window.L("Devise", "Currency", "Moneda")}</label>
              <select value={currency} onChange={(e) => setCurrency(e.target.value)} style={{ ...inp, minWidth: 100 }}>
                <option value="EUR">EUR</option>
                <option value="USD">USD</option>
                <option value="XOF">XOF</option>
              </select>
            </div>
          </div>

          {loading && <div style={{ color: "var(--text-faint)" }}>{window.L("Chargement…", "Loading…", "Cargando…")}</div>}
          {!loading && !current && (
            <div style={{ color: "#b91c1c", fontSize: 13 }}>
              {window.L("Cette combinaison plan + devise n'existe pas en BDD. Lancez la migration pricing pour la seeder.", "This plan + currency combo doesn't exist in DB. Run the pricing migration to seed it.", "Esta combinación de plan + divisa no existe en la BD. Ejecute la migración de precios para crearla.")}
            </div>
          )}
          {!loading && current && draft && (
            <>
              {/* Métadonnées affichage */}
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 14 }}>
                <div>
                  <label style={lbl}>{window.L("Nom affiché (FR)", "Display name (FR)", "Nombre mostrado (FR)")}</label>
                  <input value={draft.display_name_fr || ""} onChange={(e) => onChange("display_name_fr", e.target.value)} style={inp} />
                </div>
                <div>
                  <label style={lbl}>{window.L("Nom affiché (EN)", "Display name (EN)", "Nombre mostrado (EN)")}</label>
                  <input value={draft.display_name_en || ""} onChange={(e) => onChange("display_name_en", e.target.value)} style={inp} />
                </div>
                <div style={{ gridColumn: "1 / -1" }}>
                  <label style={lbl}>{window.L("Description (FR)", "Description (FR)", "Descripción (FR)")}</label>
                  <textarea value={draft.description_fr || ""} onChange={(e) => onChange("description_fr", e.target.value)} rows={2} style={{ ...inp, fontFamily: "inherit", resize: "vertical" }} />
                </div>
                <div style={{ gridColumn: "1 / -1" }}>
                  <label style={lbl}>{window.L("Description (EN)", "Description (EN)", "Descripción (EN)")}</label>
                  <textarea value={draft.description_en || ""} onChange={(e) => onChange("description_en", e.target.value)} rows={2} style={{ ...inp, fontFamily: "inherit", resize: "vertical" }} />
                </div>
              </div>

              {/* Prix */}
              <div style={{ padding: "12px 14px", background: "#fef3c7", borderRadius: 8, border: "1px solid #fcd34d", marginBottom: 14 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: "#92400e", textTransform: "uppercase", marginBottom: 8 }}>
                  💰 {window.L("Prix", "Pricing", "Precios")}{" "}
                  <span style={{ fontWeight: 400, fontStyle: "italic" }}>
                    ({currency === "XOF" ? "XOF entier" : (window.L("centimes — 9900 = 99,00 €", "cents — 9900 = $99.00", "céntimos — 9900 = 99,00 €"))})
                  </span>
                </div>
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
                  <div>
                    <label style={lbl}>{window.L("Mensuel", "Monthly", "Mensual")}</label>
                    <input type="number" min="0" value={draft.price_monthly == null ? "" : draft.price_monthly} onChange={(e) => onChange("price_monthly", e.target.value)} style={inp} />
                  </div>
                  <div>
                    <label style={lbl}>{window.L("Annuel", "Yearly", "Anual")}</label>
                    <input type="number" min="0" value={draft.price_yearly == null ? "" : draft.price_yearly} onChange={(e) => onChange("price_yearly", e.target.value)} style={inp} />
                  </div>
                </div>
              </div>

              {/* Limites quantitatives */}
              <div style={{ padding: "12px 14px", background: "var(--bg-sunken)", borderRadius: 8, border: "1px solid var(--line)", marginBottom: 14 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", marginBottom: 8 }}>
                  📊 {window.L("Limites (laisser vide = illimité)", "Limits (leave blank = unlimited)", "Límites (dejar en blanco = ilimitado)")}
                </div>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", gap: 10 }}>
                  <LimitField label={window.L("Projets", "Projects", "Proyectos")}             k="max_projects"   draft={draft} onChange={onChange} inp={inp} lbl={lbl} />
                  <LimitField label={window.L("Utilisateurs", "Users", "Usuarios")}           k="max_users"      draft={draft} onChange={onChange} inp={inp} lbl={lbl} />
                  <LimitField label={window.L("Indicateurs", "Indicators", "Indicadores")}       k="max_indicators" draft={draft} onChange={onChange} inp={inp} lbl={lbl} />
                  <LimitField label={window.L("Sites", "Sites", "Sitios")}                  k="max_sites"      draft={draft} onChange={onChange} inp={inp} lbl={lbl} />
                  <LimitField label={window.L("Stockage (MB)", "Storage (MB)", "Almacenamiento (MB)")}   k="max_storage_mb" draft={draft} onChange={onChange} inp={inp} lbl={lbl} />
                  <LimitField label={window.L("Exports / mois", "Exports / mo", "Exportaciones / mes")}  k="monthly_exports" draft={draft} onChange={onChange} inp={inp} lbl={lbl} />
                  <LimitField label={window.L("Crédits AI / mois", "AI credits / mo", "Créditos de IA / mes")} k="monthly_ai_credits" draft={draft} onChange={onChange} inp={inp} lbl={lbl} />
                </div>
              </div>

              {/* Features */}
              <div style={{ padding: "12px 14px", background: "var(--bg-sunken)", borderRadius: 8, border: "1px solid var(--line)", marginBottom: 14 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", marginBottom: 8 }}>
                  ✨ {window.L("Fonctionnalités incluses", "Included features", "Funcionalidades incluidas")}
                </div>
                <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
                  {FEATURE_OPTIONS.map((f) => {
                    const active = (draft.features || []).indexOf(f.code) >= 0;
                    const label  = (lang === "es" ? (f.es != null ? f.es : f.en) : lang === "fr" ? f.fr : f.en);
                    return (
                      <button key={f.code} type="button" onClick={() => toggleFeature(f.code)}
                        title={f.code}
                        style={{
                          padding: "5px 12px", borderRadius: 999,
                          background: active ? "#0f766e" : "var(--bg)",
                          color: active ? "white" : "var(--text)",
                          border: "1px solid " + (active ? "#0f766e" : "var(--line)"),
                          fontSize: 12, fontWeight: 600, cursor: "pointer",
                          display: "inline-flex", alignItems: "center", gap: 6,
                        }}>
                        {active ? <span>✓</span> : null}
                        <span>{label}</span>
                        <span style={{
                          fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
                          fontSize: 10, opacity: 0.5, fontWeight: 400,
                        }}>{f.code}</span>
                      </button>
                    );
                  })}
                </div>
              </div>

              {/* Actions */}
              {err && <div style={{ color: "#dc2626", fontSize: 12.5, marginBottom: 10 }}>{err}</div>}
              <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
                <button onClick={onSave} disabled={saving}
                  style={{ padding: "9px 16px", borderRadius: 6, border: "none", background: "#0f766e", color: "white", fontWeight: 700, cursor: "pointer", fontSize: 13 }}>
                  {saving ? (window.L("Enregistrement…", "Saving…", "Guardando…")) : (window.L("Enregistrer", "Save", "Guardar"))}
                </button>
              </div>
            </>
          )}
        </div>
      </div>
    );
  }

  function LimitField({ label, k, draft, onChange, inp, lbl }) {
    return (
      <div>
        <label style={lbl}>{label}</label>
        <input type="number" min="0"
          value={draft[k] == null ? "" : draft[k]}
          onChange={(e) => onChange(k, e.target.value === "" ? null : e.target.value)}
          placeholder="∞"
          style={inp} />
      </div>
    );
  }

  // ── Card 2 · Moyens de paiement ─────────────────────────────────────
  function PaymentMethodsAdminCard({ lang }) {
    const { data: methods, loading, refresh } = window.melr.usePaymentMethods();
    const [editing, setEditing] = useState(null);   // null | row | "new"

    const onToggle = async (m) => {
      try {
        await window.melr.paymentMethodsCrud.toggle(m.code, !m.enabled);
        await refresh();
      } catch (e) { alert(e.message); }
    };
    const onDelete = async (m) => {
      if (!confirm(window.L(
        "Supprimer le moyen de paiement « " + m.display_name_fr + " » ?",
        "Delete payment method '" + m.display_name_en + "'?",
        "¿Eliminar el método de pago « " + m.display_name_fr + " » ?"))) return;
      try {
        await window.melr.paymentMethodsCrud.remove(m.code);
        await refresh();
      } catch (e) { alert(e.message); }
    };

    return (
      <div className="card">
        <div className="card-head">
          <div className="card-title">💳 {window.L("Moyens de paiement", "Payment methods", "Métodos de pago")}</div>
          <div style={{ flex: 1 }} />
          <button onClick={() => setEditing("new")} className="btn sm primary">
            + {window.L("Ajouter", "Add", "Añadir")}
          </button>
        </div>
        <div style={{ padding: 0 }}>
          {loading && <div style={{ padding: 18, color: "var(--text-faint)" }}>{window.L("Chargement…", "Loading…", "Cargando…")}</div>}
          {!loading && methods.length === 0 && (
            <div style={{ padding: 18, color: "var(--text-faint)", fontSize: 13, lineHeight: 1.5 }}>
              {window.L(
                <>Aucun moyen de paiement configuré. <strong>La migration <code>20260526000000_payment_methods.sql</code> a-t-elle été appliquée ?</strong> Sans elle, la table <code>payment_methods</code> n'existe pas en base. Cliquez « + Ajouter » pour en créer manuellement.</>,
                <>No payment method configured. <strong>Was migration <code>20260526000000_payment_methods.sql</code> applied?</strong> Without it, the <code>payment_methods</code> table doesn't exist. Click "+ Add" to create one manually.</>,
                <>Ningún método de pago configurado. <strong>¿Se aplicó la migración <code>20260526000000_payment_methods.sql</code> ?</strong> Sin ella, la tabla <code>payment_methods</code> no existe en la base de datos. Haga clic en « + Añadir » para crear uno manualmente.</>)}
            </div>
          )}
          {!loading && methods.length > 0 && (
            <table className="tbl" style={{ width: "100%" }}>
              <thead>
                <tr>
                  <th style={{ textAlign: "left", padding: "8px 14px", width: 40 }}></th>
                  <th style={{ textAlign: "left", padding: "8px 14px" }}>{window.L("Nom", "Name", "Nombre")}</th>
                  <th style={{ textAlign: "left", padding: "8px 14px" }}>Code</th>
                  <th style={{ textAlign: "left", padding: "8px 14px" }}>Type</th>
                  <th style={{ textAlign: "left", padding: "8px 14px" }}>{window.L("Devises", "Currencies", "Divisas")}</th>
                  <th style={{ textAlign: "center", padding: "8px 14px" }}>{window.L("Actif", "Active", "Activo")}</th>
                  <th style={{ textAlign: "right", padding: "8px 14px" }}></th>
                </tr>
              </thead>
              <tbody>
                {methods.map((m) => (
                  <tr key={m.code}>
                    <td style={{ padding: "8px 14px", fontSize: 20, textAlign: "center" }}>{m.icon_emoji || "💳"}</td>
                    <td style={{ padding: "8px 14px", fontWeight: 600 }}>{(lang === "es" ? (m.display_name_es != null ? m.display_name_es : m.display_name_en) : lang === "fr" ? m.display_name_fr : m.display_name_en)}</td>
                    <td style={{ padding: "8px 14px", fontFamily: "monospace", fontSize: 11.5, color: "var(--text-faint)" }}>{m.code}</td>
                    <td style={{ padding: "8px 14px" }}>
                      <span style={{ padding: "2px 7px", borderRadius: 4, background: m.kind === "stripe" ? "#dbeafe" : m.kind === "redirect" ? "#fef3c7" : "#e0f2fe", color: m.kind === "stripe" ? "#1e40af" : m.kind === "redirect" ? "#78350f" : "#075985", fontSize: 11, fontWeight: 600 }}>
                        {m.kind}
                      </span>
                    </td>
                    <td style={{ padding: "8px 14px", fontSize: 11, color: "var(--text-faint)" }}>
                      {!m.currencies || m.currencies.length === 0 ? (window.L("Toutes", "All", "Todas")) : m.currencies.join(", ")}
                    </td>
                    <td style={{ padding: "8px 14px", textAlign: "center" }}>
                      <button onClick={() => onToggle(m)}
                        style={{ padding: "3px 12px", borderRadius: 999, border: "none", background: m.enabled ? "#15803d" : "#94a3b8", color: "white", cursor: "pointer", fontSize: 11, fontWeight: 700 }}>
                        {m.enabled ? "ON" : "OFF"}
                      </button>
                    </td>
                    <td style={{ padding: "8px 14px", textAlign: "right" }}>
                      <button onClick={() => setEditing(m)} className="btn xs" style={{ marginRight: 4 }}>✎</button>
                      <button onClick={() => onDelete(m)} className="btn xs" style={{ color: "#dc2626" }}>🗑</button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
        {editing && (
          <PaymentMethodEditor lang={lang} existing={editing === "new" ? null : editing}
            onClose={() => setEditing(null)} onSaved={refresh} />
        )}
      </div>
    );
  }

  // ── Modal d'édition / création d'un moyen de paiement ───────────────
  function PaymentMethodEditor({ lang, existing, onClose, onSaved }) {
    const isNew = !existing;
    const [code, setCode] = useState(existing ? existing.code : "");
    const [displayFr, setDisplayFr] = useState(existing ? existing.display_name_fr : "");
    const [displayEn, setDisplayEn] = useState(existing ? existing.display_name_en : "");
    const [icon, setIcon] = useState(existing ? (existing.icon_emoji || "💳") : "💳");
    const [descFr, setDescFr] = useState(existing ? (existing.description_fr || "") : "");
    const [descEn, setDescEn] = useState(existing ? (existing.description_en || "") : "");
    const [enabled, setEnabled] = useState(existing ? !!existing.enabled : false);
    const [kind, setKind] = useState(existing ? existing.kind : "manual");
    const [redirectUrl, setRedirectUrl] = useState(existing ? (existing.redirect_url || "") : "");
    const [instrFr, setInstrFr] = useState(existing ? (existing.instructions_fr || "") : "");
    const [instrEn, setInstrEn] = useState(existing ? (existing.instructions_en || "") : "");
    const [currencies, setCurrencies] = useState(existing && existing.currencies ? existing.currencies.join(", ") : "");
    const [planCodes, setPlanCodes] = useState(existing && existing.plan_codes ? existing.plan_codes.join(", ") : "");
    const [sortOrder, setSortOrder] = useState(existing ? (existing.sort_order || 100) : 100);
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState(null);
    const Modal = window.Modal;

    const onSubmit = async (e) => {
      e.preventDefault();
      if (!code.trim()) { setErr(window.L("Code requis (ex. paypal, orange_money).", "Code required.", "Código obligatorio (ej. paypal, orange_money).")); return; }
      if (!displayFr.trim() || !displayEn.trim()) { setErr(window.L("Noms FR + EN requis.", "FR + EN names required.", "Nombres FR + EN obligatorios.")); return; }
      setBusy(true); setErr(null);
      try {
        await window.melr.paymentMethodsCrud.upsert({
          code: code.trim().toLowerCase(),
          display_name_fr: displayFr.trim(),
          display_name_en: displayEn.trim(),
          icon_emoji: icon.trim() || null,
          description_fr: descFr.trim() || null,
          description_en: descEn.trim() || null,
          enabled, kind,
          redirect_url: kind === "redirect" ? (redirectUrl.trim() || null) : null,
          instructions_fr: kind === "manual" ? (instrFr.trim() || null) : null,
          instructions_en: kind === "manual" ? (instrEn.trim() || null) : null,
          currencies: currencies,    // CRUD parse les chaînes séparées par virgules
          plan_codes: planCodes,
          sort_order: sortOrder,
        });
        await onSaved();
        onClose();
      } catch (e) { setErr(e.message || String(e)); }
      finally { setBusy(false); }
    };

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

    return (
      <Modal lang={lang}
        title={isNew ? (window.L("Nouveau moyen de paiement", "New payment method", "Nuevo método de pago")) : (window.L("Modifier le moyen de paiement", "Edit payment method", "Editar el método de pago"))}
        onClose={onClose}>
        <form onSubmit={onSubmit} style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          <div style={{ display: "grid", gridTemplateColumns: "120px 1fr", gap: 12 }}>
            <div>
              <label style={lbl}>{window.L("Icône", "Icon", "Icono")}</label>
              <input value={icon} onChange={(e) => setIcon(e.target.value)} maxLength={4} style={{ ...inp, fontSize: 20, textAlign: "center", padding: "10px 6px" }} />
            </div>
            <div>
              <label style={lbl}>Code <span style={{ color: "#dc2626" }}>*</span></label>
              <input value={code} onChange={(e) => setCode(e.target.value.toLowerCase())} disabled={!isNew} placeholder="orange_money" required style={inp} />
            </div>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <div>
              <label style={lbl}>{window.L("Nom affiché (FR)", "Display (FR)", "Nombre mostrado (FR)")} *</label>
              <input value={displayFr} onChange={(e) => setDisplayFr(e.target.value)} required style={inp} />
            </div>
            <div>
              <label style={lbl}>{window.L("Nom affiché (EN)", "Display (EN)", "Nombre mostrado (EN)")} *</label>
              <input value={displayEn} onChange={(e) => setDisplayEn(e.target.value)} required style={inp} />
            </div>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <div>
              <label style={lbl}>{window.L("Description (FR)", "Description (FR)", "Descripción (FR)")}</label>
              <input value={descFr} onChange={(e) => setDescFr(e.target.value)} style={inp} />
            </div>
            <div>
              <label style={lbl}>{window.L("Description (EN)", "Description (EN)", "Descripción (EN)")}</label>
              <input value={descEn} onChange={(e) => setDescEn(e.target.value)} style={inp} />
            </div>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 100px", gap: 12 }}>
            <div>
              <label style={lbl}>{window.L("Type d'intégration", "Integration type", "Tipo de integración")}</label>
              <select value={kind} onChange={(e) => setKind(e.target.value)} style={inp}>
                <option value="manual">{window.L("Manuel (instructions)", "Manual (instructions)", "Manual (instrucciones)")}</option>
                <option value="redirect">{window.L("Redirection (URL externe)", "Redirect (external URL)", "Redirección (URL externa)")}</option>
                <option value="stripe">Stripe</option>
              </select>
            </div>
            <div>
              <label style={lbl}>{window.L("Ordre d'affichage", "Sort order", "Orden de visualización")}</label>
              <input type="number" value={sortOrder} onChange={(e) => setSortOrder(parseInt(e.target.value, 10) || 100)} style={inp} />
            </div>
            <div>
              <label style={lbl}>{window.L("Actif", "Active", "Activo")}</label>
              <button type="button" onClick={() => setEnabled(!enabled)}
                style={{ padding: "8px 10px", borderRadius: 6, border: "none", background: enabled ? "#15803d" : "#94a3b8", color: "white", cursor: "pointer", fontWeight: 700, width: "100%" }}>
                {enabled ? "ON" : "OFF"}
              </button>
            </div>
          </div>
          {kind === "redirect" && (
            <div>
              <label style={lbl}>{window.L("URL de redirection", "Redirect URL", "URL de redirección")}</label>
              <input value={redirectUrl} onChange={(e) => setRedirectUrl(e.target.value)} placeholder="https://www.paypal.com/..." style={inp} />
            </div>
          )}
          {kind === "manual" && (
            <>
              <div>
                <label style={lbl}>{window.L("Instructions (FR, Markdown)", "Instructions (FR, Markdown)", "Instrucciones (FR, Markdown)")}</label>
                <textarea value={instrFr} onChange={(e) => setInstrFr(e.target.value)} rows={6}
                  placeholder={window.L("1. Composez le code USSD…\n2. …", "1. Dial USSD code…\n2. …", "1. Marque el código USSD…\n2. …")}
                  style={{ ...inp, fontFamily: "inherit", resize: "vertical" }} />
              </div>
              <div>
                <label style={lbl}>{window.L("Instructions (EN, Markdown)", "Instructions (EN, Markdown)", "Instrucciones (EN, Markdown)")}</label>
                <textarea value={instrEn} onChange={(e) => setInstrEn(e.target.value)} rows={6} style={{ ...inp, fontFamily: "inherit", resize: "vertical" }} />
              </div>
            </>
          )}
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <div>
              <label style={lbl}>{window.L("Devises (vide = toutes)", "Currencies (empty = all)", "Divisas (vacío = todas)")}</label>
              <input value={currencies} onChange={(e) => setCurrencies(e.target.value)} placeholder="EUR, USD, XOF" style={inp} />
            </div>
            <div>
              <label style={lbl}>{window.L("Plans (vide = tous)", "Plans (empty = all)", "Planes (vacío = todos)")}</label>
              <input value={planCodes} onChange={(e) => setPlanCodes(e.target.value)} placeholder="pro, enterprise" style={inp} />
            </div>
          </div>
          {err && <div style={{ color: "#dc2626", fontSize: 12.5 }}>{err}</div>}
          <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 6 }}>
            <button type="button" onClick={onClose} disabled={busy} style={{ padding: "9px 16px", borderRadius: 6, border: "1px solid var(--line)", background: "var(--bg)", color: "var(--text)", cursor: "pointer" }}>
              {window.L("Annuler", "Cancel", "Cancelar")}
            </button>
            <button type="submit" disabled={busy} className="btn primary">
              {busy ? (window.L("Enregistrement…", "Saving…", "Guardando…")) : (window.L("Enregistrer", "Save", "Guardar"))}
            </button>
          </div>
        </form>
      </Modal>
    );
  }

  function UsageBar({ row, lang }) {
    const labels = {
      projects:   { fr: "Projets",     en: "Projects", es: "Proyectos" },
      users:      { fr: "Utilisateurs", en: "Users", es: "Usuarios" },
      indicators: { fr: "Indicateurs", en: "Indicators", es: "Indicadores" },
      sites:      { fr: "Sites",       en: "Sites", es: "Sitios" },
    };
    const lbl = labels[row.resource] || { fr: row.resource, en: row.resource };
    const colorByStatus = {
      ok:    { bar: "#10b981", bg: "#dcfce7", text: "#065f46" },
      warn:  { bar: "#f59e0b", bg: "#fef3c7", text: "#78350f" },
      block: { bar: "#dc2626", bg: "#fee2e2", text: "#7f1d1d" },
    };
    const c = colorByStatus[row.status] || colorByStatus.ok;
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4, fontSize: 13 }}>
          <span style={{ fontWeight: 600 }}>{(lang === "es" ? (lbl.es != null ? lbl.es : lbl.en) : lang === "fr" ? lbl.fr : lbl.en)}</span>
          <span style={{ color: "var(--text-faint)" }}>
            {row.used || 0} {row.unlimited ? "/ ∞" : "/ " + row.limit}
            {!row.unlimited && <span style={{ marginLeft: 8, padding: "1px 6px", background: c.bg, color: c.text, borderRadius: 999, fontSize: 11, fontWeight: 700 }}>{row.pct}%</span>}
          </span>
        </div>
        <div style={{ height: 8, background: "var(--bg-sunken)", borderRadius: 4, overflow: "hidden" }}>
          {row.unlimited ? (
            <div style={{ height: "100%", width: "100%", background: "linear-gradient(90deg,#dbeafe,#bfdbfe)" }} />
          ) : (
            <div style={{ height: "100%", width: Math.min(100, row.pct) + "%", background: c.bar, transition: "width 200ms" }} />
          )}
        </div>
        {row.status === "warn" && !row.unlimited && (
          <div style={{ marginTop: 4, fontSize: 11, color: c.text }}>
            {window.L("⚠ Vous approchez de la limite de votre plan.", "⚠ You're approaching your plan limit.", "⚠ Se está acercando al límite de su plan.")}
          </div>
        )}
        {row.status === "block" && !row.unlimited && (
          <div style={{ marginTop: 4, fontSize: 11, color: c.text, fontWeight: 600 }}>
            {window.L("🔒 Limite atteinte — passez à un plan supérieur pour continuer.", "🔒 Limit reached — upgrade to continue.", "🔒 Límite alcanzado — cambie a un plan superior para continuar.")}
          </div>
        )}
      </div>
    );
  }

  function eventLabel(type, lang) {
    const m = {
      subscribed:        { fr: "Abonnement créé",     en: "Subscription created", es: "Suscripción creada" },
      plan_changed:      { fr: "Changement de plan",  en: "Plan changed", es: "Cambio de plan" },
      cancelled:         { fr: "Abonnement annulé",   en: "Subscription cancelled", es: "Suscripción cancelada" },
      payment_succeeded: { fr: "Paiement réussi",     en: "Payment succeeded", es: "Pago realizado" },
      payment_failed:    { fr: "Paiement échoué",     en: "Payment failed", es: "Pago fallido" },
      trial_started:     { fr: "Essai démarré",       en: "Trial started", es: "Prueba iniciada" },
      trial_ended:       { fr: "Essai terminé",       en: "Trial ended", es: "Prueba finalizada" },
      reactivated:       { fr: "Réactivé",            en: "Reactivated", es: "Reactivado" },
    };
    const e = m[type] || { fr: type, en: type };
    return (lang === "es" ? (e.es != null ? e.es : e.en) : lang === "fr" ? e.fr : e.en);
  }

  // ──────────────────────────────────────────────────────────────────────
  // SuperAdminOrgPicker : affiché à la place du screen Facturation quand un
  // super-admin n'a pas d'organisation rattachée (profile.organization_id
  // NULL). Liste toutes les orgs actives et permet de choisir laquelle
  // « acter » — propage via setActingOrgId qui persiste dans localStorage.
  // ──────────────────────────────────────────────────────────────────────
  function SuperAdminOrgPicker({ lang, setActingOrgId }) {
    const { data: allOrgs, loading } = window.melr.useAllOrganizations
      ? window.melr.useAllOrganizations()
      : { data: [], loading: false };
    const [search, setSearch] = useState("");
    const visible = (allOrgs || []).filter((o) => !o.archived_at)
      .filter((o) => !search.trim() || (o.name || "").toLowerCase().includes(search.trim().toLowerCase()));

    return (
      <div className="page">
        <div className="page-header">
          <div className="page-eyebrow">{window.L("PARAMÈTRES", "SETTINGS", "AJUSTES")}</div>
          <h1 className="page-title">{window.L("Facturation", "Billing", "Facturación")}</h1>
          <div className="page-sub">
            {window.L("Votre compte super-administrateur n'a pas d'organisation par défaut. Choisissez l'organisation dont vous voulez consulter la facturation.", "Your super-admin account has no default organization. Pick which one's billing you'd like to view.", "Su cuenta de superadministrador no tiene organización por defecto. Elija la organización cuya facturación desea consultar.")}
          </div>
        </div>
        <div style={{
          marginTop: 16, padding: "16px 18px",
          borderRadius: 10, background: "linear-gradient(90deg,#ede9fe 0%,#faf5ff 100%)",
          border: "1px solid #c4b5fd", display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap",
        }}>
          <span style={{ fontSize: 22 }}>👑</span>
          <div style={{ flex: 1, minWidth: 200 }}>
            <div style={{ fontWeight: 700, color: "#5b21b6", marginBottom: 2 }}>
              {window.L("Mode super-administrateur", "Super-admin mode", "Modo superadministrador")}
            </div>
            <div style={{ fontSize: 12, color: "#6b21a8" }}>
              {window.L("Acter une organisation persiste dans votre navigateur. Vous pouvez en changer à tout moment depuis la barre du haut.", "Acting on an organization persists in your browser. You can switch at any time from the topbar.", "Actuar sobre una organización se conserva en su navegador. Puede cambiarla en cualquier momento desde la barra superior.")}
            </div>
          </div>
        </div>

        <div className="card" style={{ marginTop: 16 }}>
          <div className="card-head" style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <div className="card-title">{window.L("Organisations", "Organizations", "Organizaciones")}</div>
            <div style={{ flex: 1 }} />
            <input type="search" value={search} onChange={(e) => setSearch(e.target.value)}
              placeholder={window.L("Rechercher…", "Search…", "Buscar…")}
              style={{ padding: "6px 10px", borderRadius: 6, border: "1px solid var(--line)", background: "var(--bg)", fontSize: 12, minWidth: 200 }} />
          </div>
          {loading ? (
            <div style={{ padding: 28, textAlign: "center", color: "var(--text-faint)" }}>
              {window.L("Chargement des organisations…", "Loading organizations…", "Cargando organizaciones…")}
            </div>
          ) : visible.length === 0 ? (
            <div style={{ padding: 28, textAlign: "center", color: "var(--text-faint)" }}>
              {window.L("Aucune organisation trouvée.", "No organization found.", "No se ha encontrado ninguna organización.")}
            </div>
          ) : (
            <div style={{ padding: 8 }}>
              {visible.map((o) => (
                <button key={o.id} onClick={() => { setActingOrgId && setActingOrgId(o.id); }}
                  style={{
                    width: "100%", textAlign: "left",
                    padding: "12px 14px", marginBottom: 6,
                    border: "1px solid var(--line)", borderRadius: 8,
                    background: "var(--bg-elev)", cursor: "pointer",
                    display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12,
                  }}>
                  <div>
                    <div style={{ fontWeight: 600, fontSize: 14, color: "var(--text)" }}>{o.name}</div>
                    <div style={{ fontSize: 11, color: "var(--text-faint)", marginTop: 2 }}>
                      {o.slug} {o.plan_code ? " · " + (window.L("Plan ", "Plan ", "Plan ")) + o.plan_code : ""}
                    </div>
                  </div>
                  <span style={{ color: "#7c3aed", fontWeight: 700, fontSize: 13 }}>
                    {window.L("Acter →", "Act as →", "Actuar →")}
                  </span>
                </button>
              ))}
            </div>
          )}
        </div>

        <div style={{ marginTop: 16, padding: 14, borderRadius: 8, background: "var(--bg-sunken)", border: "1px solid var(--line)", fontSize: 12, color: "var(--text-faint)", lineHeight: 1.55 }}>
          <strong>{window.L("Astuce :", "Tip:", "Consejo:")}</strong>{" "}
          {window.L(
            <>pour qu'une organisation soit votre « org par défaut » (sans avoir à l'acter à chaque session), exécutez dans Supabase SQL Editor :{" "}
                <code style={{ background: "var(--bg)", padding: "1px 6px", borderRadius: 4, fontSize: 11 }}>
                  UPDATE profiles SET organization_id = '&lt;org-uuid&gt;' WHERE id = auth.uid()
                </code>
              </>,
            <>to make an organization your "default" (so you don't need to act on it every session), run in Supabase SQL Editor:{" "}
                <code style={{ background: "var(--bg)", padding: "1px 6px", borderRadius: 4, fontSize: 11 }}>
                  UPDATE profiles SET organization_id = '&lt;org-uuid&gt;' WHERE id = auth.uid()
                </code>
              </>,
            <>para que una organización sea su « org por defecto » (sin tener que confirmarla en cada sesión), ejecute en Supabase SQL Editor :{" "}
                <code style={{ background: "var(--bg)", padding: "1px 6px", borderRadius: 4, fontSize: 11 }}>
                  UPDATE profiles SET organization_id = '&lt;org-uuid&gt;' WHERE id = auth.uid()
                </code>
              </>)}
        </div>
      </div>
    );
  }

  window.BillingScreen = BillingScreen;

  // ──────────────────────────────────────────────────────────────────────
  // FeatureGate : composant de hard-gating Pro / Enterprise.
  // Si l'org de l'user a la feature → rend les children (passe-plat).
  // Sinon → rend un upsell screen (verrou + bouton Voir les plans).
  // Super-admin contourne TOUJOURS le gate (cf. useHasFeature).
  //
  // Usage : <FeatureGate code="exante" lang={lang}>
  //           <ExAnte {...screenProps} />
  //         </FeatureGate>
  // ──────────────────────────────────────────────────────────────────────
  function FeatureGate({ code, lang, featureLabel, children }) {
    const { allowed, loading, plan } = window.melr.useHasFeature(code);
    if (loading) {
      return <div className="page"><div style={{ padding: 28, color: "var(--text-faint)" }}>
        {window.L("Vérification de votre plan…", "Checking your plan…", "Comprobando su plan…")}
      </div></div>;
    }
    if (allowed) return children;
    return <FeatureUpsell code={code} lang={lang} featureLabel={featureLabel} currentPlan={plan} />;
  }
  window.FeatureGate = FeatureGate;

  // ── Upsell screen affiché quand le plan courant n'autorise pas la feature
  function FeatureUpsell({ code, lang, featureLabel, currentPlan }) {
    // Mapping code → libellé + description, mêmes textes que le catalogue
    // de la page de tarification (FEATURE_CATALOG dans screens-pricing.jsx).
    const META = {
      exante:           { fr: "Évaluation ex-ante par Prix de Référence", en: "Ex-ante appraisal by Reference Price", es: "Evaluación ex ante por Precio de Referencia",
                          emoji: "📈", required: "Pro",
                          desc_fr: "Module phare de MELR : analyse coût-bénéfice, multicritère, sensibilité, finances publiques. Génération de rapports d'instruction pré-projet exportables au format Word/PDF.",
                          desc_en: "MELR flagship module: CBA, multi-criteria, sensitivity, public finance. Generates pre-project appraisal reports exportable to Word/PDF." },
      dhis2:            { fr: "Intégration DHIS2", en: "DHIS2 integration", es: "Integración DHIS2", emoji: "💾", required: "Pro",
                          desc_fr: "Connexion bidirectionnelle avec votre instance DHIS2 (push valeurs, pull analytics, import catalogue).", desc_en: "Two-way connection with your DHIS2 instance." },
      msc:              { fr: "Histoires de changement (MSC)", en: "Stories of change (MSC)", es: "Historias de cambio (MSC)", emoji: "📖", required: "Pro",
                          desc_fr: "Collecte qualitative + sélection d'histoires significatives + synthèse exportable.", desc_en: "Qualitative collection + selection + exportable synthesis." },
      learning:         { fr: "Apprentissage (le L de MELR)", en: "Learning (the L in MELR)", es: "Aprendizaje (la L de MELR)", emoji: "🧠", required: "Pro",
                          desc_fr: "Questions d'apprentissage instruites par projet + Histoires de changement (MSC). Base d'évidence structurée et synthèse trans-projets pour les bailleurs.",
                          desc_en: "Project-level learning questions + Stories of Change (MSC). Structured evidence base and cross-project synthesis for donors." },
      kobo_odk:         { fr: "Intégration KoboToolbox / ODK Central", en: "KoboToolbox / ODK Central integration", es: "Integración KoboToolbox / ODK Central", emoji: "📲", required: "Pro",
                          desc_fr: "Connectez vos instances Kobo ou ODK Central et importez les soumissions de vos enquêtes terrain comme valeurs d'indicateurs MELR. Pull automatique avec mapping XLSForm → indicateur.",
                          desc_en: "Connect your Kobo or ODK Central instances and import field survey submissions as MELR indicator values. Automated pull with XLSForm → indicator mapping." },
      portfolio:        { fr: "Tableau de bord portefeuille", en: "Portfolio dashboard", es: "Panel de control de cartera", emoji: "📊", required: "Pro",
                          desc_fr: "Vue agrégée multi-projets pour bailleurs et PMU avec alertes RAG.", desc_en: "Aggregated multi-project view for donors/PMUs with RAG alerts." },
      logframe_export:  { fr: "Cadre logique exportable", en: "Logframe export", es: "Marco lógico exportable", emoji: "📋", required: "Pro",
                          desc_fr: "Génération du cadre logique standard 4 colonnes au format donateur (.docx).", desc_en: "Standard 4-column logframe export in donor format (.docx)." },
      ai_assist:        { fr: "Assistant IA", en: "AI Assistant", es: "Asistente de IA", emoji: "🤖", required: "Enterprise",
                          desc_fr: "Suggestions d'indicateurs, génération de PIRS, résumé exécutif, détection d'anomalies.", desc_en: "Indicator suggestions, PIRS generation, exec summary, anomaly detection." },
      api:              { fr: "API publique REST", en: "Public REST API", es: "API pública REST", emoji: "🔌", required: "Enterprise",
                          desc_fr: "Clés API par organisation, doc OpenAPI, intégrations PowerBI / scripts.", desc_en: "Per-org API keys, OpenAPI docs, PowerBI / script integrations." },
      white_label:      { fr: "Marque blanche", en: "White-label", es: "Marca blanca", emoji: "🎨", required: "Enterprise",
                          desc_fr: "Logo, couleurs, sous-domaine personnalisés.", desc_en: "Custom logo, colors, sub-domain." },
      dedicated_support:{ fr: "Support dédié", en: "Dedicated support", es: "Soporte dedicado", emoji: "🎧", required: "Enterprise",
                          desc_fr: "Account manager, chat + visio, réponse garantie.", desc_en: "Account manager, chat + video, guaranteed response." },
      sla:              { fr: "SLA contractuel 99,9 %", en: "99.9% SLA", es: "SLA contractual del 99,9 %", emoji: "⏱", required: "Enterprise",
                          desc_fr: "Garantie de disponibilité avec pénalités contractuelles.", desc_en: "Uptime guarantee with contractual penalties." },
      backup_dr:        { fr: "Backup & Disaster Recovery", en: "Backup & Disaster Recovery", es: "Backup y Disaster Recovery", emoji: "🛡", required: "Enterprise",
                          desc_fr: "Sauvegardes quotidiennes + procédure de restauration documentée + drill trimestriel.", desc_en: "Daily backups + documented restore procedure + quarterly drill." },
    };
    const m = META[code] || { fr: featureLabel || code, en: featureLabel || code, emoji: "🔒", required: "Pro" };
    const planLabel = currentPlan ? currentPlan.charAt(0).toUpperCase() + currentPlan.slice(1) : "—";

    return (
      <div className="page">
        <div style={{
          maxWidth: 680, margin: "60px auto 0", padding: "36px 32px",
          background: "white", border: "1px solid #e2e8f0", borderRadius: 14,
          boxShadow: "0 10px 40px rgba(0,0,0,0.08)", textAlign: "center",
        }}>
          <div style={{ fontSize: 64, marginBottom: 12 }}>{m.emoji}🔒</div>
          <div style={{
            display: "inline-block", padding: "4px 12px",
            background: "#fef3c7", color: "#78350f",
            borderRadius: 999, fontSize: 11, fontWeight: 700,
            letterSpacing: "0.05em", textTransform: "uppercase", marginBottom: 16,
          }}>
            {window.L("Fonctionnalité ", "Feature ", "Funcionalidad ")}{m.required}
          </div>
          <h1 style={{ fontSize: 28, fontWeight: 800, margin: "0 0 12px", color: "#0f172a" }}>
            {(lang === "es" ? (m.es != null ? m.es : m.en) : lang === "fr" ? m.fr : m.en)}
          </h1>
          <p style={{ fontSize: 14.5, color: "#475569", lineHeight: 1.6, maxWidth: 520, margin: "0 auto 26px" }}>
            {(lang === "es" ? (m.desc_es != null ? m.desc_es : m.desc_en) : lang === "fr" ? m.desc_fr : m.desc_en)}
          </p>
          <div style={{ padding: "14px 18px", background: "#f8fafc", borderRadius: 10, border: "1px solid #e2e8f0", marginBottom: 24, fontSize: 13 }}>
            <div style={{ color: "#64748b", marginBottom: 4 }}>
              {window.L("Votre plan actuel", "Your current plan", "Su plan actual")}
            </div>
            <div style={{ fontSize: 18, fontWeight: 700, color: "#0f172a" }}>{planLabel}</div>
            <div style={{ fontSize: 12, color: "#64748b", marginTop: 6 }}>
              {window.L(
                <>Passez en <strong>{m.required}</strong> pour débloquer cette fonctionnalité et 4 autres modules métier.</>,
                <>Upgrade to <strong>{m.required}</strong> to unlock this and 4 more business modules.</>,
                <>Cambie a <strong>{m.required}</strong> para desbloquear esta funcionalidad y 4 módulos de negocio más.</>)}
            </div>
          </div>
          <div style={{ display: "flex", gap: 10, justifyContent: "center", flexWrap: "wrap" }}>
            <a href="/MELR?pricing=1"
              style={{
                padding: "12px 22px", borderRadius: 8,
                background: "#0f766e", color: "white", textDecoration: "none",
                fontWeight: 700, fontSize: 14, display: "inline-flex", alignItems: "center", gap: 8,
              }}>
              {window.L("Voir les plans →", "View plans →", "Ver los planes →")}
            </a>
            <a href="mailto:it@reft-africa.org?subject=Upgrade%20MELR"
              style={{
                padding: "12px 22px", borderRadius: 8,
                background: "white", color: "#0f172a", textDecoration: "none",
                border: "1px solid #cbd5e1", fontWeight: 600, fontSize: 14,
              }}>
              {window.L("Contacter REFT", "Contact REFT", "Contactar con REFT")}
            </a>
          </div>
        </div>
      </div>
    );
  }
  window.FeatureUpsell = FeatureUpsell;

  // ──────────────────────────────────────────────────────────────────────
  // SuspensionBanner : bandeau rouge affiché sur TOUTES les pages quand
  // l'organisation courante est suspendue. Inséré une seule fois dans App
  // (cf. app.jsx), pas dans chaque écran. Super-admin voit le bandeau mais
  // pas le lock (peut continuer à naviguer pour diagnostiquer).
  // ──────────────────────────────────────────────────────────────────────
  function SuspensionBanner({ lang, isSuperAdmin, setRoute }) {
    const s = window.melr.useOrgSuspension();
    if (s.loading || !s.suspended) return null;
    const reasonLabel = {
      trial_expired:           { fr: "Votre période d'essai est terminée",       en: "Your trial period has ended", es: "Su período de prueba ha finalizado" },
      payment_overdue_monthly: { fr: "Paiement mensuel en retard",                en: "Monthly payment overdue", es: "Pago mensual atrasado" },
      payment_overdue_yearly:  { fr: "Paiement annuel en retard",                 en: "Yearly payment overdue", es: "Pago anual atrasado" },
    };
    const lbl = reasonLabel[s.reason] || { fr: "Organisation suspendue", en: "Organization suspended", es: "Organización suspendida" };
    const sinceStr = s.since
      ? new Date(s.since).toLocaleDateString(window.L("fr-FR", "en-US", "es-ES"), { day: "numeric", month: "short" })
      : null;
    return (
      <div style={{
        position: "sticky", top: 0, zIndex: 99998,
        background: "linear-gradient(90deg, #b91c1c 0%, #dc2626 100%)",
        color: "white", padding: "10px 18px",
        boxShadow: "0 2px 8px rgba(185,28,28,0.3)",
        display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap",
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, flex: 1, minWidth: 0 }}>
          <span style={{ fontSize: 22 }}>🔒</span>
          <div style={{ minWidth: 0 }}>
            <div style={{ fontWeight: 700, fontSize: 13.5 }}>
              {window.L("⚠ Service suspendu — ", "⚠ Service suspended — ", "⚠ Servicio suspendido — ")}
              {(lang === "es" ? (lbl.es != null ? lbl.es : lbl.en) : lang === "fr" ? lbl.fr : lbl.en)}
              {sinceStr && <span style={{ marginLeft: 8, opacity: 0.85, fontWeight: 400, fontSize: 12 }}>
                {window.L("depuis le " + sinceStr, "since " + sinceStr, "desde el " + sinceStr)}
              </span>}
            </div>
            <div style={{ fontSize: 11.5, opacity: 0.9, marginTop: 1 }}>
              {window.L("Vous gardez l'accès en lecture seule. Régularisez votre abonnement pour réactiver les saisies.", "You still have read-only access. Settle your subscription to re-enable editing.", "Conserva el acceso en solo lectura. Regularice su suscripción para reactivar la edición.")}
            </div>
          </div>
        </div>
        <div style={{ display: "flex", gap: 6, flexShrink: 0 }}>
          <button onClick={() => setRoute && setRoute("billing")}
            style={{ padding: "7px 14px", borderRadius: 6, border: "none", background: "white", color: "#b91c1c", fontWeight: 700, cursor: "pointer", fontSize: 12 }}>
            {window.L("Régulariser →", "Settle →", "Regularizar →")}
          </button>
          {isSuperAdmin && (
            <span style={{ padding: "5px 9px", borderRadius: 6, background: "rgba(255,255,255,0.2)", fontSize: 10.5, fontWeight: 700, alignSelf: "center", letterSpacing: "0.04em" }}>
              {window.L("ACCÈS SUPER-ADMIN", "SUPER-ADMIN BYPASS", "ACCESO SUPERADMIN")}
            </span>
          )}
        </div>
      </div>
    );
  }
  window.SuspensionBanner = SuspensionBanner;

  // ──────────────────────────────────────────────────────────────────────
  // UsageBadge : petit chip de quota toujours visible en sidebar.
  // Couleur = pire statut de tous les compteurs (block > warn > ok).
  // Click → ouvre Billing screen.
  // ──────────────────────────────────────────────────────────────────────
  function UsageBadge({ lang, setRoute }) {
    const { data: planData } = window.melr.useOrgPlan();
    const { data: usage } = window.melr.useUsageStats();
    if (!planData || !planData.definition || !usage) return null;
    const rows = window.melr.usageVsLimits(usage, planData.definition);
    if (rows.length === 0) return null;
    const hasBlock = rows.some((r) => r.status === "block");
    const hasWarn  = rows.some((r) => r.status === "warn");
    const status = hasBlock ? "block" : hasWarn ? "warn" : "ok";
    const colorMap = {
      ok:    { bg: "#dcfce7", border: "#86efac", text: "#065f46", icon: "✓" },
      warn:  { bg: "#fef3c7", border: "#fcd34d", text: "#78350f", icon: "⚠" },
      block: { bg: "#fee2e2", border: "#fca5a5", text: "#7f1d1d", icon: "🔒" },
    };
    const c = colorMap[status];
    const planLabel = (lang === "es" ? (planData.definition.display_name_es != null ? planData.definition.display_name_es : planData.definition.display_name_en) : lang === "fr" ? planData.definition.display_name_fr : planData.definition.display_name_en);
    // Stat la plus pertinente à afficher : celle au pct le plus élevé qui n'est pas illimitée
    const top = rows.filter((r) => !r.unlimited).sort((a, b) => b.pct - a.pct)[0];
    return (
      <button onClick={() => setRoute && setRoute("billing")}
        title={window.L(
          "Plan " + planLabel + " · cliquer pour voir l'utilisation et changer de plan",
          "Plan " + planLabel + " · click to view usage and change plan",
          "Plan " + planLabel + " · haga clic para ver el uso y cambiar de plan")}
        style={{
          display: "flex", alignItems: "center", gap: 6,
          padding: "5px 9px", borderRadius: 6,
          background: c.bg, border: "1px solid " + c.border, color: c.text,
          fontSize: 11, fontWeight: 600, cursor: "pointer", width: "100%",
          marginTop: 6,
        }}>
        <span>{c.icon}</span>
        <span style={{ flex: 1, textAlign: "left", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
          {planLabel}{top ? " · " + (top.used || 0) + "/" + top.limit + " " + top.resource : ""}
        </span>
      </button>
    );
  }
  window.UsageBadge = UsageBadge;
})();
