/* Uleez — Admin screens part 3: facturation loueurs (abonnements & créances)
 *
 * Suivi administratif des loueurs vus comme des CLIENTS d'Uleez. Deux natures d'argent
 * cohabitent volontairement dans un seul écran, parce que la question de l'admin est une seule :
 * « combien ce loueur me doit-il, et depuis quand ? »
 *   — la commission + les frais de service encaissés pour notre compte sur les locations du mois ;
 *   — l'abonnement qui rend l'agence visible sur les plateformes.
 *
 * Fichier séparé : admin2.jsx dépasse déjà 2 400 lignes.
 */
const { useState: useA3, useEffect: useAE3 } = React;

const PLAN_LABELS = { monthly: 'Mensuel', quarterly: 'Trimestriel', yearly: 'Annuel' };

const SUB_STATUS = {
  trial:     { label: 'Essai',     cls: 'blue' },
  active:    { label: 'Actif',     cls: 'green' },
  past_due:  { label: 'Échu',      cls: 'amber' },
  suspended: { label: 'Suspendu',  cls: 'red' },
  cancelled: { label: 'Résilié',   cls: 'gray' },
  expired:   { label: 'Expiré',    cls: 'gray' },
};

const INVOICE_STATUS = {
  draft:          { label: 'Brouillon', cls: 'gray' },
  issued:         { label: 'Émise',     cls: 'blue' },
  sent:           { label: 'Envoyée',   cls: 'blue' },
  partially_paid: { label: 'Partielle', cls: 'amber' },
  paid:           { label: 'Réglée',    cls: 'green' },
  overdue:        { label: 'En retard', cls: 'red' },
  void:           { label: 'Annulée',   cls: 'gray' },
};

const KIND_LABELS = { commission: 'Commission', subscription: 'Abonnement' };

/** AAAA-MM du mois précédent — la période que l'admin veut facturer par défaut. */
function previousPeriod() {
  const d = new Date();
  d.setMonth(d.getMonth() - 1);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
}

// ─── Écran principal ─────────────────────────────────────────────────────────
function AdminBilling({ navigate, params, setParams }) {
  const toast = useToast();
  const [tab, setTab] = useA3('invoices');
  const [summary, setSummary] = useA3(null);
  const [invoices, setInvoices] = useA3([]);
  const [subs, setSubs] = useA3([]);
  // Suivi des abonnements : population des contrats, MRR, encaissé, échéances, churn.
  const [subRevenue, setSubRevenue] = useA3(null);
  const [loading, setLoading] = useA3(true);
  const [err, setErr] = useA3(false);

  const [filters, setFilters] = useA3({ kind: '', status: '', overdueOnly: '', period: '', search: '' });
  // Recherche par loueur : saisie locale, appliquée après une courte pause. Le filtrage se fait
  // CÔTÉ SERVEUR (la liste est paginée à 200 : un filtre local mentirait sur l'absence de résultat).
  const [searchInput, setSearchInput] = useA3('');
  useAE3(() => {
    const id = setTimeout(() => setFilters(f => (f.search === searchInput.trim() ? f : { ...f, search: searchInput.trim() })), 350);
    return () => clearTimeout(id);
  }, [searchInput]);
  const [paying, setPaying] = useA3(null);
  const [voiding, setVoiding] = useA3(null);
  const [subAction, setSubAction] = useA3(null);   // { sub, mode }
  const [creating, setCreating] = useA3(false);
  const [generating, setGenerating] = useA3(false);
  // Sélection pour le règlement groupé — la seule façon praticable de réconcilier un
  // historique rattrapé.
  const [selected, setSelected] = useA3([]);
  const [bulkPaying, setBulkPaying] = useA3(false);

  const anchor = /^\d{4}-(0[1-9]|1[0-2])$/.test(params?.anchor || '') ? params.anchor : null;
  const setAnchor = (a) =>
    setParams(a ? { ...params, anchor: a } : (({ anchor, ...rest }) => rest)(params || {}));

  const load = async () => {
    setLoading(true);
    setErr(false);
    const date = anchorToISO(anchor);
    try {
      const [s, inv, sb, rev] = await Promise.all([
        API.admin.getBillingSummary(),
        API.admin.getInvoices({ ...filters, limit: 200 }),
        API.admin.getSubscriptions(),
        API.admin.getSubscriptionSummary(date).catch(() => null),
      ]);
      setSummary(s);
      setInvoices(inv?.data || []);
      setSubs(Array.isArray(sb) ? sb : []);
      setSubRevenue(rev);
      // Une sélection qui survivrait à un rechargement pointerait des factures qui ne sont
      // peut-être plus à l'écran — et le règlement groupé porterait sur l'invisible.
      setSelected([]);
    } catch {
      setErr(true);
    } finally {
      setLoading(false);
    }
  };

  useAE3(() => { load(); }, []);
  useAE3(() => { if (!loading) load(); }, [filters.kind, filters.status, filters.overdueOnly, filters.period, filters.search, anchor]);

  const setFilter = (k, v) => setFilters(f => ({ ...f, [k]: v }));

  // Seules les créances réellement soldables entrent dans une sélection.
  const payable = invoices.filter(i => i.remaining > 0 && i.status !== 'void' && i.status !== 'paid');
  const toggleOne = (id) =>
    setSelected(s => (s.includes(id) ? s.filter(x => x !== id) : [...s, id]));
  const toggleAll = () =>
    setSelected(s => (s.length === payable.length ? [] : payable.map(i => i.id)));
  const selectedTotal = invoices
    .filter(i => selected.includes(i.id))
    .reduce((s, i) => s + (i.remaining || 0), 0);

  const bulkPay = async () => {
    setBulkPaying(true);
    try {
      const res = await API.admin.bulkRecordInvoicePayment(selected, { method: 'cash' });
      toast({
        variant: res.settled > 0 ? 'success' : 'info',
        title: `${res.settled} créance(s) réglée(s)`,
        // On NOMME les échecs : « 12 réglées » sur 15 sélectionnées laisserait l'admin croire
        // que tout est passé, et les trois restantes deviendraient invisibles.
        desc: res.failed?.length ? `${res.failed.length} en échec — voir le détail dans la liste` : undefined,
      });
      load();
    } catch (ex) {
      toast({ variant: 'error', title: 'Règlement groupé impossible', desc: ex.message });
    } finally {
      setBulkPaying(false);
    }
  };

  const download = (inv) => API.admin.downloadInvoice(inv.id, inv.reference)
    .catch(() => toast({ variant: 'error', title: 'Téléchargement impossible' }));

  const send = async (inv, dryRun) => {
    try {
      const res = await API.admin.sendInvoice(inv.id, dryRun);
      toast({
        variant: 'success',
        title: dryRun ? 'E-mail de test envoyé' : 'Facture envoyée',
        desc: res?.to || inv.lessorEmail,
      });
      if (!dryRun) load();
    } catch (ex) {
      toast({ variant: 'error', title: 'Envoi impossible', desc: ex.message });
    }
  };

  const generate = async () => {
    const period = previousPeriod();
    setGenerating(true);
    try {
      const res = await API.admin.generateInvoices({ period, kind: 'commission' });
      toast({
        variant: res.generated > 0 ? 'success' : 'info',
        title: `${res.generated} facture(s) générée(s)`,
        desc: res.generated > 0 ? `Période ${period}` : `Aucune créance à facturer sur ${period}`,
      });
      load();
    } catch (ex) {
      toast({ variant: 'error', title: 'Génération impossible', desc: ex.message });
    } finally {
      setGenerating(false);
    }
  };

  // ── Encours ────────────────────────────────────────────────────────────────
  const ageCards = summary ? [
    { key: 'd0_30', label: '0 – 30 jours', color: 'green' },
    { key: 'd31_60', label: '31 – 60 jours', color: 'amber' },
    { key: 'd61_90', label: '61 – 90 jours', color: 'violet' },
    { key: 'd90plus', label: '+ de 90 jours', color: 'rose' },
  ] : [];

  return (
    <div className="page-wide">
      <div className="toolbar" style={{ marginBottom: 14 }}>
        <div>
          <h2 style={{ margin: 0, fontSize: 22, fontWeight: 800, letterSpacing: '-.5px' }}>Facturation loueurs</h2>
          <div className="muted" style={{ fontSize: 14, marginTop: 2 }}>
            Abonnements, commissions et frais de service à reverser à Uleez
          </div>
        </div>
        <div className="spacer" />
        <Button variant="secondary" icon="refresh" size="sm" onClick={load}>Actualiser</Button>
        <Button variant="secondary" icon="plus" size="sm" onClick={() => setCreating(true)}>Nouvel abonnement</Button>
        <Button variant="primary" icon="banknote" size="sm" loading={generating} onClick={generate}>
          Générer {previousPeriod()}
        </Button>
      </div>

      {/* Un total d'encours seul ne dit rien : 300 000 DA récents et 300 000 DA impayés
          depuis 3 mois appellent des actions opposées. D'où la ventilation par ancienneté.

          Le titre explicite ce que « encours » recouvre. Quatre tuiles nues affichant 0 ne
          disaient pas si la plateforme allait bien ou si rien n'avait été facturé. */}
      <div className="muted" style={{ fontSize: 12, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.5px', marginBottom: 8 }}>
        Créances ouvertes, par ancienneté depuis l'échéance
      </div>
      <div className="stat-grid" style={{ marginBottom: 18 }}>
        {loading
          ? [0, 1, 2, 3].map(i => <Sk key={i} w="100%" h={110} r={16} />)
          : err
            ? <div className="card card-pad" style={{ gridColumn: '1/-1' }}>
                <EmptyState variant="err" title="Impossible de charger la facturation"
                  action={<Button variant="secondary" icon="refresh" onClick={load}>Réessayer</Button>} />
              </div>
            : (summary?.totalOutstanding || 0) === 0
              // Zéro partout a deux causes opposées — tout est réglé, ou rien n'a été
              // facturé — et quatre tuiles à 0 ne permettaient pas de les distinguer.
              ? <div className="card card-pad" style={{ gridColumn: '1/-1' }}>
                  <EmptyState variant="empty" icon="checkCircle"
                    title="Aucune créance ouverte"
                    desc={summary?.invoiceCount === 0
                      ? "Aucune facture n'est en attente de règlement. Les créances apparaissent ici dès qu'une facture est émise et non soldée."
                      : 'Tout ce qui a été facturé est réglé.'} />
                </div>
              : ageCards.map(c => (
                  <StatCard key={c.key} icon="wallet" color={c.color}
                    value={KD.fmtNum(summary.ageBuckets?.[c.key] || 0)}
                    label={`Impayé depuis ${c.label} (DA)`}
                    meta={c.key === 'd90plus' ? 'Risque de suspension' : undefined} />
                ))}
      </div>

      {!loading && !err && summary && (
        <div className="card card-pad" style={{ marginBottom: 18, display: 'flex', gap: 26, flexWrap: 'wrap' }}>
          <div>
            <div className="muted" style={{ fontSize: 12.5 }}>Encours total</div>
            <div style={{ fontSize: 20, fontWeight: 800 }}>{KD.fmtDA(summary.totalOutstanding)}</div>
          </div>
          <div>
            <div className="muted" style={{ fontSize: 12.5 }}>dont commissions</div>
            <div style={{ fontSize: 20, fontWeight: 800 }}>{KD.fmtDA(summary.byKind?.commission || 0)}</div>
          </div>
          <div>
            <div className="muted" style={{ fontSize: 12.5 }}>dont abonnements</div>
            <div style={{ fontSize: 20, fontWeight: 800 }}>{KD.fmtDA(summary.byKind?.subscription || 0)}</div>
          </div>
          <div>
            <div className="muted" style={{ fontSize: 12.5 }}>Créances ouvertes</div>
            <div style={{ fontSize: 20, fontWeight: 800 }}>{summary.invoiceCount}</div>
          </div>
        </div>
      )}

      <div className="chips" style={{ marginBottom: 14 }}>
        <Chip active={tab === 'invoices'} count={invoices.length} onClick={() => setTab('invoices')}>Créances</Chip>
        <Chip active={tab === 'subs'} count={subs.length} onClick={() => setTab('subs')}>Abonnements</Chip>
        <Chip active={tab === 'lessors'} count={summary?.byLessor?.length || 0} onClick={() => setTab('lessors')}>Par loueur</Chip>
      </div>

      {tab === 'invoices' && (
        <Card>
          <div className="toolbar" style={{ padding: '12px 14px', borderBottom: '1px solid var(--line)' }}>
            <Select value={filters.kind} onChange={e => setFilter('kind', e.target.value)}>
              <option value="">Toutes natures</option>
              <option value="commission">Commission</option>
              <option value="subscription">Abonnement</option>
            </Select>
            <Select value={filters.status} onChange={e => setFilter('status', e.target.value)}>
              <option value="">Tous statuts</option>
              {Object.entries(INVOICE_STATUS).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </Select>
            <Select value={filters.overdueOnly} onChange={e => setFilter('overdueOnly', e.target.value)}>
              <option value="">Échéance : toutes</option>
              <option value="true">En retard uniquement</option>
            </Select>
            <input className="inp" style={{ width: 130 }} placeholder="AAAA-MM"
              value={filters.period} onChange={e => setFilter('period', e.target.value)} />
            <input className="inp" style={{ width: 210 }} placeholder="Loueur (nom ou e-mail)"
              value={searchInput} onChange={e => setSearchInput(e.target.value)} />
            <div className="spacer" />
            {selected.length > 0 && (
              <Button variant="primary" icon="checkCircle" size="sm" loading={bulkPaying} onClick={bulkPay}>
                Régler {selected.length} créance(s) — {KD.fmtDA(selectedTotal)}
              </Button>
            )}
          </div>

          {loading
            ? <div className="card-pad"><TableSkeleton cols={8} /></div>
            : invoices.length === 0
              ? <EmptyState variant="empty" icon="wallet" title="Aucune créance"
                  desc="Générez les factures de commission du mois écoulé pour les voir apparaître ici." />
              : <div className="tbl-wrap"><table className="tbl">
                  <thead><tr>
                    <th style={{ width: 34 }}>
                      <input type="checkbox"
                        title="Tout sélectionner"
                        disabled={payable.length === 0}
                        checked={payable.length > 0 && selected.length === payable.length}
                        onChange={toggleAll} />
                    </th>
                    <th>Référence</th><th>Loueur</th><th>Nature</th><th>Période</th>
                    <th>Échéance</th>
                    <th style={{ textAlign: 'right' }}>Montant</th>
                    <th style={{ textAlign: 'right' }}>Restant</th>
                    <th>Statut</th>
                    <th style={{ textAlign: 'right' }}>Actions</th>
                  </tr></thead>
                  <tbody>
                    {invoices.map(inv => (
                      <tr key={inv.id}>
                        <td>
                          {/* Seules les créances soldables sont cochables : proposer de régler
                              une facture déjà payée ou annulée n'aboutirait qu'à un échec. */}
                          <input type="checkbox"
                            disabled={!(inv.remaining > 0 && inv.status !== 'void' && inv.status !== 'paid')}
                            checked={selected.includes(inv.id)}
                            onChange={() => toggleOne(inv.id)} />
                        </td>
                        <td className="mono strong">{inv.reference}</td>
                        <td>
                          <div className="cellstack">
                            <div className="truncate" title={inv.businessName}>{inv.businessName}</div>
                            <div className="sub truncate">{inv.lessorEmail || '— pas d\'e-mail —'}</div>
                          </div>
                        </td>
                        <td>{KIND_LABELS[inv.kind] || inv.kind}</td>
                        <td className="nowrap">{inv.periodLabel}</td>
                        <td className="nowrap">
                          <div className="cellstack">
                            <div>{inv.dueAt ? new Date(inv.dueAt).toLocaleDateString('fr-FR') : '—'}</div>
                            {inv.isOverdue && <div className="sub" style={{ color: 'var(--rose-600, #e11d48)' }}>
                              {inv.daysOverdue} j de retard
                            </div>}
                          </div>
                        </td>
                        <td className="cellnum" style={{ textAlign: 'right' }}>{KD.fmtDA(inv.amountDue)}</td>
                        <td className="cellnum" style={{ textAlign: 'right', fontWeight: 800 }}>
                          {inv.remaining > 0 ? KD.fmtDA(inv.remaining) : '—'}
                        </td>
                        <td><StatusBadge map={INVOICE_STATUS} value={inv.status} /></td>
                        <td style={{ textAlign: 'right' }}>
                          <Dropdown
                            trigger={<button className="btn btn-secondary btn-icon sm"><Icon name="moreV" size={16} /></button>}
                            items={[
                              { label: 'Télécharger le PDF', onClick: () => download(inv) },
                              { label: 'Envoyer au loueur', onClick: () => send(inv, false) },
                              // Filet de sécurité avant la première vraie campagne : relire
                              // le document tel qu'il arrivera, avec de vraies données.
                              { label: 'M\'envoyer un test', onClick: () => send(inv, true) },
                              ...(inv.status !== 'paid' && inv.status !== 'void'
                                ? [{ label: 'Enregistrer un règlement', onClick: () => setPaying(inv) }]
                                : []),
                              ...(inv.status !== 'void'
                                ? [{ label: 'Annuler la facture', danger: true, onClick: () => setVoiding(inv) }]
                                : []),
                            ]}
                          />
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table></div>}
        </Card>
      )}

      {tab === 'subs' && (
        <>
        {/* L'ancre ne concerne QUE cet onglet : « perçu ce mois » et le churn sont des
            chiffres de période, alors que les créances et les contrats sont des états
            courants. Poser le navigateur en haut de l'écran laisserait croire qu'il déplace
            aussi l'encours, qui n'a pas de mois. */}
        <PeriodNavigator
          anchor={anchor}
          onChange={setAnchor}
          disabled={loading}
          sub="Revenus d'abonnement et résiliations de ce mois"
        />
        {/* Ce que l'abonnement RAPPORTE, distinct de ce qu'il reste dû.
            `recognized` étale un plan annuel sur les mois qu'il couvre — c'est ce qui rend
            mois, trimestre et année comparables entre eux. `collected` est la trésorerie
            réelle, encaissement par encaissement : c'est la réponse à « combien ai-je
            effectivement perçu en abonnements ? ». Les deux sont affichés parce qu'ils
            répondent à deux questions différentes et qu'on les confond facilement. */}
        {!loading && subRevenue && (
          <Card style={{ marginBottom: 16 }}>
            <CardHead title="Revenus d'abonnement" sub="Ce que les loueurs paient pour exister sur la plateforme"
              icon={{ name: 'building', color: 'violet' }} />
            <div className="card-pad">
              <div className="stat-grid">
                <div className="statcard" style={{ borderLeft: '3px solid #7c3aed' }}>
                  <div className="muted" style={{ fontSize: 12, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.5px' }}>Revenu récurrent mensuel</div>
                  <div style={{ fontSize: 24, fontWeight: 800, marginTop: 6, color: '#7c3aed' }}>{KD.fmtDA(subRevenue.mrr)}</div>
                  <div className="muted" style={{ fontSize: 11.5, marginTop: 4 }}>
                    {subRevenue.counts?.live || 0} contrat(s) vivant(s) · {KD.fmtDA(subRevenue.arr)} / an
                  </div>
                </div>
                {[
                  ['Perçu — mois', subRevenue.collected?.month, subRevenue.recognized?.month, '#059669'],
                  ['Perçu — trimestre', subRevenue.collected?.quarter, subRevenue.recognized?.quarter, '#0284c7'],
                  ['Perçu — année', subRevenue.collected?.year, subRevenue.recognized?.year, '#0f766e'],
                ].map(([label, cash, rec, c]) => (
                  <div key={label} className="statcard" style={{ borderLeft: `3px solid ${c}` }}>
                    <div className="muted" style={{ fontSize: 12, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.5px' }}>{label}</div>
                    <div style={{ fontSize: 24, fontWeight: 800, marginTop: 6, color: c }}>{KD.fmtDA(cash || 0)}</div>
                    {/* Deux chiffres qui ne s'égalisent pas, et ne le doivent pas :
                        « perçu » est la CAISSE (le versement compte en entier, le jour où il
                        entre) ; « reconnu » est la COMPTABILITÉ (le tarif étalé sur la période
                        couverte, seule base sur laquelle mois/trimestre/année s'additionnent).
                        L'écart n'est anormal que s'il vient d'un règlement jamais saisi — ce
                        que la ligne ci-dessous dit explicitement. */}
                    <div className="muted" style={{ fontSize: 11.5, marginTop: 4, lineHeight: 1.5 }}>
                      encaissé (caisse) · {KD.fmtDA(rec || 0)} reconnu au prorata
                    </div>
                    {(rec || 0) > (cash || 0) && (
                      <div style={{ fontSize: 11, marginTop: 4, color: 'var(--amber-700)', fontWeight: 700 }}>
                        {KD.fmtDA((rec || 0) - (cash || 0))} facturés sans règlement saisi
                      </div>
                    )}
                  </div>
                ))}
              </div>

              {/* Un contrat en période de bienvenue n'émet AUCUNE facture — c'est la règle,
                  pas une anomalie. Sans cette mention, ses 0 DA passent pour un bug. */}
              {(subRevenue.counts?.trial || 0) > 0 && (
                <div className="banner-info" style={{ marginTop: 12 }}>
                  <strong>{subRevenue.counts.trial} contrat(s) en période de bienvenue.</strong>{' '}
                  Aucune facture d'abonnement n'est émise tant qu'elle dure : ils ne comptent
                  ni dans le perçu, ni dans l'encours, ni dans le revenu récurrent.
                </div>
              )}

              <div className="row" style={{ gap: 26, flexWrap: 'wrap', marginTop: 14, paddingTop: 14, borderTop: '1px solid var(--line)' }}>
                {[
                  ['En essai', subRevenue.counts?.trial, null],
                  ['Actifs', subRevenue.counts?.active, null],
                  ['Échus', subRevenue.counts?.past_due, 'var(--amber-700)'],
                  ['Suspendus', subRevenue.counts?.suspended, 'var(--rose-700)'],
                  ['Encours abonnements', KD.fmtDA(subRevenue.outstanding || 0), null],
                  [`Résiliations ${subRevenue.churn?.periodLabel || ''}`, `${subRevenue.churn?.ended || 0} (${subRevenue.churn?.rate || 0} %)`, null],
                ].map(([label, val, color]) => (
                  <div key={label}>
                    <div className="muted" style={{ fontSize: 12 }}>{label}</div>
                    <div style={{ fontSize: 18, fontWeight: 800, color: color || undefined }}>{val ?? 0}</div>
                  </div>
                ))}
              </div>

              {/* La liste qui a de la valeur opérationnelle : qui sera coupé, et quand. */}
              {subRevenue.atRisk?.length > 0 && (
                <div className="banner-warn" style={{ marginTop: 14 }}>
                  <strong>{subRevenue.atRisk.length} loueur(s) seront suspendus automatiquement</strong>
                  <div style={{ marginTop: 6, lineHeight: 1.7 }}>
                    {subRevenue.atRisk.slice(0, 5).map(a => (
                      <div key={a.subscriptionId}>
                        {a.businessName} — échu depuis {a.daysPastDue} j, coupure le{' '}
                        {new Date(a.graceEndsAt).toLocaleDateString('fr-FR')}
                      </div>
                    ))}
                    {subRevenue.atRisk.length > 5 && <div className="muted">… et {subRevenue.atRisk.length - 5} autre(s)</div>}
                  </div>
                </div>
              )}

              {subRevenue.expiringSoon?.length > 0 && (
                <div className="banner-info" style={{ marginTop: 10 }}>
                  <strong>{subRevenue.expiringSoon.length} échéance(s) dans les 30 jours.</strong>{' '}
                  Prochaine : {subRevenue.expiringSoon[0].businessName} dans {subRevenue.expiringSoon[0].daysToExpiry} j.
                </div>
              )}
            </div>
          </Card>
        )}

        <Card>
          {loading
            ? <div className="card-pad"><TableSkeleton cols={7} /></div>
            : subs.length === 0
              ? <EmptyState variant="empty" icon="building" title="Aucun abonnement"
                  desc="Créez un abonnement pour suivre la visibilité payante d'une agence." />
              : <div className="tbl-wrap"><table className="tbl">
                  <thead><tr>
                    <th>Loueur</th><th>Plan</th>
                    <th style={{ textAlign: 'right' }}>Tarif</th>
                    <th>Échéance</th><th>Renouvellement</th>
                    <th style={{ textAlign: 'right' }}>Encours</th>
                    <th>Statut</th>
                    <th style={{ textAlign: 'right' }}>Actions</th>
                  </tr></thead>
                  <tbody>
                    {subs.map(s => (
                      <tr key={s.id}>
                        <td>
                          <div className="cellstack">
                            <div className="truncate" title={s.businessName}>{s.businessName}</div>
                            <div className="sub truncate">{s.lessorEmail || '— pas d\'e-mail —'}</div>
                          </div>
                        </td>
                        <td>{PLAN_LABELS[s.plan] || s.plan}</td>
                        <td className="cellnum" style={{ textAlign: 'right' }}>{KD.fmtDA(s.priceAmount)}</td>
                        <td className="nowrap">
                          <div className="cellstack">
                            <div>{new Date(s.currentPeriodEnd).toLocaleDateString('fr-FR')}</div>
                            <div className="sub" style={{ color: s.daysToExpiry <= 15 ? 'var(--amber-700)' : undefined }}>
                              {s.daysToExpiry >= 0 ? `dans ${s.daysToExpiry} j` : `échu depuis ${-s.daysToExpiry} j`}
                            </div>
                          </div>
                        </td>
                        <td>{s.autoRenew ? 'Automatique' : 'Manuel'}</td>
                        <td className="cellnum" style={{ textAlign: 'right', fontWeight: 800, color: s.outstanding > 0 ? 'var(--rose-600, #e11d48)' : undefined }}>
                          {s.outstanding > 0 ? KD.fmtDA(s.outstanding) : '—'}
                        </td>
                        <td><StatusBadge map={SUB_STATUS} value={s.status} /></td>
                        <td style={{ textAlign: 'right' }}>
                          <Dropdown
                            trigger={<button className="btn btn-secondary btn-icon sm"><Icon name="moreV" size={16} /></button>}
                            items={[
                              { label: 'Renouveler la période', onClick: () => setSubAction({ sub: s, mode: 'renew' }) },
                              ...(s.status === 'suspended'
                                ? [{ label: 'Réactiver', onClick: () => setSubAction({ sub: s, mode: 'reactivate' }) }]
                                : [{ label: 'Suspendre', danger: true, onClick: () => setSubAction({ sub: s, mode: 'suspend' }) }]),
                              { label: 'Résilier', danger: true, onClick: () => setSubAction({ sub: s, mode: 'cancel' }) },
                            ]}
                          />
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table></div>}
        </Card>
        </>
      )}

      {tab === 'lessors' && (
        <Card>
          {loading
            ? <div className="card-pad"><TableSkeleton cols={4} /></div>
            : !summary?.byLessor?.length
              ? <EmptyState variant="empty" icon="building" title="Aucun encours"
                  desc="Tous les loueurs sont à jour de leurs règlements." />
              : <div className="tbl-wrap"><table className="tbl">
                  <thead><tr>
                    <th>Loueur</th>
                    <th style={{ textAlign: 'right' }}>Encours</th>
                    <th>Créance la plus ancienne</th>
                    <th style={{ textAlign: 'right' }}>Actions</th>
                  </tr></thead>
                  <tbody>
                    {summary.byLessor.map(l => (
                      <tr key={l.lessorId}>
                        <td className="strong">{l.businessName}</td>
                        <td className="cellnum" style={{ textAlign: 'right', fontWeight: 800 }}>{KD.fmtDA(l.outstanding)}</td>
                        <td>
                          {l.oldestDays > 0
                            ? <Badge cls={l.oldestDays > 90 ? 'red' : l.oldestDays > 30 ? 'amber' : 'gray'}>
                                {l.oldestDays} j de retard
                              </Badge>
                            : <span className="muted">Dans les délais</span>}
                        </td>
                        <td style={{ textAlign: 'right' }}>
                          <Button size="sm" variant="ghost" iconRight="chevRight"
                            onClick={() => { setFilters(f => ({ ...f, kind: '', status: '' })); setTab('invoices'); }}>
                            Voir les créances
                          </Button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table></div>}
        </Card>
      )}

      {paying && <RecordInvoicePaymentModal invoice={paying} onClose={() => setPaying(null)}
        onSuccess={() => { setPaying(null); load(); }} />}
      {voiding && <VoidInvoiceModal invoice={voiding} onClose={() => setVoiding(null)}
        onSuccess={() => { setVoiding(null); load(); }} />}
      {subAction && <SubscriptionActionModal {...subAction} onClose={() => setSubAction(null)}
        onSuccess={() => { setSubAction(null); load(); }} />}
      {creating && <CreateSubscriptionModal onClose={() => setCreating(false)}
        onSuccess={() => { setCreating(false); load(); }} />}
    </div>
  );
}

// ─── Modales ─────────────────────────────────────────────────────────────────

function RecordInvoicePaymentModal({ invoice, onClose, onSuccess }) {
  const toast = useToast();
  const [saving, setSaving] = useA3(false);
  const [amount, setAmount] = useA3(String(invoice.remaining));
  const [method, setMethod] = useA3('virement');
  const [reference, setReference] = useA3('');

  const submit = async () => {
    const value = Number(amount);
    if (!Number.isFinite(value) || value <= 0) {
      toast({ variant: 'error', title: 'Montant invalide' });
      return;
    }
    setSaving(true);
    try {
      await API.admin.recordInvoicePayment(invoice.id, { amount: value, method, reference: reference || undefined });
      toast({ variant: 'success', title: 'Règlement enregistré', desc: invoice.reference });
      onSuccess();
    } catch (ex) {
      toast({ variant: 'error', title: 'Erreur', desc: ex.message });
    } finally {
      setSaving(false);
    }
  };

  return (
    <Modal title="Enregistrer un règlement" desc={`${invoice.reference} · ${invoice.businessName}`}
      icon={{ name: 'banknote', color: 'green' }} onClose={onClose}
      footer={<>
        <Button variant="secondary" onClick={onClose}>Annuler</Button>
        <Button variant="primary" icon="check" loading={saving} onClick={submit}>Enregistrer</Button>
      </>}>
      <div className="kv"><span className="k">Montant dû</span><span className="v">{KD.fmtDA(invoice.amountDue)}</span></div>
      <div className="kv"><span className="k">Déjà réglé</span><span className="v">{KD.fmtDA(invoice.amountPaid)}</span></div>
      <div className="kv" style={{ marginBottom: 12 }}>
        <span className="k">Restant</span><span className="v strong">{KD.fmtDA(invoice.remaining)}</span>
      </div>
      <Field label="Montant reçu (DA)">
        <Input type="number" value={amount} onChange={e => setAmount(e.target.value)} />
      </Field>
      <Field label="Moyen de règlement">
        <Select value={method} onChange={e => setMethod(e.target.value)}>
          <option value="virement">Virement</option>
          <option value="especes">Espèces</option>
          <option value="cheque">Chèque</option>
          <option value="compensation">Compensation</option>
        </Select>
      </Field>
      <Field label="Référence (facultatif)">
        <Input value={reference} onChange={e => setReference(e.target.value)} placeholder="N° de virement, de chèque…" />
      </Field>
    </Modal>
  );
}

function VoidInvoiceModal({ invoice, onClose, onSuccess }) {
  const toast = useToast();
  const [saving, setSaving] = useA3(false);
  const [reason, setReason] = useA3('');
  const tooShort = reason.trim().length < 5;

  const submit = async () => {
    if (tooShort) return;
    setSaving(true);
    try {
      await API.admin.voidInvoice(invoice.id, reason.trim());
      toast({ variant: 'info', title: 'Facture annulée', desc: invoice.reference });
      onSuccess();
    } catch (ex) {
      toast({ variant: 'error', title: 'Erreur', desc: ex.message });
    } finally {
      setSaving(false);
    }
  };

  return (
    <Modal title="Annuler la facture" desc={`${invoice.reference} · ${invoice.businessName}`}
      icon={{ name: 'alert', color: 'amber' }} onClose={onClose}
      footer={<>
        <Button variant="secondary" onClick={onClose}>Retour</Button>
        <Button variant="danger" icon="x" loading={saving} disabled={tooShort} onClick={submit}>Annuler la facture</Button>
      </>}>
      <div className="muted" style={{ fontSize: 13, lineHeight: 1.55, marginBottom: 12 }}>
        La facture reste en base et son détail est conservé — elle cesse simplement d'être une créance.
      </div>
      <Field label="Motif (obligatoire)">
        <Textarea rows={3} value={reason} onChange={e => setReason(e.target.value)}
          placeholder="Ex. : facture émise en double." />
      </Field>
    </Modal>
  );
}

function SubscriptionActionModal({ sub, mode, onClose, onSuccess }) {
  const toast = useToast();
  const [saving, setSaving] = useA3(false);
  const [reason, setReason] = useA3('');
  const [force, setForce] = useA3(false);

  const needsReason = mode === 'suspend' || mode === 'cancel' || (mode === 'reactivate' && force);
  const tooShort = needsReason && reason.trim().length < 5;

  const CONFIG = {
    renew: {
      title: 'Renouveler la période',
      icon: { name: 'refresh', color: 'green' },
      cta: 'Renouveler',
      variant: 'primary',
      body: `La période sera prolongée d'un cycle ${(PLAN_LABELS[sub.plan] || sub.plan).toLowerCase()} et une créance d'abonnement de ${KD.fmtDA(sub.priceAmount)} sera émise.`,
    },
    suspend: {
      title: "Suspendre l'abonnement",
      icon: { name: 'alert', color: 'amber' },
      cta: 'Suspendre',
      variant: 'danger',
      body: "L'abonnement et le compte loueur passent en suspendu : l'agence n'apparaît plus sur les plateformes Uleez.",
    },
    reactivate: {
      title: "Réactiver l'abonnement",
      icon: { name: 'check', color: 'green' },
      cta: 'Réactiver',
      variant: 'primary',
      body: sub.outstanding > 0
        ? `Ce loueur doit encore ${KD.fmtDA(sub.outstanding)}. La réactivation est bloquée tant que la créance n'est pas réglée — sauf à la forcer avec un motif.`
        : 'Le compte loueur redevient visible sur les plateformes Uleez.',
    },
    cancel: {
      title: "Résilier l'abonnement",
      icon: { name: 'x', color: 'rose' },
      cta: 'Résilier',
      variant: 'danger',
      body: "Le contrat est clos. L'historique de facturation reste consultable.",
    },
  };
  const cfg = CONFIG[mode];

  const submit = async () => {
    if (tooShort) return;
    setSaving(true);
    try {
      if (mode === 'renew') await API.admin.renewSubscription(sub.id);
      else if (mode === 'suspend') await API.admin.suspendSubscription(sub.id, reason.trim());
      else if (mode === 'cancel') await API.admin.cancelSubscription(sub.id, reason.trim());
      else await API.admin.reactivateSubscription(sub.id, force ? { force: true, reason: reason.trim() } : {});
      toast({ variant: 'success', title: cfg.title, desc: sub.businessName });
      onSuccess();
    } catch (ex) {
      toast({ variant: 'error', title: 'Erreur', desc: ex.message });
    } finally {
      setSaving(false);
    }
  };

  return (
    <Modal title={cfg.title} desc={sub.businessName} icon={cfg.icon} onClose={onClose}
      footer={<>
        <Button variant="secondary" onClick={onClose}>Retour</Button>
        <Button variant={cfg.variant} loading={saving} disabled={tooShort} onClick={submit}>{cfg.cta}</Button>
      </>}>
      <div className="muted" style={{ fontSize: 13, lineHeight: 1.55, marginBottom: 12 }}>{cfg.body}</div>
      {mode === 'reactivate' && sub.outstanding > 0 && (
        <label className="col-menu-row" style={{ padding: '8px 0' }}>
          <Checkbox checked={force} onChange={() => setForce(f => !f)} />
          <span>Forcer la réactivation malgré l'encours</span>
        </label>
      )}
      {needsReason && (
        <Field label="Motif (obligatoire)">
          <Textarea rows={3} value={reason} onChange={e => setReason(e.target.value)} />
        </Field>
      )}
    </Modal>
  );
}

function CreateSubscriptionModal({ onClose, onSuccess }) {
  const toast = useToast();
  const [saving, setSaving] = useA3(false);
  const [lessors, setLessors] = useA3([]);
  // `markPaid` coché PAR DÉFAUT : le cas nominal est l'ouverture d'un contrat au comptoir,
  // après encaissement. Décocher reste possible pour un contrat ouvert avant paiement — la
  // facture est alors émise et reste ouverte, et le recouvrement s'applique normalement.
  const [form, setForm] = useA3({
    lessorId: '', plan: 'monthly', priceAmount: '', autoRenew: true,
    markPaid: true, paymentMethod: 'cash', paymentReference: '',
  });

  useAE3(() => {
    API.admin.getLessors('approved')
      .then(d => setLessors(Array.isArray(d) ? d.map(API.normalizeLessor) : []))
      .catch(() => setLessors([]));
  }, []);

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const submit = async () => {
    if (!form.lessorId) { toast({ variant: 'error', title: 'Choisissez un loueur' }); return; }
    setSaving(true);
    try {
      const res = await API.admin.createSubscription({
        lessorId: form.lessorId,
        plan: form.plan,
        // Vide = tarif courant des réglages. Le serveur exige alors que les tarifs aient été
        // confirmés par un admin : c'est ce qui empêche un prix indicatif de partir en facture.
        priceAmount: form.priceAmount === '' ? undefined : Number(form.priceAmount),
        autoRenew: form.autoRenew,
        markPaid: form.markPaid,
        paymentMethod: form.markPaid ? form.paymentMethod : undefined,
        paymentReference: form.markPaid && form.paymentReference ? form.paymentReference : undefined,
      });
      // On DIT ce qui a été fait de la facture. « Abonnement créé » tout court laissait
      // l'admin découvrir plus tard, sur un écran à 0 DA, qu'aucune créance n'avait été émise.
      toast({
        variant: 'success',
        title: 'Abonnement créé',
        desc: res?.invoice
          ? (res.payment
              ? `Facture ${res.invoice.reference} émise et réglée`
              : `Facture ${res.invoice.reference} émise — règlement à enregistrer`)
          : (res?.inTrial
              ? 'Période de bienvenue en cours : aucune facture émise'
              : 'Aucune facture émise'),
      });
      onSuccess();
    } catch (ex) {
      toast({ variant: 'error', title: 'Création impossible', desc: ex.message });
    } finally {
      setSaving(false);
    }
  };

  return (
    <Modal title="Nouvel abonnement" desc="Suivi de la visibilité payante d'une agence"
      icon={{ name: 'building', color: 'green' }} onClose={onClose}
      footer={<>
        <Button variant="secondary" onClick={onClose}>Annuler</Button>
        <Button variant="primary" icon="check" loading={saving} onClick={submit}>Créer</Button>
      </>}>
      <Field label="Loueur">
        <Select value={form.lessorId} onChange={e => set('lessorId', e.target.value)}>
          <option value="">— Choisir —</option>
          {lessors.map(l => <option key={l.id} value={l.id}>{l.name}</option>)}
        </Select>
      </Field>
      <Field label="Formule">
        <Select value={form.plan} onChange={e => set('plan', e.target.value)}>
          {Object.entries(PLAN_LABELS).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
        </Select>
      </Field>
      <Field label="Tarif (DA) — laisser vide pour le tarif configuré">
        <Input type="number" value={form.priceAmount} onChange={e => set('priceAmount', e.target.value)}
          placeholder="Tarif des Réglages" />
      </Field>
      <label className="col-menu-row" style={{ padding: '8px 0' }}>
        <Checkbox checked={form.autoRenew} onChange={() => set('autoRenew', !form.autoRenew)} />
        <span>Renouvellement automatique</span>
      </label>

      <div style={{ marginTop: 10, paddingTop: 12, borderTop: '1px solid var(--line)' }}>
        <label className="col-menu-row" style={{ padding: '8px 0' }}>
          <Checkbox checked={form.markPaid} onChange={() => set('markPaid', !form.markPaid)} />
          <span>Le loueur a réglé aujourd'hui</span>
        </label>
        <div className="muted" style={{ fontSize: 11.5, lineHeight: 1.5, marginBottom: form.markPaid ? 10 : 0 }}>
          La facture de la première période est émise dans tous les cas. Cochez pour
          enregistrer le règlement dans la foulée ; sinon elle apparaîtra en créance ouverte.
        </div>
        {form.markPaid && (
          <div className="grid-2" style={{ gap: 10 }}>
            <Field label="Moyen de paiement">
              <Select value={form.paymentMethod} onChange={e => set('paymentMethod', e.target.value)}>
                <option value="cash">Espèces</option>
                <option value="bank_transfer">Virement</option>
                <option value="cheque">Chèque</option>
                <option value="card">Carte</option>
              </Select>
            </Field>
            <Field label="Référence (facultatif)">
              <Input value={form.paymentReference} onChange={e => set('paymentReference', e.target.value)}
                placeholder="N° de reçu" />
            </Field>
          </div>
        )}
      </div>
    </Modal>
  );
}

Object.assign(window, { AdminBilling });
