/* ============================================================
   AGENDA — Financeiro, Tarefas, Histórico
   ============================================================ */

/* ---------------- FINANCEIRO ---------------- */
function FinanceScreen(ctx) {
  const { terms, transactions, services, clients, suppliers = [], businessType, addClient, addSupplier, addTransaction, updateTransaction, payTransaction, deleteTransaction, toast } = ctx;
  const DB = window.DB;
  const [range, setRange] = React.useState('mes');
  const [periodDate, setPeriodDate] = React.useState(() => new Date(DB.today));
  const [filter, setFilter] = React.useState('Todas');
  const [search, setSearch] = React.useState('');
  const [typeFilter, setTypeFilter] = React.useState('');
  const [statusFilter, setStatusFilter] = React.useState('');
  const [clientFilter, setClientFilter] = React.useState('');
  const [supplierFilter, setSupplierFilter] = React.useState('');
  const [adding, setAdding] = React.useState(false);
  const [editing, setEditing] = React.useState(null);
  const [viewing, setViewing] = React.useState(null);
  const [receiving, setReceiving] = React.useState(null);

  const cleanMethod = (method) => String(method || 'Pix').replace(/\s+\d+\/\d+$/, '').trim() || 'Pix';
  const txPaymentPlan = (t) => Array.isArray(t.paymentPlan)
    ? t.paymentPlan
        .map((p) => ({ method: cleanMethod(p.method), value: Math.abs(Number(p.value) || 0), installments: Math.max(1, Number(p.installments) || 1) }))
        .filter((p) => p.value > 0)
    : [];
  const paymentSummary = (t) => {
    if (t.status === 'pendente') return 'A definir';
    const plan = txPaymentPlan(t);
    if (plan.length > 0) {
      return plan.map((p) => p.method + ' ' + DB.money(p.value) + (p.installments > 1 ? ' (' + p.installments + 'x)' : '')).join(' + ');
    }
    return t.method || 'A definir';
  };
  const supplierName = (id) => {
    const s = suppliers.find((x) => String(x.id) === String(id));
    return s ? s.name : '';
  };
  const clientName = (id) => {
    const c = clients.find((x) => String(x.id) === String(id));
    return c ? c.name : '';
  };
  const norm = (v) => String(v || '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');

  const saveTx = async (data) => {
    try { await addTransaction(data); toast('Lançamento registrado!'); setAdding(false); }
    catch (e) { toast(e.message, 'x'); }
  };
  const saveEditTx = async (data) => {
    if (!editing || !updateTransaction) return;
    try {
      await updateTransaction(editing.id, data);
      toast('Lançamento atualizado!');
      setEditing(null);
      setViewing(null);
    } catch (e) {
      toast(e.message, 'x');
    }
  };
  const saveReceive = async (data) => {
    if (!receiving) return;
    await payTransaction(receiving.id, data);
    setReceiving(null);
  };

  const cloneDate = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
  const startOfWeek = (d) => {
    const out = cloneDate(d);
    const wd = (out.getDay() + 6) % 7;
    out.setDate(out.getDate() - wd);
    return out;
  };
  const addPeriod = (dir) => {
    setPeriodDate((prev) => {
      const d = cloneDate(prev);
      if (range === 'semana') d.setDate(d.getDate() + (dir * 7));
      else if (range === 'mes') return new Date(d.getFullYear(), d.getMonth() + dir, 1);
      else return new Date(d.getFullYear() + dir, d.getMonth(), 1);
      return d;
    });
  };
  const periodBounds = (() => {
    const base = cloneDate(periodDate);
    if (range === 'semana') {
      const start = startOfWeek(base);
      const end = new Date(start); end.setDate(start.getDate() + 6);
      return { start, end };
    }
    if (range === 'mes') {
      return { start: new Date(base.getFullYear(), base.getMonth(), 1), end: new Date(base.getFullYear(), base.getMonth() + 1, 0) };
    }
    return { start: new Date(base.getFullYear(), 0, 1), end: new Date(base.getFullYear(), 11, 31) };
  })();
  const periodTitle = (() => {
    if (range === 'semana') {
      const a = periodBounds.start.toLocaleDateString('pt-BR', { day: '2-digit', month: 'short' });
      const b = periodBounds.end.toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' });
      return a + ' a ' + b;
    }
    if (range === 'mes') {
      const label = periodBounds.start.toLocaleDateString('pt-BR', { month: 'long', year: 'numeric' });
      return label.charAt(0).toUpperCase() + label.slice(1);
    }
    return String(periodBounds.start.getFullYear());
  })();

  // recorte por período
  const inRange = (t) => {
    const d = new Date(t.date + 'T00:00');
    return d >= periodBounds.start && d <= periodBounds.end;
  };
  const txs = transactions.filter(inRange);

  const paidTxs = txs.filter(t => t.status === 'pago');
  const receitas = paidTxs.filter(t => t.value > 0).reduce((s, t) => s + t.value, 0);
  const despesas = paidTxs.filter(t => t.value < 0).reduce((s, t) => s + Math.abs(t.value), 0);
  const saldo = receitas - despesas;
  const pendentes = txs.filter(t => t.status === 'pendente' && t.value > 0);
  const pendente = pendentes.reduce((s, t) => s + t.value, 0);
  const aPagar = txs.filter(t => t.status === 'pendente' && t.value < 0).reduce((s, t) => s + Math.abs(t.value), 0);

  const searchText = norm(search);
  const filtered = txs.filter((t) => {
    if (filter !== 'Todas') {
      if (filter === 'Receitas' && t.value <= 0) return false;
      if (filter === 'Despesas' && t.value >= 0) return false;
      if (filter === 'Pendentes' && t.status !== 'pendente') return false;
    }
    if (typeFilter && t.cat !== typeFilter) return false;
    if (statusFilter && t.status !== statusFilter) return false;
    if (clientFilter && String(t.clientId || '') !== String(clientFilter)) return false;
    if (supplierFilter && String(t.supplierId || '') !== String(supplierFilter)) return false;
    if (searchText) {
      const hay = norm([
        t.desc,
        t.cat,
        t.status,
        t.tag,
        paymentSummary(t),
        clientName(t.clientId),
        supplierName(t.supplierId),
      ].filter(Boolean).join(' '));
      if (!hay.includes(searchText)) return false;
    }
    return true;
  });

  // receita recebida no periodo selecionado
  const paidIncomeOn = (start, end = start) => transactions
    .filter((t) => {
      const d = new Date(t.date + 'T00:00');
      return t.status === 'pago' && t.value > 0 && d >= start && d <= end;
    })
    .reduce((s, t) => s + t.value, 0);
  const chart = (() => {
    if (range === 'semana') {
      const days = ['Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab', 'Dom'];
      return days.map((lbl, i) => {
        const d = new Date(periodBounds.start); d.setDate(periodBounds.start.getDate() + i);
        return [lbl, paidIncomeOn(d)];
      });
    }
    if (range === 'mes') {
      const points = [];
      for (let start = new Date(periodBounds.start); start <= periodBounds.end; start.setDate(start.getDate() + 7)) {
        const end = new Date(start); end.setDate(start.getDate() + 6);
        if (end > periodBounds.end) end.setTime(periodBounds.end.getTime());
        points.push([String(start.getDate()).padStart(2, '0') + '-' + String(end.getDate()).padStart(2, '0'), paidIncomeOn(start, end)]);
      }
      return points;
    }
    return Array.from({ length: 12 }, (_, i) => {
      const start = new Date(periodBounds.start.getFullYear(), i, 1);
      const end = new Date(periodBounds.start.getFullYear(), i + 1, 0);
      const label = start.toLocaleDateString('pt-BR', { month: 'short' }).replace('.', '');
      return [label.charAt(0).toUpperCase() + label.slice(1), paidIncomeOn(start, end)];
    });
  })();
  const maxC = Math.max(1, ...chart.map(c => c[1]));
  const chartValueLabel = (v) => {
    if (v <= 0) return '';
    if (v < 1000) return DB.money0(v);
    if (v < 1000000) {
      const mil = v / 1000;
      return 'R$ ' + mil.toLocaleString('pt-BR', { maximumFractionDigits: mil >= 10 ? 0 : 1 }) + ' mil';
    }
    const mi = v / 1000000;
    return 'R$ ' + mi.toLocaleString('pt-BR', { maximumFractionDigits: mi >= 10 ? 0 : 1 }) + ' mi';
  };

  // formas de pagamento (% sobre receitas do período)
  const recTx = paidTxs.filter(t => t.value > 0);
  const totalRec = recTx.reduce((s, t) => s + t.value, 0) || 1;
  const methodColors = ['var(--ev-teal)', 'var(--ev-blue)', 'var(--ev-amber)', 'var(--ev-cyan)', 'var(--ev-rose)', 'var(--ev-green)'];
  const methodParts = recTx.map((t) => ({ method: cleanMethod(t.method), value: Math.abs(Number(t.value) || 0) }));
  const methods = [...new Set(methodParts.map(p => p.method))]
    .map(m => [m, Math.round(methodParts.filter(p => p.method === m).reduce((s, p) => s + p.value, 0) / totalRec * 100)])
    .sort((a, b) => b[1] - a[1]).slice(0, 6);

  const rangeLabel = periodTitle;
  const chartTitle = range === 'semana' ? 'Receita por dia' : range === 'mes' ? 'Receita por semana' : 'Receita por mês';

  const cards = [
    { lbl: 'Receitas', val: DB.money(receitas), color: 'var(--success)', bg: 'var(--success-soft)', ic: 'trendUp', delta: recTx.length + ' recebidos' },
    { lbl: 'Despesas', val: DB.money(despesas), color: 'var(--danger)', bg: 'var(--danger-soft)', ic: 'trendDown', delta: paidTxs.filter(t => t.value < 0).length + ' pagos' },
    { lbl: 'Saldo', val: DB.money(saldo), color: 'var(--primary)', bg: 'var(--primary-soft)', ic: 'wallet', delta: rangeLabel },
    { lbl: 'A receber', val: DB.money(pendente), color: 'var(--warning)', bg: 'var(--warning-soft)', ic: 'clock', delta: aPagar > 0 ? DB.money(aPagar) + ' a pagar' : pendentes.length + ' pendente' + (pendentes.length === 1 ? '' : 's') },
  ];

  const exportar = () => {
    window.exportCSV('financeiro-' + range + '.csv', [
      ['Data', 'Descrição', 'Categoria', terms.client || 'Cliente', 'Fornecedor', 'Forma', 'Status', 'Valor'],
      ...filtered.map(t => [t.date, t.desc, t.cat, clientName(t.clientId), supplierName(t.supplierId), paymentSummary(t), t.status, String(t.value).replace('.', ',')]),
    ]);
    toast('Relatério exportado!', 'download');
  };

  return (
    <div className="content-inner fadein">
      <window.Header title="Financeiro" sub="Acompanhe receitas, despesas e pagamentos" action={
        <div className="row wrap" style={{ gap: 10 }}>
          <window.Seg value={range} onChange={setRange} options={[{ value: 'semana', label: 'Semana' }, { value: 'mes', label: 'Mês' }, { value: 'ano', label: 'Ano' }]} />
          <div className="row" style={{ gap: 6 }}>
            <button type="button" className="iconbtn bordered" title="Periodo anterior" onClick={() => addPeriod(-1)}>
              <window.Icon name="chevLeft" size={16} />
            </button>
            <span className="badge plain" style={{ minWidth: 150, justifyContent: 'center', height: 36 }}>{periodTitle}</span>
            <button type="button" className="iconbtn bordered" title="Proximo periodo" onClick={() => addPeriod(1)}>
              <window.Icon name="chevRight" size={16} />
            </button>
            <button type="button" className="btn outline sm" onClick={() => setPeriodDate(new Date(DB.today))}>Hoje</button>
          </div>
          <window.Button variant="outline" icon="download" onClick={exportar}>Exportar</window.Button>
          <window.Button variant="primary" icon="plus" onClick={() => setAdding(true)}>Novo lançamento</window.Button>
        </div>
      } />

      <div className="kpi-grid" style={{ marginBottom: 16 }}>
        {cards.map((c, i) => (
          <div className="card kpi" key={i}>
            <div className="top"><div className="ic" style={{ background: c.bg, color: c.color }}><window.Icon name={c.ic} size={19} /></div><div className="lbl">{c.lbl}</div></div>
            <div className="val mono" style={{ fontSize: 24 }}>{c.val}</div>
            <div className="delta up" style={{ color: 'var(--text-3)' }}>{c.delta}</div>
          </div>
        ))}
      </div>

      <div className="grid-2" style={{ marginBottom: 16 }}>
        <div className="card card-pad">
          <div className="row between" style={{ marginBottom: 18 }}>
            <div><div style={{ fontWeight: 600, fontSize: 14.5 }}>{chartTitle}</div><div className="muted" style={{ fontSize: 12 }}>{periodTitle}</div></div>
            <window.Badge cls="green" dot>{DB.money(chart.reduce((s, c) => s + c[1], 0))}</window.Badge>
          </div>
          <div style={{ display: 'flex', alignItems: 'flex-end', gap: 14, height: 180 }}>
            {chart.map(([d, v]) => (
              <div key={d} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 7, height: '100%', justifyContent: 'flex-end' }}>
                <div className="mono" style={{ fontSize: 10.5, color: 'var(--text-3)', fontWeight: 600, whiteSpace: 'nowrap' }}>{chartValueLabel(v)}</div>
                <div style={{ width: '100%', maxWidth: 38, height: Math.max(4, v / maxC * 140), background: v === maxC && v > 0 ? 'var(--primary)' : 'var(--primary-soft-2)', borderRadius: '6px 6px 0 0', transition: 'height .4s' }} />
                <div className="muted" style={{ fontSize: 11 }}>{d}</div>
              </div>
            ))}
          </div>
        </div>

        <div className="card card-pad">
          <div style={{ fontWeight: 600, fontSize: 14.5, marginBottom: 4 }}>Formas de pagamento</div>
          <div className="muted" style={{ fontSize: 12, marginBottom: 18 }}>{rangeLabel}</div>
          {methods.length === 0 && <div className="muted" style={{ fontSize: 12.5 }}>Sem receitas no período.</div>}
          {methods.map(([m, pct], i) => (
            <div key={m} style={{ marginBottom: 14 }}>
              <div className="row between" style={{ marginBottom: 6 }}><span style={{ fontSize: 12.5, fontWeight: 500 }}>{m}</span><span className="mono muted" style={{ fontSize: 12 }}>{pct}%</span></div>
              <div className="bar"><i style={{ width: pct + '%', background: methodColors[i % methodColors.length] }} /></div>
            </div>
          ))}
        </div>
      </div>

      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="row between wrap" style={{ padding: 14, gap: 12, borderBottom: '1px solid var(--border)' }}>
          <div className="row" style={{ gap: 8 }}><h3 style={{ fontSize: 14.5 }}>Transações</h3><span className="badge plain">{filtered.length}</span></div>
          <div className="row wrap" style={{ gap: 7 }}>
            {['Todas', 'Receitas', 'Despesas', 'Pendentes'].map(f => <button key={f} className={'chip' + (filter === f ? ' active' : '')} onClick={() => setFilter(f)}>{f}</button>)}
          </div>
        </div>
        <div className="row wrap finance-filters" style={{ padding: 14, gap: 10, borderBottom: '1px solid var(--border)' }}>
          <div className="input-icon finance-search">
            <window.Icon name="search" size={16} />
            <input className="input" placeholder={`Pesquisar por descrição, ${terms.client || 'cliente'}, fornecedor, tipo...`} value={search} onChange={(e) => setSearch(e.target.value)} />
          </div>
          <window.Select value={typeFilter} onChange={(e) => setTypeFilter(e.target.value)} style={{ flex: '0 0 150px' }}>
            <option value="">Todos os tipos</option>
            <option value="Receita">Receitas</option>
            <option value="Despesa">Despesas</option>
          </window.Select>
          <window.Select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)} style={{ flex: '0 0 150px' }}>
            <option value="">Todos os status</option>
            <option value="pendente">Pendentes</option>
            <option value="pago">Pagos</option>
          </window.Select>
          <window.Select value={clientFilter} onChange={(e) => setClientFilter(e.target.value)} style={{ flex: '0 0 190px' }}>
            <option value="">Todos os {String(terms.clients || 'clientes').toLowerCase()}</option>
            {clients.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
          </window.Select>
          <window.Select value={supplierFilter} onChange={(e) => setSupplierFilter(e.target.value)} style={{ flex: '0 0 190px' }}>
            <option value="">Todos os fornecedores</option>
            {suppliers.map((s) => <option key={s.id} value={s.id}>{s.name}</option>)}
          </window.Select>
          <span className="finance-clear" style={{ visibility: (search || typeFilter || statusFilter || clientFilter || supplierFilter || filter !== 'Todas') ? 'visible' : 'hidden' }}>
            <window.Button variant="ghost" icon="x" onClick={() => { setSearch(''); setTypeFilter(''); setStatusFilter(''); setClientFilter(''); setSupplierFilter(''); setFilter('Todas'); }}>Limpar</window.Button>
          </span>
        </div>
        {filtered.length === 0 ? <window.Empty icon="wallet" title="Nenhuma transação" desc="Nada registrado neste período." /> : (
          <div style={{ overflowX: 'auto', minHeight: 280 }}>
            <table className="tbl">
              <thead><tr><th>Descrição</th><th>{terms.client || 'Cliente'}</th><th>Fornecedor</th><th>Data</th><th>Forma</th><th>Status</th><th style={{ textAlign: 'right' }}>Valor</th><th></th></tr></thead>
              <tbody>
                {filtered.map(t => {
                  const cName = clientName(t.clientId);
                  const sName = supplierName(t.supplierId);
                  return (
                  <tr key={t.id} style={{ cursor: 'pointer' }} onClick={() => setViewing(t)}>
                    <td>
                      <div className="row" style={{ gap: 10 }}>
                        <div className="ic" style={{ width: 32, height: 32, borderRadius: 8, background: t.value > 0 ? 'var(--success-soft)' : 'var(--danger-soft)', color: t.value > 0 ? 'var(--success)' : 'var(--danger)', display: 'grid', placeItems: 'center', flex: 'none' }}>
                          <window.Icon name={t.value > 0 ? 'trendUp' : 'trendDown'} size={15} />
                        </div>
                        <div style={{ minWidth: 0 }}>
                          <div style={{ fontWeight: 500 }}>{t.desc}</div>
                        </div>
                      </div>
                    </td>
                    <td>
                      {cName ? (
                        <span className="badge plain" style={{ height: 22 }}>
                          <window.Icon name="user" size={12} /> {cName}
                        </span>
                      ) : <span className="muted">-</span>}
                    </td>
                    <td>
                      {sName ? (
                        <span className="badge plain" style={{ height: 22 }}>
                          <window.Icon name="building" size={12} /> {sName}
                        </span>
                      ) : <span className="muted">-</span>}
                    </td>
                    <td className="muted">{new Date(t.date + 'T00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'short' })}</td>
                    <td><span className="badge plain" title={paymentSummary(t)}>{paymentSummary(t)}</span></td>
                    <td><window.Badge cls={t.status === 'pago' ? 'green' : 'amber'} dot>{t.status === 'pago' ? 'Pago' : 'Pendente'}</window.Badge></td>
                    <td className="mono" style={{ textAlign: 'right', fontWeight: 700, color: t.value > 0 ? 'var(--success)' : 'var(--text)' }}>{DB.money(t.value)}</td>
                    <td style={{ textAlign: 'right' }}>
                      <div className="row" style={{ gap: 6, justifyContent: 'flex-end' }}>
                        {t.status === 'pendente' && <button className="btn outline sm" onClick={(e) => { e.stopPropagation(); setReceiving(t); }}>{t.value > 0 ? 'Receber' : 'Pagar'}</button>}
                        {false && t.status === 'pendente' && (
                          <window.Menu width={170} trigger={<button className="btn outline sm" onClick={(e) => e.stopPropagation()}>{t.value > 0 ? 'Receber' : 'Pagar'}</button>}>
                            <div className="mlabel">Forma de pagamento</div>
                            {['Pix', 'Cartão', 'Dinheiro', 'Transf.', 'Boleto'].map(m => (
                              <button key={m} onClick={(e) => { e.stopPropagation(); payTransaction(t.id, m); }}>{m}</button>
                            ))}
                          </window.Menu>
                        )}
                        <button className="iconbtn" style={{ width: 28, height: 28 }} title="Editar lançamento"
                          onClick={(e) => { e.stopPropagation(); setEditing(t); }}>
                          <window.Icon name="edit" size={14} />
                        </button>
                        <button className="iconbtn" style={{ width: 28, height: 28 }} title="Excluir lançamento"
                          onClick={(e) => { e.stopPropagation(); if (window.confirm('Excluir este lançamento? Esta ação não pode ser desfeita.')) deleteTransaction(t.id); }}>
                          <window.Icon name="trash" size={14} />
                        </button>
                      </div>
                    </td>
                  </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>

      {adding && <TxModal terms={terms} services={services} clients={clients} suppliers={suppliers} businessType={businessType} addClient={addClient} addSupplier={addSupplier} onClose={() => setAdding(false)} onSave={saveTx} />}
      {editing && <TxModal transaction={editing} terms={terms} services={services} clients={clients} suppliers={suppliers} businessType={businessType} addClient={addClient} addSupplier={addSupplier} onClose={() => setEditing(null)} onSave={saveEditTx} />}
      {viewing && <TransactionDetailsModal transaction={viewing} terms={terms} clients={clients} suppliers={suppliers} paymentSummary={paymentSummary} onClose={() => setViewing(null)} onEdit={() => { setEditing(viewing); setViewing(null); }} />}
      {receiving && <ReceiveTxModal transaction={receiving} clients={clients} suppliers={suppliers} terms={terms} onClose={() => setReceiving(null)} onSave={saveReceive} />}
    </div>
  );
}

function TransactionDetailsModal({ transaction, terms = {}, clients = [], suppliers = [], paymentSummary, onClose, onEdit }) {
  const DB = window.DB;
  const clientLabel = terms.client || 'Cliente';
  const client = clients.find((c) => String(c.id) === String(transaction.clientId));
  const supplier = suppliers.find((s) => String(s.id) === String(transaction.supplierId));
  const isExpense = Number(transaction.value) < 0;
  const dueText = transaction.dueDate
    ? new Date(transaction.dueDate + 'T00:00').toLocaleDateString('pt-BR')
    : '-';
  const dateText = transaction.date
    ? new Date(transaction.date + 'T00:00').toLocaleDateString('pt-BR')
    : '-';
  const partyText = client ? client.name : (supplier ? supplier.name : '-');
  const Detail = ({ label, children }) => (
    <div>
      <div className="muted" style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', marginBottom: 4 }}>{label}</div>
      <div style={{ fontSize: 13.5, fontWeight: 500 }}>{children || '-'}</div>
    </div>
  );

  return (
    <window.Modal wide title="Detalhes do lançamento" sub={`${transaction.desc} - ${DB.money(transaction.value)}`} onClose={onClose}
      footer={<><div style={{ flex: 1 }} /><window.Button variant="ghost" onClick={onClose}>Fechar</window.Button><window.Button variant="primary" icon="edit" onClick={onEdit}>Editar</window.Button></>}>
      <div className="grid-2">
        <div className="card card-pad">
          <div className="row" style={{ gap: 10, marginBottom: 16 }}>
            <div className="ic" style={{ width: 38, height: 38, borderRadius: 10, background: isExpense ? 'var(--danger-soft)' : 'var(--success-soft)', color: isExpense ? 'var(--danger)' : 'var(--success)', display: 'grid', placeItems: 'center' }}>
              <window.Icon name={isExpense ? 'trendDown' : 'trendUp'} size={17} />
            </div>
            <div>
              <div style={{ fontWeight: 700 }}>{transaction.desc}</div>
              <div className="muted" style={{ fontSize: 12 }}>{isExpense ? 'Despesa' : 'Receita'} #{transaction.id}</div>
            </div>
          </div>
          <div className="grid-2" style={{ gap: 16 }}>
            <Detail label="Valor">{DB.money(transaction.value)}</Detail>
            <Detail label="Status"><window.Badge cls={transaction.status === 'pago' ? 'green' : 'amber'} dot>{transaction.status === 'pago' ? 'Pago' : 'Pendente'}</window.Badge></Detail>
            <Detail label="Data">{dateText}</Detail>
            <Detail label="Vencimento">{dueText}</Detail>
            <Detail label="Categoria">{transaction.tag || '-'}</Detail>
            <Detail label="Forma">{paymentSummary(transaction)}</Detail>
          </div>
        </div>
        <div className="card card-pad">
          <div style={{ fontWeight: 700, marginBottom: 16 }}>Vínculos</div>
          <div className="stack" style={{ gap: 16 }}>
            <Detail label={client ? clientLabel : supplier ? 'Fornecedor' : `${clientLabel}/Fornecedor`}>
              <span className="row" style={{ gap: 8 }}>
                {(client || supplier) && <window.Icon name={client ? 'user' : 'building'} size={15} />}
                <span>{partyText}</span>
              </span>
            </Detail>
            {client && <Detail label="Contato">{client.phone || client.email || '-'}</Detail>}
            {supplier && <Detail label="Contato">{supplier.phone || supplier.email || supplier.document || '-'}</Detail>}
            <Detail label="Tipo">{transaction.cat || (isExpense ? 'Despesa' : 'Receita')}</Detail>
          </div>
        </div>
      </div>
    </window.Modal>
  );
}

/* ---------- NOVO LANÇAMENTO (entrada/saída avulsa) ---------- */
function TxModal({ transaction = null, terms, services = [], clients = [], suppliers = [], businessType, addClient, addSupplier, onClose, onSave }) {
  const DB = window.DB;
  const apptPlural = ((terms && terms.appt) || 'atendimento').toLowerCase() + 's';
  const isEdit = !!transaction;
  const cleanMethod = (method) => String(method || 'Pix').replace(/\s+\d+\/\d+$/, '').trim() || 'Pix';
  const txPayments = isEdit && Array.isArray(transaction.paymentPlan)
    ? transaction.paymentPlan
        .map((p) => ({ method: cleanMethod(p.method), value: Math.abs(Number(p.value) || 0), installments: Math.max(1, Number(p.installments) || 1) }))
        .filter((p) => p.value > 0)
    : [];
  const initialForm = isEdit
    ? {
        cat: Number(transaction.value) < 0 ? 'Despesa' : 'Receita',
        desc: transaction.desc || '',
        value: Math.abs(Number(transaction.value) || 0),
        date: transaction.date || DB.iso(DB.today),
        dueDate: transaction.dueDate || '',
        tag: transaction.tag || '',
        status: transaction.status || 'pendente',
        weekendPolicy: 'keep',
        clientId: transaction.clientId ? String(transaction.clientId) : '',
        supplierId: transaction.supplierId ? String(transaction.supplierId) : '',
        serviceId: '',
      }
    : { cat: 'Receita', desc: '', value: '', date: DB.iso(DB.today), dueDate: '', tag: '', status: 'pendente', weekendPolicy: 'keep', clientId: '', supplierId: '', serviceId: '' };
  const [f, setF] = React.useState(initialForm);
  const [localClients, setLocalClients] = React.useState(clients || []);
  const [localSuppliers, setLocalSuppliers] = React.useState(suppliers || []);
  const [clientQ, setClientQ] = React.useState('');
  const [clientOpen, setClientOpen] = React.useState(false);
  const [newClientOpen, setNewClientOpen] = React.useState(false);
  const [newClient, setNewClient] = React.useState({ name: '', phone: '', email: '' });
  const [clientSaving, setClientSaving] = React.useState(false);
  const [clientErr, setClientErr] = React.useState('');
  const [supplierQ, setSupplierQ] = React.useState('');
  const [supplierOpen, setSupplierOpen] = React.useState(false);
  const [newSupplierOpen, setNewSupplierOpen] = React.useState(false);
  const [newSupplier, setNewSupplier] = React.useState({ name: '', phone: '', email: '', document: '' });
  const [supplierSaving, setSupplierSaving] = React.useState(false);
  const [supplierErr, setSupplierErr] = React.useState('');
  const [fixo, setFixo] = React.useState(false);
  const [payments, setPayments] = React.useState(txPayments.length > 0 ? txPayments : [{ method: cleanMethod(transaction?.method), value: isEdit ? Math.abs(Number(transaction.value) || 0) : '', installments: 1 }]);
  const [saving, setSaving] = React.useState(false);
  const clientComboRef = React.useRef(null);
  const supplierComboRef = React.useRef(null);
  
  const set = (k) => (e) => setF(prev => ({ ...prev, [k]: e.target.value }));
  const pricedServices = (services || []).filter((s) => Number(s.price) > 0);
  
  const selectClientLegacy = (e) => {
    const cid = e.target.value;
    const client = clients.find((c) => String(c.id) === String(cid));
    if (!client) {
      setF(prev => ({ ...prev, clientId: '' }));
      return;
    }
    const planPrice = client.planPrice > 0 ? client.planPrice : (client.planId ? (pricedServices.find(s => s.id === client.planId)?.price || 0) : 0);
    const planName = client.planId ? pricedServices.find(s => s.id === client.planId)?.name : '';
    setF(prev => {
      const v = planPrice > 0 ? Number(planPrice) : prev.value;
      setPayments([{ method: 'Pix', value: v, installments: 1 }]);
      return { ...prev, clientId: cid, cat: 'Receita', desc: planName ? `Mensalidade - ${planName}` : `Cobrança - ${client.name}`, value: v, status: 'pendente' };
    });
  };

  React.useEffect(() => { setLocalClients(clients || []); }, [clients]);
  React.useEffect(() => { setLocalSuppliers(suppliers || []); }, [suppliers]);
  React.useEffect(() => {
    if (!clientOpen) return;
    const close = (e) => {
      if (clientComboRef.current && !clientComboRef.current.contains(e.target)) setClientOpen(false);
    };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [clientOpen]);
  React.useEffect(() => {
    if (!supplierOpen) return;
    const close = (e) => {
      if (supplierComboRef.current && !supplierComboRef.current.contains(e.target)) setSupplierOpen(false);
    };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [supplierOpen]);

  const clientLabel = terms.client || 'Cliente';
  const selectedClient = localClients.find((c) => String(c.id) === String(f.clientId));
  const selectedSupplier = localSuppliers.find((s) => String(s.id) === String(f.supplierId));
  const norm = (v) => String(v || '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  const clientFilter = norm(clientQ);
  const clientDigits = DB.mask.digits(clientQ);
  const filteredClients = (localClients || []).filter((c) => {
    if (!clientFilter && !clientDigits) return true;
    return norm(c.name).includes(clientFilter) ||
      norm(c.email).includes(clientFilter) ||
      norm(c.phone).includes(clientFilter) ||
      (clientDigits && DB.mask.digits(c.phone || '').includes(clientDigits)) ||
      (clientDigits && DB.mask.digits(c.cpf || '').includes(clientDigits));
  }).slice(0, 25);
  const supplierFilter = norm(supplierQ);
  const supplierDigits = DB.mask.digits(supplierQ);
  const filteredSuppliers = (localSuppliers || []).filter((s) => {
    if (!supplierFilter && !supplierDigits) return true;
    return norm(s.name).includes(supplierFilter) ||
      norm(s.email).includes(supplierFilter) ||
      norm(s.phone).includes(supplierFilter) ||
      norm(s.document).includes(supplierFilter) ||
      (supplierDigits && DB.mask.digits(s.phone || '').includes(supplierDigits)) ||
      (supplierDigits && DB.mask.digits(s.document || '').includes(supplierDigits));
  }).slice(0, 25);

  const applyClient = (client) => {
    if (!client) {
      setF(prev => ({ ...prev, clientId: '' }));
      return;
    }
    const planPrice = client.planPrice > 0 ? client.planPrice : (client.planId ? (pricedServices.find(s => s.id === client.planId)?.price || 0) : 0);
    const planName = client.planId ? pricedServices.find(s => s.id === client.planId)?.name : '';
    setF(prev => {
      if (isEdit) return { ...prev, clientId: String(client.id), cat: 'Receita' };
      const v = planPrice > 0 ? Number(planPrice) : prev.value;
      setPayments([{ method: 'Pix', value: v, installments: 1 }]);
      return { ...prev, clientId: String(client.id), cat: 'Receita', desc: planName ? `Mensalidade - ${planName}` : `Cobranca - ${client.name}`, value: v, status: 'pendente' };
    });
  };

  const selectClient = (client) => {
    applyClient(client);
    setClientQ('');
    setClientOpen(false);
    setNewClientOpen(false);
    setClientErr('');
  };
  const clearClient = () => {
    setF(prev => ({ ...prev, clientId: '' }));
    setClientQ('');
    setClientOpen(false);
  };
  const setNewClientField = (k) => (e) => setNewClient(prev => ({ ...prev, [k]: e.target.value }));
  const createInlineClient = async () => {
    const name = (newClient.name || clientQ || '').trim();
    if (!name || clientSaving || !addClient) return;
    setClientSaving(true);
    setClientErr('');
    try {
      const created = await addClient({ ...newClient, name });
      setLocalClients(prev => prev.some(c => c.id === created.id) ? prev : [created, ...prev]);
      selectClient(created);
      setNewClient({ name: '', phone: '', email: '' });
    } catch (e) {
      setClientErr(e.message || 'Nao foi possivel cadastrar.');
    } finally {
      setClientSaving(false);
    }
  };

  const selectSupplier = (supplier) => {
    if (!supplier) return;
    setF(prev => ({ ...prev, supplierId: String(supplier.id), cat: 'Despesa' }));
    setSupplierQ('');
    setSupplierOpen(false);
    setNewSupplierOpen(false);
    setSupplierErr('');
  };
  const clearSupplier = () => {
    setF(prev => ({ ...prev, supplierId: '' }));
    setSupplierQ('');
    setSupplierOpen(false);
  };
  const setNewSupplierField = (k) => (e) => setNewSupplier(prev => ({ ...prev, [k]: e.target.value }));
  const createInlineSupplier = async () => {
    const name = (newSupplier.name || supplierQ || '').trim();
    if (!name || supplierSaving || !addSupplier) return;
    setSupplierSaving(true);
    setSupplierErr('');
    try {
      const created = await addSupplier({ ...newSupplier, name });
      setLocalSuppliers(prev => prev.some(s => s.id === created.id) ? prev : [created, ...prev]);
      selectSupplier(created);
      setNewSupplier({ name: '', phone: '', email: '', document: '' });
    } catch (e) {
      setSupplierErr(e.message || 'Nao foi possivel cadastrar.');
    } finally {
      setSupplierSaving(false);
    }
  };

  const selectService = (e) => {
    const val = e.target.value;
    const service = pricedServices.find((s) => String(s.id) === String(val));
    if (!service) { setF(prev => ({ ...prev, serviceId: '' })); return; }
    const v = Number(service.price) || '';
    setPayments([{ method: 'Pix', value: v, installments: 1 }]);
    setF(prev => ({ ...prev, serviceId: String(service.id), cat: 'Receita', desc: service.name, value: v, status: 'pendente' }));
  };
  
  const total = Math.abs(Number(f.value) || 0);
  const sum = payments.reduce((acc, p) => acc + (Number(p.value) || 0), 0);
  const remaining = Math.round((total - sum) * 100) / 100;
  
  const updatePayment = (idx, key, value) => {
    setPayments((items) => items.map((item, i) => i === idx ? { ...item, [key]: value } : item));
  };
  const addPayment = () => {
    setPayments((items) => [...items, { method: 'Pix', value: Math.max(0, remaining), installments: 1 }]);
  };
  const removePayment = (idx) => {
    setPayments((items) => items.length <= 1 ? items : items.filter((_, i) => i !== idx));
  };
  const changeStatus = (e) => {
    const status = e.target.value;
    setF(prev => ({ ...prev, status }));
    if (status === 'pago') {
      setPayments((items) => {
        if (items.length > 0 && cleanMethod(items[0].method) !== 'A definir') return items;
        return [{ method: 'Pix', value: total || Math.abs(Number(f.value) || 0), installments: 1 }];
      });
    }
  };

  const semValor = f.cat === 'Despesa' && total === 0;
  const showPayments = f.status !== 'pendente';
  const valid = f.desc.trim() !== '' && 
    (semValor || (total > 0 &&
      (!showPayments || (
        payments.every((p) => Number(p.value) > 0 && Number(p.installments) >= 1) &&
        Math.abs(remaining) <= 0.01
      ))));

  const save = async () => {
    if (!valid || saving) return;
    setSaving(true);
    let payload = { ...f, value: total };
    if (!isEdit && fixo) {
      payload.recurrence = '';
      if (f.dueDate) payload.date = f.dueDate;
    } else {
      payload.recurrence = 1;
    }
    payload.clientId = f.cat === 'Receita' ? f.clientId : '';
    payload.supplierId = f.cat === 'Despesa' ? f.supplierId : '';
    payload.method = showPayments ? (payments[0]?.method || 'Pix') : 'A definir';
    payload.payments = showPayments ? payments.map((p) => ({ method: p.method, value: Number(p.value) || 0, installments: Math.max(1, Number(p.installments) || 1) })) : [];
    if (isEdit) payload.splitPayments = false;
    try { await onSave(payload); } finally { setSaving(false); }
  };

  return (
    <window.Modal title={isEdit ? 'Editar lançamento' : 'Novo lançamento'} sub={isEdit ? `#${transaction.id} - ${new Date((f.date || DB.iso(DB.today)) + 'T00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' })}` : new Date().toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' })} onClose={onClose}
      footer={<><div style={{ flex: 1 }} /><window.Button variant="ghost" onClick={onClose}>Cancelar</window.Button>
        <window.Button variant="primary" icon="check" disabled={saving || !valid} onClick={save}>{saving ? 'Salvando...' : 'Salvar'}</window.Button></>}>
      <div className="form-grid">
        <window.Field label="Tipo" full>
          <window.Seg value={f.cat} onChange={(v) => setF({ ...f, cat: v })}
            options={[{ value: 'Receita', label: 'Entrada (receita)' }, { value: 'Despesa', label: 'Saída (despesa)' }]} />
        </window.Field>
        
        {f.cat === 'Receita' && (
          <window.Field label={clientLabel} full optional>
            <div className="client-combo" ref={clientComboRef}>
              <div className="client-combo-input">
                <div className="input-icon">
                  <window.Icon name="search" size={16} />
                  <input className="input"
                    placeholder={selectedClient ? selectedClient.name : `Buscar ou cadastrar ${clientLabel.toLowerCase()}`}
                    value={clientOpen ? clientQ : ''}
                    onFocus={() => { setClientOpen(true); setClientQ(''); }}
                    onChange={(e) => { setClientQ(e.target.value); setClientOpen(true); setNewClient(prev => ({ ...prev, name: e.target.value })); }} />
                </div>
                {selectedClient && (
                  <button type="button" className="iconbtn bordered" title="Limpar selecao" onClick={clearClient}>
                    <window.Icon name="x" size={15} />
                  </button>
                )}
              </div>
              {selectedClient && !clientOpen && (
                <div className="client-combo-selected">
                  <window.Icon name="user" size={14} />
                  <span>{selectedClient.name}</span>
                  {(selectedClient.phone || selectedClient.email) && <small>{selectedClient.phone || selectedClient.email}</small>}
                </div>
              )}
              {clientOpen && (
                <div className="client-combo-panel">
                  <div className="client-combo-list">
                    {filteredClients.length === 0 && <div className="client-combo-empty">Nenhum cadastro encontrado.</div>}
                    {filteredClients.map((c) => (
                      <button type="button" key={c.id} className="client-combo-option" onClick={() => selectClient(c)}>
                        <span>{c.name}</span>
                        <small>{[c.phone, c.email].filter(Boolean).join(' - ') || 'Sem contato'}</small>
                      </button>
                    ))}
                  </div>
                  <div className="client-combo-create">
                    {!newClientOpen ? (
                      <button type="button" className="btn outline block" onClick={() => { setNewClientOpen(true); setNewClient(prev => ({ ...prev, name: prev.name || clientQ })); }}>
                        <window.Icon name="plus" size={16} /> Cadastrar {clientLabel.toLowerCase()}
                      </button>
                    ) : (
                      <div className="client-combo-form">
                        <window.Input placeholder="Nome completo" value={newClient.name} onChange={setNewClientField('name')} />
                        <window.MaskInput mask="phone" placeholder="Telefone" value={newClient.phone} onChange={setNewClientField('phone')} />
                        <window.Input placeholder="E-mail" value={newClient.email} onChange={setNewClientField('email')} />
                        {clientErr && <div className="field-hint err"><window.Icon name="info" size={12} /> {clientErr}</div>}
                        <div className="row" style={{ justifyContent: 'flex-end', gap: 8 }}>
                          <window.Button variant="ghost" size="sm" onClick={() => { setNewClientOpen(false); setClientErr(''); }}>Cancelar</window.Button>
                          <window.Button variant="primary" size="sm" icon="check" disabled={clientSaving || !newClient.name.trim()} onClick={createInlineClient}>{clientSaving ? 'Salvando...' : 'Cadastrar'}</window.Button>
                        </div>
                      </div>
                    )}
                  </div>
                </div>
              )}
            </div>
          </window.Field>
        )}

        {f.cat === 'Despesa' && (
          <window.Field label="Fornecedor" full optional>
            <div className="client-combo" ref={supplierComboRef}>
              <div className="client-combo-input">
                <div className="input-icon">
                  <window.Icon name="search" size={16} />
                  <input className="input"
                    placeholder={selectedSupplier ? selectedSupplier.name : 'Buscar ou cadastrar fornecedor'}
                    value={supplierOpen ? supplierQ : ''}
                    onFocus={() => { setSupplierOpen(true); setSupplierQ(''); }}
                    onChange={(e) => { setSupplierQ(e.target.value); setSupplierOpen(true); setNewSupplier(prev => ({ ...prev, name: e.target.value })); }} />
                </div>
                {selectedSupplier && (
                  <button type="button" className="iconbtn bordered" title="Limpar fornecedor" onClick={clearSupplier}>
                    <window.Icon name="x" size={15} />
                  </button>
                )}
              </div>
              {selectedSupplier && !supplierOpen && (
                <div className="client-combo-selected">
                  <window.Icon name="building" size={14} />
                  <span>{selectedSupplier.name}</span>
                  {(selectedSupplier.phone || selectedSupplier.email) && <small>{selectedSupplier.phone || selectedSupplier.email}</small>}
                </div>
              )}
              {supplierOpen && (
                <div className="client-combo-panel">
                  <div className="client-combo-list">
                    {filteredSuppliers.length === 0 && <div className="client-combo-empty">Nenhum fornecedor encontrado.</div>}
                    {filteredSuppliers.map((s) => (
                      <button type="button" key={s.id} className="client-combo-option" onClick={() => selectSupplier(s)}>
                        <span>{s.name}</span>
                        <small>{[s.phone, s.email, s.document].filter(Boolean).join(' - ') || 'Sem contato'}</small>
                      </button>
                    ))}
                  </div>
                  <div className="client-combo-create">
                    {!newSupplierOpen ? (
                      <button type="button" className="btn outline block" onClick={() => { setNewSupplierOpen(true); setNewSupplier(prev => ({ ...prev, name: prev.name || supplierQ })); }}>
                        <window.Icon name="plus" size={16} /> Cadastrar fornecedor
                      </button>
                    ) : (
                      <div className="client-combo-form">
                        <window.Input placeholder="Nome do fornecedor" value={newSupplier.name} onChange={setNewSupplierField('name')} />
                        <window.MaskInput mask="phone" placeholder="Telefone" value={newSupplier.phone} onChange={setNewSupplierField('phone')} />
                        <window.Input placeholder="E-mail" value={newSupplier.email} onChange={setNewSupplierField('email')} />
                        <window.Input placeholder="CPF/CNPJ" value={newSupplier.document} onChange={setNewSupplierField('document')} />
                        {supplierErr && <div className="field-hint err"><window.Icon name="info" size={12} /> {supplierErr}</div>}
                        <div className="row" style={{ justifyContent: 'flex-end', gap: 8 }}>
                          <window.Button variant="ghost" size="sm" onClick={() => { setNewSupplierOpen(false); setSupplierErr(''); }}>Cancelar</window.Button>
                          <window.Button variant="primary" size="sm" icon="check" disabled={supplierSaving || !newSupplier.name.trim()} onClick={createInlineSupplier}>{supplierSaving ? 'Salvando...' : 'Cadastrar'}</window.Button>
                        </div>
                      </div>
                    )}
                  </div>
                </div>
              )}
            </div>
          </window.Field>
        )}
        
        {f.cat === 'Receita' && businessType === 'pilates' && pricedServices.length > 0 && (
          <window.Field label="Plano / aula" full optional>
            <window.Select value={f.serviceId || ''} onChange={selectService}>
              <option value="">Sem plano/aula vinculado</option>
              {pricedServices.map((s) => <option key={s.id} value={s.id}>{s.name} · {DB.money(s.price)}</option>)}
            </window.Select>
          </window.Field>
        )}
        
        <window.Field label="Descrição" full>
          <window.Input placeholder={f.cat === 'Despesa' ? 'Ex.: Aluguel da sala, material...' : 'Ex.: Venda de produto, sinal...'} value={f.desc} onChange={set('desc')} autoFocus />
        </window.Field>
        
        <window.Field label="Valor">
          <window.MoneyInput value={f.value === '' ? '' : f.value} onChange={(n) => {
             setF({ ...f, value: n });
             if (payments.length === 1) updatePayment(0, 'value', n);
          }} />
        </window.Field>

        <window.Field label="Data">
          <window.Input type="date" value={f.date} onChange={set('date')} />
        </window.Field>
        
        <window.Field label="Vencimento" optional>
          <window.Input type="date" value={f.dueDate} onChange={set('dueDate')} />
        </window.Field>

        {f.cat === 'Despesa' && f.dueDate && !fixo && (
          <window.Field label="Se cair no sábado ou domingo" full>
            <window.Select value={f.weekendPolicy || 'keep'} onChange={set('weekendPolicy')}>
              <option value="keep">Manter a data mesmo no fim de semana</option>
              <option value="previous_business_day">Antecipar para o dia útil anterior</option>
              <option value="next_business_day">Adiar para o próximo dia útil</option>
            </window.Select>
          </window.Field>
        )}
        
        <window.Field label="Status">
          <window.Select value={f.status} onChange={changeStatus}>
            <option value="pago">{f.cat === 'Despesa' ? 'Pago' : 'Recebido'}</option>
            <option value="pendente">{f.cat === 'Despesa' ? 'A pagar' : 'A receber'}</option>
          </window.Select>
        </window.Field>
        
        <window.Field label="Categoria" optional>
          <window.Input placeholder={f.cat === 'Despesa' ? 'Ex: Aluguel, Luz, Impostos' : 'Ex: Mensalidade, Produto'} value={f.tag} onChange={set('tag')} />
        </window.Field>
      </div>
      
      {showPayments && <div className="stack" style={{ gap: 12, marginTop: 16 }}>
        <div style={{ fontWeight: 500, fontSize: 14 }}>Pagamento</div>
        {payments.map((p, idx) => (
          <div key={idx} className="card card-pad" style={{ padding: 12 }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr .7fr 38px', gap: 10, alignItems: 'end' }}>
              <window.Field label="Forma">
                <window.Select value={p.method} onChange={(e) => updatePayment(idx, 'method', e.target.value)}>
                  <option>Pix</option><option>Cartão</option><option>Dinheiro</option><option>Transf.</option><option>Boleto</option>
                </window.Select>
              </window.Field>
              <window.Field label="Valor">
                <window.MoneyInput value={p.value === '' ? '' : p.value} onChange={(n) => updatePayment(idx, 'value', n)} />
              </window.Field>
              <window.Field label="Parcelas">
                <window.Input type="number" min="1" max="60" value={p.installments} onChange={(e) => updatePayment(idx, 'installments', e.target.value)} />
              </window.Field>
              <div className="row" style={{ alignItems: 'end', justifyContent: 'flex-end', minHeight: 40 }}>
                <button type="button" className="iconbtn bordered" title="Remover" onClick={() => removePayment(idx)} disabled={payments.length <= 1}>
                  <window.Icon name="trash" size={15} />
                </button>
              </div>
            </div>
          </div>
        ))}
        <div className="row between wrap" style={{ gap: 10 }}>
          <window.Button variant="outline" icon="plus" onClick={addPayment}>Adicionar forma</window.Button>
          <div className="muted" style={{ fontSize: 12.5 }}>Soma: {DB.money(sum)}</div>
        </div>
      </div>}
      
      {!isEdit && <div style={{ marginTop: 16, padding: '12px 14px', borderRadius: 10, background: 'var(--surface-2, #f5f5f5)', display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer' }}
        onClick={() => setFixo(!fixo)}>
        <input type="checkbox" checked={fixo} onChange={() => setFixo(!fixo)} style={{ width: 18, height: 18, accentColor: 'var(--primary)' }} />
        <div>
          <div style={{ fontWeight: 500, fontSize: 14 }}>Fixo (repete todo mês)</div>
          <div className="muted" style={{ fontSize: 12 }}>{(() => {
            const dia = f.dueDate ? Number(f.dueDate.split('-')[2]) : Number(f.date.split('-')[2]);
            if (!dia) return 'Defina o vencimento para escolher o dia';
            if (dia > 28) return `Todo dia ${dia} (ou Último dia do mês quando não existir)`;
            return `Todo dia ${dia} de cada mês`;
          })()}</div>
        </div>
      </div>}
      {!isEdit && fixo && f.cat === 'Despesa' && (
        <div style={{ marginTop: 10, padding: '12px 14px', borderRadius: 10, background: 'var(--surface-2, #f5f5f5)' }}>
          <window.Field label="Se cair no sábado ou domingo">
            <window.Select value={f.weekendPolicy || 'keep'} onChange={set('weekendPolicy')}>
              <option value="keep">Manter a data mesmo no fim de semana</option>
              <option value="previous_business_day">Antecipar para o dia útil anterior</option>
              <option value="next_business_day">Adiar para o próximo dia útil</option>
            </window.Select>
          </window.Field>
        </div>
      )}
    </window.Modal>
  );
}

function ReceiveTxModal({ transaction, clients = [], suppliers = [], terms = {}, onClose, onSave }) {
  const DB = window.DB;
  const total = Math.abs(Number(transaction.value) || 0);
  const cleanMethod = (method) => String(method || 'Pix').replace(/\s+\d+\/\d+$/, '').trim() || 'Pix';
  const plannedPayments = Array.isArray(transaction.paymentPlan)
    ? transaction.paymentPlan
        .map((p) => ({ method: cleanMethod(p.method), value: Math.abs(Number(p.value) || 0), installments: Math.max(1, Number(p.installments) || 1) }))
        .filter((p) => p.value > 0)
    : [];
  const [payments, setPayments] = React.useState(plannedPayments.length > 0 ? plannedPayments : [{ method: cleanMethod(transaction.method), value: total, installments: 1 }]);
  const [saving, setSaving] = React.useState(false);
  const methods = [...new Set(['Pix', 'Cartão', 'Dinheiro', 'Transf.', 'Boleto', ...payments.map((p) => cleanMethod(p.method))])];
  const clientLabel = terms.client || 'Cliente';
  const client = clients.find((c) => String(c.id) === String(transaction.clientId));
  const clientText = client ? client.name : '';
  const supplier = suppliers.find((s) => String(s.id) === String(transaction.supplierId));
  const supplierText = supplier ? supplier.name : '';
  const partyText = transaction.value < 0 ? supplierText : clientText;
  const partyLabel = transaction.value < 0 ? 'Fornecedor' : clientLabel;
  const partyIcon = transaction.value < 0 ? 'building' : 'user';
  const partyDisplay = partyText || 'Não informado';
  const modalSub = `${partyLabel}: ${partyDisplay} - ${transaction.desc} - ${DB.money(total)}`;
  const sum = payments.reduce((acc, p) => acc + (Number(p.value) || 0), 0);
  const remaining = Math.round((total - sum) * 100) / 100;
  const update = (idx, key, value) => {
    setPayments((items) => items.map((item, i) => i === idx ? { ...item, [key]: value } : item));
  };
  const addPayment = () => {
    setPayments((items) => [...items, { method: 'Pix', value: Math.max(0, remaining), installments: 1 }]);
  };
  const removePayment = (idx) => {
    setPayments((items) => items.length <= 1 ? items : items.filter((_, i) => i !== idx));
  };
  const valid = total > 0 && Math.abs(remaining) <= 0.01 && payments.every((p) => Number(p.value) > 0 && Number(p.installments) >= 1);
  const save = async () => {
    if (!valid || saving) return;
    setSaving(true);
    try {
      await onSave({
        desc: transaction.desc,
        cat: transaction.value < 0 ? 'Despesa' : 'Receita',
        value: total,
        date: DB.iso(DB.today),
        payments: payments.map((p) => ({
          method: p.method,
          value: Number(p.value) || 0,
          installments: Math.max(1, Number(p.installments) || 1),
        })),
      });
    } finally {
      setSaving(false);
    }
  };
  return (
    <window.Modal title={transaction.value < 0 ? 'Confirmar pagamento' : 'Confirmar recebimento'} sub={modalSub} onClose={onClose}
      footer={<><div style={{ flex: 1 }} /><window.Button variant="ghost" onClick={onClose}>Cancelar</window.Button>
        <window.Button variant="primary" icon="check" disabled={saving || !valid} onClick={save}>{saving ? 'Salvando...' : 'Confirmar'}</window.Button></>}>
      <div className="stack" style={{ gap: 12 }}>
        <div className="row" style={{ gap: 8, fontSize: 13, fontWeight: 500 }}>
          <window.Icon name={partyIcon} size={15} />
          <span>{partyLabel}: {partyDisplay}</span>
        </div>
        {payments.map((p, idx) => (
          <div key={idx} className="card card-pad" style={{ padding: 12 }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1.25fr 1.25fr .7fr 38px', gap: 14, alignItems: 'end' }}>
              <window.Field label="Forma">
                <window.Select value={p.method} onChange={(e) => update(idx, 'method', e.target.value)}>
                  {methods.map((m) => <option key={m}>{m}</option>)}
                </window.Select>
              </window.Field>
              <window.Field label="Valor">
                <window.MoneyInput value={p.value === '' ? '' : p.value} onChange={(n) => update(idx, 'value', n)} />
              </window.Field>
              <window.Field label="Parcelas">
                <window.Input type="number" min="1" max="60" value={p.installments} onChange={(e) => update(idx, 'installments', e.target.value)} />
              </window.Field>
              <div className="row" style={{ alignItems: 'end', justifyContent: 'flex-end', minHeight: 40 }}>
                <button type="button" className="iconbtn bordered" title="Remover forma" onClick={() => removePayment(idx)} disabled={payments.length <= 1}>
                  <window.Icon name="trash" size={15} />
                </button>
              </div>
            </div>
          </div>
        ))}
        <div className="row between wrap" style={{ gap: 10 }}>
          <window.Button variant="outline" icon="plus" onClick={addPayment}>Adicionar forma</window.Button>
          <div className="muted" style={{ fontSize: 12.5 }}>Soma: {DB.money(sum)}</div>
        </div>
      </div>
    </window.Modal>
  );
}

/* ---------------- TAREFAS ---------------- */
function TasksScreen(ctx) {
  const { tasks, toast } = ctx;
  const [adding, setAdding] = React.useState(false);
  const [text, setText] = React.useState('');
  const cols = [
    { key: 'alta', label: 'Prioridade alta', cls: 'red' },
    { key: 'media', label: 'Prioridade média', cls: 'amber' },
    { key: 'baixa', label: 'Prioridade baixa', cls: 'blue' },
  ];
  const done = tasks.filter(t => t.done).length;

  const create = async () => {
    if (!text.trim()) return;
    await ctx.addTask(text.trim());
    setText(''); setAdding(false); toast('Tarefa criada!');
  };

  return (
    <div className="content-inner fadein">
      <window.Header title="Tarefas & lembretes" sub={`${tasks.length - done} pendentes · ${done} concluídas`} action={
        <window.Button variant="primary" icon="plus" onClick={() => setAdding(true)}>Nova tarefa</window.Button>
      } />
      {adding && (
        <div className="card card-pad fadein" style={{ marginBottom: 16, display: 'flex', gap: 10 }}>
          <input className="input" placeholder="Descreva a tarefa e pressione Enter..." value={text} autoFocus
            onChange={e => setText(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter') create(); }} />
          <window.Button variant="ghost" onClick={() => setAdding(false)}>Cancelar</window.Button>
        </div>
      )}
      <div className="grid-3">
        {cols.map(col => {
          const list = tasks.filter(t => t.prio === col.key);
          return (
            <div className="card" key={col.key}>
              <div className="card-head"><span className={'badge ' + col.cls}><i className="d" />{col.label}</span><span className="muted" style={{ fontSize: 12, marginLeft: 'auto' }}>{list.length}</span></div>
              <div className="card-pad" style={{ display: 'flex', flexDirection: 'column', gap: 9, minHeight: 80 }}>
                {list.length === 0 && <div className="muted" style={{ fontSize: 12.5, textAlign: 'center', padding: '14px 0' }}>Nada por aqui</div>}
                {list.map(t => (
                  <div key={t.id} className="card" style={{ padding: 12, display: 'flex', gap: 10, alignItems: 'flex-start', opacity: t.done ? .6 : 1 }}>
                    <window.Checkbox on={t.done} onClick={() => ctx.toggleTask(t.id, !t.done)} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 500, textDecoration: t.done ? 'line-through' : 'none' }}>{t.text}</div>
                      <div className="row" style={{ gap: 7, marginTop: 7 }}>
                        <span className="badge plain" style={{ height: 20 }}><window.Icon name="clock" size={12} /> {t.due}</span>
                        <span className="badge plain" style={{ height: 20 }}>{t.tag}</span>
                      </div>
                    </div>
                    <button className="iconbtn" style={{ width: 26, height: 26 }} title="Excluir" onClick={() => ctx.deleteTask(t.id)}>
                      <window.Icon name="trash" size={13} />
                    </button>
                  </div>
                ))}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ---------------- HISTÓRICO ---------------- */
function HistoryScreen(ctx) {
  const { terms, appts, clients, services, team, statusMap, toast } = ctx;
  const DB = window.DB;
  const apptPlural = terms.appt.toLowerCase() + 's';
  const initialClientId = ctx.historyClientId !== 'all' && clients.some((client) => Number(client.id) === Number(ctx.historyClientId))
    ? String(ctx.historyClientId)
    : 'all';
  const [clientId, setClientId] = React.useState(initialClientId);
  const [serviceId, setServiceId] = React.useState('all');
  const [proId, setProId] = React.useState('all');
  const [status, setStatus] = React.useState('all');
  const [dateFrom, setDateFrom] = React.useState('');
  const [dateTo, setDateTo] = React.useState('');
  React.useEffect(() => {
    const requested = ctx.historyClientId !== 'all' && clients.some((client) => Number(client.id) === Number(ctx.historyClientId))
      ? String(ctx.historyClientId)
      : 'all';
    setClientId(requested);
  }, [ctx.historyClientId]);
  const todayMin = DB.iso(DB.today);
  const past = appts.filter(a => a.date <= todayMin).sort((a, b) => (b.date + b.start).localeCompare(a.date + a.start));
  const cli = (id) => clients.find(c => c.id === id);
  const svc = (id) => services.find(s => s.id === id);
  const pro = (id) => team.find(p => p.id === id);
  const ini = (n) => String(n || '?').split(' ').map(x => x[0]).slice(0, 2).join('').toUpperCase();
  const statusOptions = ['confirmado', 'pendente', 'concluido', 'faltou', 'cancelado'];
  const shown = past.filter((a) => {
    if (clientId !== 'all' && Number(a.clientId) !== Number(clientId)) return false;
    if (serviceId === 'manual' && !a.manualServiceName) return false;
    if (serviceId !== 'all' && serviceId !== 'manual' && Number(a.serviceId) !== Number(serviceId)) return false;
    if (proId !== 'all' && Number(a.proId) !== Number(proId) && !(a.participants || []).some((part) => Number(part.proId) === Number(proId))) return false;
    if (status !== 'all' && a.status !== status) return false;
    if (dateFrom && a.date < dateFrom) return false;
    if (dateTo && a.date > dateTo) return false;
    return true;
  });
  const selectedClient = clientId === 'all' ? null : cli(Number(clientId));
  const hasManualServices = past.some((a) => !!a.manualServiceName);

  // agrupar por data
  const groups = {};
  shown.forEach(a => { (groups[a.date] = groups[a.date] || []).push(a); });
  const fmtDate = (d) => {
    const dt = new Date(d + 'T00:00');
    if (d === todayMin) return 'Hoje';
    if (d === DB.iso(DB.addDays(-1))) return 'Ontem';
    return dt.toLocaleDateString('pt-BR', { weekday: 'long', day: 'numeric', month: 'long' });
  };

  const exportar = () => {
    const suffix = selectedClient ? '-' + String(selectedClient.name || selectedClient.id).toLowerCase().replace(/[^a-z0-9]+/g, '-') : '';
    window.exportCSV('historico' + suffix + '.csv', [
      ['Data', 'Inicio', 'Fim', terms.client, terms.service, terms.pro, 'Valor', 'Status'],
      ...shown.map(a => {
        const c = cli(a.clientId), s = a.manualServiceName ? { name: a.manualServiceName, price: a.manualServicePrice || 0, color: 'violet' } : svc(a.serviceId), p = pro(a.proId);
        const st = statusMap[a.status] || { label: a.status };
        return [a.date, a.start, window.apptEnd(a), c ? c.name : (a.clientLabel || `Sem ${terms.client.toLowerCase()}`), s ? s.name : '', p ? p.name : '', s ? String(s.price).replace('.', ',') : '', st.label];
      }),
    ]);
    toast('Histórico exportado!', 'download');
  };
  const clearFilters = () => {
    setClientId('all');
    setServiceId('all');
    setProId('all');
    setStatus('all');
    setDateFrom('');
    setDateTo('');
  };

  return (
    <div className="content-inner fadein">
      <window.Header title={selectedClient ? `Histórico de ${selectedClient.name}` : `Histórico de ${apptPlural}`} sub={`${shown.length} de ${past.length} registros`} action={
        <window.Button variant="outline" icon="download" onClick={exportar}>Exportar</window.Button>
      } />
      <div className="card card-pad" style={{ marginBottom: 18 }}>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 12 }}>
          <window.Field label={terms.client}>
            <window.Select value={clientId} onChange={(e) => setClientId(e.target.value)}>
              <option value="all">Todos os {terms.clients.toLowerCase()}</option>
              {[...clients].sort((a, b) => String(a.name).localeCompare(String(b.name), 'pt-BR')).map((client) => <option key={client.id} value={client.id}>{client.name}</option>)}
            </window.Select>
          </window.Field>
          <window.Field label={terms.service}>
            <window.Select value={serviceId} onChange={(e) => setServiceId(e.target.value)}>
              <option value="all">Todos os {terms.services.toLowerCase()}</option>
              {services.map((service) => <option key={service.id} value={service.id}>{service.name}</option>)}
              {hasManualServices && <option value="manual">Informado manualmente</option>}
            </window.Select>
          </window.Field>
          <window.Field label={terms.pro}>
            <window.Select value={proId} onChange={(e) => setProId(e.target.value)}>
              <option value="all">Todos os {terms.pros.toLowerCase()}</option>
              {team.filter((member) => !member.systemIdentity).map((member) => <option key={member.id} value={member.id}>{member.name}</option>)}
            </window.Select>
          </window.Field>
          <window.Field label="Status">
            <window.Select value={status} onChange={(e) => setStatus(e.target.value)}>
              <option value="all">Todos os status</option>
              {statusOptions.map((value) => <option key={value} value={value}>{(statusMap[value] || { label: value }).label}</option>)}
            </window.Select>
          </window.Field>
          <window.Field label="Data inicial" optional><window.Input type="date" value={dateFrom} max={dateTo || todayMin} onChange={(e) => setDateFrom(e.target.value)} /></window.Field>
          <window.Field label="Data final" optional><window.Input type="date" value={dateTo} min={dateFrom || ''} max={todayMin} onChange={(e) => setDateTo(e.target.value)} /></window.Field>
        </div>
        <div className="row between wrap" style={{ marginTop: 12 }}>
          <span className="muted" style={{ fontSize: 12.5 }}>{shown.length} registro{shown.length === 1 ? '' : 's'} encontrado{shown.length === 1 ? '' : 's'}</span>
          <window.Button variant="ghost" icon="x" onClick={clearFilters}>Limpar filtros</window.Button>
        </div>
      </div>
      {Object.keys(groups).length === 0 && <window.Empty icon="history" title="Sem registros" desc={`Nenhum ${terms.appt.toLowerCase()} neste filtro.`} />}
      <div className="stack" style={{ gap: 22 }}>
        {Object.keys(groups).map(date => (
          <div key={date}>
            <div className="row" style={{ gap: 10, marginBottom: 11 }}>
              <h3 style={{ fontSize: 14, textTransform: 'capitalize' }}>{fmtDate(date)}</h3>
              <span className="badge plain">{groups[date].length}</span>
              <div style={{ flex: 1, height: 1, background: 'var(--border)' }} />
            </div>
            <div className="card" style={{ overflow: 'hidden' }}>
              {groups[date].map((a, i) => {
                const c = cli(a.clientId), s = a.manualServiceName ? { name: a.manualServiceName, price: a.manualServicePrice || 0, color: 'violet' } : svc(a.serviceId), p = pro(a.proId), st = statusMap[a.status] || { label: a.status, cls: 'plain' };
                if (!s || !p) return null;
                const displayClientName = c ? c.name : (a.clientLabel || p.name || `Sem ${terms.client.toLowerCase()}`);
                return (
                  <div className="lrow" key={a.id} style={{ padding: '12px 16px', borderBottom: i < groups[date].length - 1 ? '1px solid var(--border)' : 'none', cursor: 'pointer' }}
                    onClick={() => ctx.openAppt(a)}>
                    <div className="mono" style={{ fontSize: 12.5, color: 'var(--text-2)', width: 76 }}>{a.start}–{window.apptEnd(a)}</div>
                    <window.Avatar initials={ini(displayClientName)} color={p ? p.color : 'blue'} size={36} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontWeight: 600, fontSize: 13.5 }}>{displayClientName}</div>
                      <div className="muted" style={{ fontSize: 12 }}>{s.name} · {p.name.replace('Dra. ', '')}</div>
                    </div>
                    <span className="mono muted" style={{ fontSize: 12.5 }}>{s.price > 0 ? DB.money0(s.price) : '—'}</span>
                    <window.Badge cls={st.cls} dot>{st.label}</window.Badge>
                  </div>
                );
              })}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function TasksScreenFull(ctx) {
  const { tasks, clients, terms, toast } = ctx;
  const DB = window.DB;
  const blank = () => ({ title: '', dueDate: DB.iso(DB.today), dueTime: '09:00', clientId: 'none', prio: 'media' });
  const [adding, setAdding] = React.useState(false);
  const [form, setForm] = React.useState(blank);
  const cols = [
    { key: 'alta', label: 'Prioridade alta', cls: 'red' },
    { key: 'media', label: 'Prioridade media', cls: 'amber' },
    { key: 'baixa', label: 'Prioridade baixa', cls: 'blue' },
  ];
  const done = tasks.filter(t => t.done).length;
  const set = (key) => (e) => setForm({ ...form, [key]: e.target.value });
  const clientName = (id) => {
    const c = clients.find(x => x.id === Number(id));
    return c ? c.name : '';
  };
  const dueLabel = (t) => {
    if (t.dueDate) {
      const d = new Date(t.dueDate + 'T00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'short' });
      return t.dueTime ? d + ', ' + t.dueTime : d;
    }
    return t.due || 'Sem data';
  };
  const create = async () => {
    if (!form.title.trim()) return;
    const selectedClient = clientName(form.clientId);
    await ctx.addTask({
      title: form.title.trim(),
      dueDate: form.dueDate,
      dueTime: form.dueTime,
      prio: form.prio,
      clientId: form.clientId === 'none' ? null : Number(form.clientId),
      tag: selectedClient || 'Geral',
    });
    setForm(blank());
    setAdding(false);
    toast('Tarefa criada!');
  };

  return (
    <div className="content-inner fadein">
      <window.Header title="Tarefas & lembretes" sub={`${tasks.length - done} pendentes - ${done} concluidas`} action={
        <window.Button variant="primary" icon="plus" onClick={() => setAdding(true)}>Nova tarefa</window.Button>
      } />
      {adding && (
        <div className="card card-pad fadein" style={{ marginBottom: 16 }}>
          <div className="form-grid">
            <window.Field label="Titulo" full>
              <window.Input placeholder="Ex.: Confirmar retorno" value={form.title} autoFocus onChange={set('title')} onKeyDown={e => { if (e.key === 'Enter') create(); }} />
            </window.Field>
            <window.Field label="Dia"><window.Input type="date" value={form.dueDate} onChange={set('dueDate')} /></window.Field>
            <window.Field label="Hora"><window.Input type="time" value={form.dueTime} onChange={set('dueTime')} /></window.Field>
            <window.Field label={terms.client}>
              <window.Select value={form.clientId} onChange={set('clientId')}>
                <option value="none">Sem {terms.client.toLowerCase()}</option>
                {clients.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
              </window.Select>
            </window.Field>
            <window.Field label="Prioridade">
              <window.Select value={form.prio} onChange={set('prio')}>
                <option value="alta">Alta</option>
                <option value="media">Media</option>
                <option value="baixa">Baixa</option>
              </window.Select>
            </window.Field>
          </div>
          <div className="row" style={{ justifyContent: 'flex-end', gap: 10, marginTop: 14 }}>
            <window.Button variant="ghost" onClick={() => { setAdding(false); setForm(blank()); }}>Cancelar</window.Button>
            <window.Button variant="primary" icon="check" onClick={create}>Salvar tarefa</window.Button>
          </div>
        </div>
      )}
      <div className="grid-3">
        {cols.map(col => {
          const list = tasks.filter(t => t.prio === col.key);
          return (
            <div className="card" key={col.key}>
              <div className="card-head"><span className={'badge ' + col.cls}><i className="d" />{col.label}</span><span className="muted" style={{ fontSize: 12, marginLeft: 'auto' }}>{list.length}</span></div>
              <div className="card-pad" style={{ display: 'flex', flexDirection: 'column', gap: 9, minHeight: 80 }}>
                {list.length === 0 && <div className="muted" style={{ fontSize: 12.5, textAlign: 'center', padding: '14px 0' }}>Nada por aqui</div>}
                {list.map(t => (
                  <div key={t.id} className="card" style={{ padding: 12, display: 'flex', gap: 10, alignItems: 'flex-start', opacity: t.done ? .6 : 1 }}>
                    <window.Checkbox on={t.done} onClick={() => ctx.toggleTask(t.id, !t.done)} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 500, textDecoration: t.done ? 'line-through' : 'none' }}>{t.text}</div>
                      <div className="row wrap" style={{ gap: 7, marginTop: 7 }}>
                        <span className="badge plain" style={{ height: 20 }}><window.Icon name="clock" size={12} /> {dueLabel(t)}</span>
                        {(t.clientId || t.tag) && <span className="badge plain" style={{ height: 20 }}><window.Icon name="user" size={12} /> {clientName(t.clientId) || t.tag}</span>}
                      </div>
                    </div>
                    <button className="iconbtn" style={{ width: 26, height: 26 }} title="Excluir" onClick={() => ctx.deleteTask(t.id)}>
                      <window.Icon name="trash" size={13} />
                    </button>
                  </div>
                ))}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ---------------- MENSALIDADE (cobrança do SaaS — visão do cliente/admin) ---------------- */
function BillingScreen(ctx) {
  const DB = window.DB;
  const { toast } = ctx;
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(null);
  const [selPlanId, setSelPlanId] = React.useState(null);
  const [selMonths, setSelMonths] = React.useState(1);
  const [showCheckout, setShowCheckout] = React.useState(false);
  const [showPlanFeatures, setShowPlanFeatures] = React.useState(false);
  const [form, setForm] = React.useState({ name: '', doc: '', email: '' });
  const [submitting, setSubmitting] = React.useState(false);
  const [checking, setChecking] = React.useState(false);
  const [cancelling, setCancelling] = React.useState(false);
  const [pixData, setPixData] = React.useState(null);
  const [pixLoading, setPixLoading] = React.useState(false);

  const load = React.useCallback(async () => {
    try {
      const d = await window.API.billing.get();
      setData(d);
      setForm({ name: d.customer.name || '', doc: d.customer.doc || '', email: d.customer.email || '' });
      setSelPlanId(d.currentPlanId || (d.plans && d.plans[0] && d.plans[0].id) || null);
      setError(null);
    } catch (e) {
      setError(e.message || 'Erro ao carregar a mensalidade.');
    } finally {
      setLoading(false);
    }
  }, []);
  React.useEffect(() => { load(); }, [load]);

  // carrega o QR/copia-e-cola do PIX quando há uma cobrança em aberto
  const openPayId = data && data.openPayment ? data.openPayment.id : null;
  React.useEffect(() => {
    if (!openPayId) { setPixData(null); return; }
    let alive = true;
    setPixLoading(true);
    (async () => {
      try {
        const r = await window.API.billing.pix(openPayId);
        if (alive) setPixData(r && r.payload ? r : null);
      } catch { if (alive) setPixData(null); }
      finally { if (alive) setPixLoading(false); }
    })();
    return () => { alive = false; };
  }, [openPayId]);

  const statusMeta = {
    em_dia:   { label: 'Em dia',   cls: 'green', soft: 'success' },
    pendente: { label: 'Pendente', cls: 'amber', soft: 'warning' },
    atrasado: { label: 'Atrasado', cls: 'red',   soft: 'danger' },
  };
  const typeLabel = { MANUAL: 'Manual', CREDIT: 'Crédito', PIX: 'PIX', BOLETO: 'Boleto', CREDIT_CARD: 'Cartão', UNDEFINED: 'Online' };
  const payStatusLabel = { RECEIVED: 'Recebido', CONFIRMED: 'Confirmado', PENDING: 'Aguardando', OVERDUE: 'Vencido', REFUNDED: 'Estornado', CANCELLED: 'Cancelada' };
  const fmtDate = (d) => d ? new Date(String(d).replace(' ', 'T')).toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' }) : '—';
  const monthLabel = (m) => m === 1 ? '1 mês' : m + ' meses';

  if (loading) {
    return (
      <div className="content-inner fadein">
        <window.Header title="Mensalidade" sub="Plano e pagamentos da sua empresa" />
        <div className="card card-pad muted">Carregando...</div>
      </div>
    );
  }
  if (error) {
    return (
      <div className="content-inner fadein">
        <window.Header title="Mensalidade" sub="Plano e pagamentos da sua empresa" />
        <window.Empty icon="info" title="Não foi possível carregar" desc={error} />
      </div>
    );
  }

  const plans = data.plans || [];
  const payments = data.payments || [];
  const durations = data.durations || [1];
  const discounts = data.discounts || {};
  const sm = statusMeta[data.status] || statusMeta.em_dia;
  const currentPlan = plans.find(p => p.id === data.currentPlanId) || null;
  const selPlan = plans.find(p => p.id === selPlanId) || plans[0] || null;
  const selectedIsCurrentPlan = !!(currentPlan && selPlan && currentPlan.id === selPlan.id);
  const totalPaid = payments.filter(p => p.status === 'RECEIVED' || p.status === 'CONFIRMED').reduce((s, p) => s + p.value, 0);
  const accessUntil = [data.paidUntil, data.trialEndsAt].filter(Boolean).sort().pop() || null;
  const priceFor = (p, m) => p ? Math.round(p.price * m * (1 - ((discounts[String(m)] || 0) / 100)) * 100) / 100 : 0;
  const planLimits = (p) => !p ? '' : [
    ((p.maxUsers ?? p.maxAdmins) === 0 ? 'Acessos ilimitados' : (p.maxUsers ?? p.maxAdmins) + ((p.maxUsers ?? p.maxAdmins) === 1 ? ' acesso com login' : ' acessos com login')),
    (p.hasTeam ? (p.maxPros === 0 ? 'Equipe ilimitada' : 'Até ' + p.maxPros + ' na equipe') : 'Sem equipe'),
  ].join(' · ');
  const planComparisonRows = [
    { label: 'Agenda e calendário', value: () => true },
    { label: 'Cadastro de clientes / alunos', value: () => true },
    { label: 'Serviços, planos e aulas', value: () => true },
    { label: 'Tarefas e histórico', value: () => true },
    { label: 'Financeiro', value: (p) => p.hasFinance },
    { label: 'PIX, boleto e cartão', value: () => true },
    { label: 'Usuários com login e senha', value: (p) => (p.maxUsers ?? p.maxAdmins) === 0 ? 'Ilimitados' : String(p.maxUsers ?? p.maxAdmins) },
    { label: 'Equipe / profissionais', value: (p) => !p.hasTeam ? 'Não inclui' : (p.maxPros === 0 ? 'Ilimitada' : `Até ${p.maxPros}`) },
    { label: 'Gestáo das agendas da equipe', value: (p) => p.hasTeam },
    { label: 'Confirmações e lembretes por e-mail', value: (p) => p.hasAppointmentEmail },
    { label: 'Agendamento em grupo / reunião', value: (p) => p.hasGroupMeetings },
    { label: 'Chat interno', value: (p) => p.hasInternalChat ? 'Em breve' : false },
    { label: 'Sincronização com Google Drive', value: (p) => ['profissional', 'empresarial'].includes(p.slug) ? 'Em breve' : false },
    { label: 'Sincronização com Google Agenda', value: (p) => ['profissional', 'empresarial'].includes(p.slug) ? 'Em breve' : false },
    { label: 'Sincronização com Google Meet', value: (p) => p.slug === 'empresarial' ? 'Em breve' : false },
    { label: 'Suporte', value: (p) => p.hasSupport },
  ];
  const selTotal = priceFor(selPlan, selMonths);
  const openPay = data.openPayment || null;
  // pró-rata: crédito dos dias restantes do plano atual ao TROCAR de plano
  const lastPaid = payments.find(p => (p.status === 'RECEIVED' || p.status === 'CONFIRMED') && p.months > 0 && (p.planId === currentPlan?.id || p.planId == null));
  const todayStart = new Date();
  todayStart.setHours(0, 0, 0, 0);
  const trialRemainingDays = !currentPlan && data.trialEndsAt ? Math.max(0, Math.ceil((new Date(data.trialEndsAt + 'T00:00:00') - todayStart) / 86400000)) : 0;
  const accessRemainingDays = data.paidUntil ? Math.max(0, Math.ceil((new Date(data.paidUntil + 'T00:00:00') - todayStart) / 86400000)) : 0;
  const paidDate = lastPaid?.date ? new Date(String(lastPaid.date).slice(0, 10) + 'T00:00:00') : null;
  const usedPeriodDays = paidDate ? Math.max(0, Math.floor((todayStart - paidDate) / 86400000)) : 0;
  const remainingDays = lastPaid ? Math.max(0, lastPaid.months * 30 - usedPeriodDays) : Math.min(30, accessRemainingDays);
  const isPlanChange = currentPlan && selPlan && currentPlan.id !== selPlan.id && remainingDays > 0;
  const dailyValue = lastPaid ? ((lastPaid.value + (lastPaid.creditApplied || 0)) / Math.max(1, lastPaid.months * 30)) : (currentPlan ? currentPlan.price / 30 : 0);
  const periodCredit = isPlanChange ? Math.round(dailyValue * remainingDays * 100) / 100 : 0;
  const availableCredit = Math.round(((data.creditBalance || 0) + periodCredit) * 100) / 100;
  const credit = Math.min(availableCredit, selTotal);
  const creditRemaining = Math.round((availableCredit - credit) * 100) / 100;
  const netTotal = Math.round((selTotal - credit) * 100) / 100;
  const checkoutActionLabel = !selPlan ? 'Selecione um plano' : (
    selectedIsCurrentPlan ? 'Renovar ' + selPlan.name : (currentPlan ? 'Mudar para ' + selPlan.name : 'Assinar ' + selPlan.name)
  ) + ' · ' + monthLabel(selMonths) + ' · ' + DB.money(netTotal);
  const usage = data.usage || {};
  const loginUsers = Number(usage.loginUsers || 0);
  const teamMembers = Number(usage.teamMembers || 0);
  const maxPlanUsers = selPlan ? Number(selPlan.maxUsers ?? selPlan.maxAdmins ?? 0) : 0;
  const maxPlanTeam = selPlan ? Number(selPlan.maxPros || 0) : 0;
  const blockedUsers = maxPlanUsers > 0 ? Math.max(0, loginUsers - maxPlanUsers) : 0;
  const archivedTeam = selPlan && !selPlan.hasTeam ? teamMembers : 0;
  const excessTeam = selPlan && selPlan.hasTeam && maxPlanTeam > 0 ? Math.max(0, teamMembers - maxPlanTeam) : 0;
  const firstAdminName = usage.firstAdmin && usage.firstAdmin.name ? usage.firstAdmin.name : 'o primeiro administrador cadastrado';
  const planRank = { individual: 1, profissional: 2, empresarial: 3 };
  const isPlanRegression = !!(currentPlan && selPlan && (planRank[selPlan.slug] || 0) < (planRank[currentPlan.slug] || 0));
  const lostFeatures = !selPlan ? [] : [
    (!currentPlan || currentPlan.hasFinance) && !selPlan.hasFinance ? 'Financeiro' : null,
    (!currentPlan || currentPlan.hasTeam) && !selPlan.hasTeam ? 'Equipe' : null,
    (!currentPlan || currentPlan.hasGroupMeetings) && !selPlan.hasGroupMeetings ? 'reuniões em grupo' : null,
    (!currentPlan || currentPlan.hasInternalChat) && !selPlan.hasInternalChat ? 'chat interno' : null,
  ].filter(Boolean);
  const hasPlanImpact = isPlanRegression && (blockedUsers > 0 || archivedTeam > 0 || excessTeam > 0 || lostFeatures.length > 0);

  const startCheckout = () => {
    if (!data.asaasReady) { toast('Pagamento online ainda não configurado. Fale com o suporte.', 'x'); return; }
    if (!selPlan) { toast('Selecione um plano.', 'x'); return; }
    setShowCheckout(true);
  };
  const confirmCheckout = async () => {
    if (submitting || !selPlan) return;
    setSubmitting(true);
    try {
      const r = await window.API.billing.checkout({ planId: selPlan.id, months: selMonths, name: form.name, doc: form.doc, email: form.email });
      setShowCheckout(false);
      toast(r.confirmed ? 'Plano ativado usando seu crédito!' : ((r.credit > 0 ? 'Cobrança gerada com ' + DB.money(r.credit) + ' de crédito abatido!' : 'Cobrança gerada!') + ' O PIX aparecerá nesta tela.'), 'check');
      await load();
    } catch (e) {
      toast(e.message || 'Erro ao gerar a cobrança', 'x');
    } finally {
      setSubmitting(false);
    }
  };
  const verifyPayment = async () => {
    if (!openPay || checking) return;
    setChecking(true);
    try {
      const r = await window.API.billing.check(openPay.id);
      if (r.confirmed) {
        toast('Pagamento confirmado! Seu acesso foi estendido.', 'check');
        load();
      } else if (r.cancelled) {
        toast('Essa cobrança foi cancelada e não está mais disponível. Gere uma nova se quiser pagar.', 'info');
        load();
      } else {
        toast('Pagamento ainda não identificado. Se já pagou, aguarde alguns instantes e tente de novo.', 'info');
      }
    } catch (e) {
      toast(e.message || 'Erro ao verificar o pagamento', 'x');
    } finally {
      setChecking(false);
    }
  };
  const cancelCharge = async () => {
    if (!openPay || cancelling) return;
    if (!window.confirm('Cancelar esta cobrança em aberto? Depois você poderá gerar outra.')) return;
    setCancelling(true);
    try {
      await window.API.billing.cancel(openPay.id);
      toast('Cobrança cancelada.', 'check');
      load();
    } catch (e) {
      toast(e.message || 'Erro ao cancelar a cobrança', 'x');
    } finally {
      setCancelling(false);
    }
  };

  return (
    <div className="content-inner fadein">
      <window.Header title="Mensalidade" sub="Plano e pagamentos da sua empresa" />

      <div className="kpi-grid" style={{ marginBottom: 16 }}>
        <div className="card kpi">
          <div className="top"><div className="ic" style={{ background: 'var(--primary-soft)', color: 'var(--primary)' }}><window.Icon name="wallet" size={19} /></div><div className="lbl">Seu plano</div></div>
          <div className="val" style={{ fontSize: 22 }}>{currentPlan ? currentPlan.name : 'Período de teste'}</div>
          <div className="delta up" style={{ color: 'var(--text-3)' }}>{currentPlan ? DB.money(currentPlan.price) + '/mês' : 'Escolha um plano abaixo'}</div>
        </div>
        <div className="card kpi">
          <div className="top"><div className="ic" style={{ background: 'var(--' + sm.soft + '-soft)', color: 'var(--' + sm.soft + ')' }}><window.Icon name={data.status === 'em_dia' ? 'check' : 'clock'} size={19} /></div><div className="lbl">Situação</div></div>
          <div className="val" style={{ fontSize: 20, marginTop: 4 }}><window.Badge cls={sm.cls} dot>{sm.label}</window.Badge></div>
          <div className="delta up" style={{ color: 'var(--text-3)' }}>Último pagamento: {fmtDate(data.lastPaymentAt)}</div>
        </div>
        <div className="card kpi">
          <div className="top"><div className="ic" style={{ background: 'var(--success-soft)', color: 'var(--success)' }}><window.Icon name="check2" size={19} /></div><div className="lbl">Acesso até</div></div>
          <div className="val mono" style={{ fontSize: 22 }}>{accessUntil ? fmtDate(accessUntil) : '—'}</div>
          <div className="delta up" style={{ color: 'var(--text-3)' }}>{totalPaid > 0 ? 'Total pago: ' + DB.money(totalPaid) : (currentPlan ? '' : 'Período de teste')}</div>
        </div>
      </div>

      {openPay && (
        <div className="card card-pad" style={{ marginBottom: 16, borderColor: 'var(--warning)' }}>
          <div style={{ fontWeight: 600, fontSize: 14 }}>Cobrança em aberto — aguardando pagamento</div>
          <div className="muted" style={{ fontSize: 12.5, margin: '4px 0 12px' }}>{(openPay.months ? monthLabel(openPay.months) + ' · ' : '') + DB.money(openPay.value)}. Pague com PIX aqui (ou boleto/cartão pelo link) e confirme — o acesso é estendido na confirmação. Para assinar outro plano, cancele esta cobrança primeiro.</div>

          {pixData && pixData.payload && (
            <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', alignItems: 'flex-start', marginBottom: 14, padding: 14, background: 'var(--surface-2)', borderRadius: 'var(--r-md)' }}>
              {pixData.encodedImage && <img alt="QR Code PIX" src={'data:image/png;base64,' + pixData.encodedImage} style={{ width: 150, height: 150, borderRadius: 8, background: '#fff', flex: 'none' }} />}
              <div style={{ flex: 1, minWidth: 220 }}>
                <div style={{ fontWeight: 600, fontSize: 13 }}>Pague com PIX agora</div>
                <div className="muted" style={{ fontSize: 12, margin: '4px 0 9px' }}>Escaneie o QR Code ou copie o código abaixo no app do seu banco. A confirmação é automática.</div>
                <div className="row" style={{ gap: 6 }}>
                  <input className="input" readOnly value={pixData.payload} onFocus={(e) => e.target.select()} style={{ fontSize: 11, fontFamily: 'var(--font-mono)' }} />
                  <window.Button variant="outline" onClick={() => { if (navigator.clipboard) navigator.clipboard.writeText(pixData.payload); toast('Código PIX copiado!', 'check'); }}>Copiar</window.Button>
                </div>
              </div>
            </div>
          )}
          {pixLoading && !pixData && <div className="muted" style={{ fontSize: 12, marginBottom: 12 }}>Carregando PIX...</div>}

          <div className="row wrap" style={{ gap: 8 }}>
            {openPay.invoiceUrl && <window.Button variant="outline" onClick={() => window.open(openPay.invoiceUrl, '_blank')}>Boleto / cartão</window.Button>}
            <window.Button variant="primary" icon="refresh" disabled={checking} onClick={verifyPayment}>{checking ? 'Verificando...' : 'Já paguei / Verificar'}</window.Button>
            <window.Button variant="ghost" disabled={cancelling} onClick={cancelCharge}>{cancelling ? 'Cancelando...' : 'Cancelar cobrança'}</window.Button>
          </div>
        </div>
      )}

      {!openPay && (data.asaasReady ? (
        <div className="card card-pad" style={{ marginBottom: 16 }}>
          <div style={{ fontWeight: 600, fontSize: 14 }}>{currentPlan ? 'Renovar ou alterar plano' : 'Escolha seu plano'}</div>
          <div className="muted" style={{ fontSize: 12.5, marginBottom: 12 }}>{currentPlan ? 'A renovação soma tempo ao acesso pago atual. Para mudar de plano, selecione outra opção abaixo.' : `O período é somado ao seu acesso atual.${trialRemainingDays > 0 ? ` Você ainda tem ${trialRemainingDays} dia(s) de teste; eles serão mantidos e o plano começará depois do teste.` : (data.trialEndsAt ? ' Seu teste vai até ' + fmtDate(data.trialEndsAt) + '.' : '')}`}</div>
          {!currentPlan && <div style={{ padding: '10px 12px', marginBottom: 12, borderRadius: 'var(--r-sm)', background: 'var(--info-soft)', color: 'var(--info)', fontSize: 12.5, lineHeight: 1.5 }}>
            Durante o teste, todos os recursos e limites ficam liberados. Ao contratar, passam a valer os limites do plano escolhido; nenhum dado será apagado.
          </div>}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(160px,1fr))', gap: 10, marginBottom: 16 }}>
            {plans.map(p => {
              const isSel = selPlan && p.id === selPlan.id;
              return (
                <div key={p.id} style={{ position: 'relative', overflow: 'hidden', background: isSel ? 'var(--primary-soft)' : 'var(--surface)', border: '1px solid ' + (isSel ? 'var(--primary)' : 'var(--border-strong)'), borderRadius: 'var(--r-md)' }}>
                  <button type="button" onClick={() => setSelPlanId(p.id)} aria-pressed={isSel}
                    style={{ display: 'block', width: '100%', textAlign: 'left', cursor: 'pointer', border: 0, background: 'transparent', color: 'inherit', padding: '13px 14px 9px' }}>
                    <div style={{ fontWeight: 700, fontSize: 14 }}>{p.name}</div>
                    <div className="mono" style={{ fontSize: 16, fontWeight: 700, marginTop: 2 }}>{DB.money(p.price)}<span style={{ fontSize: 11, fontWeight: 400, color: 'var(--text-3)' }}>/mês</span></div>
                    <div className="muted" style={{ fontSize: 11, marginTop: 7, lineHeight: 1.5 }}>{planLimits(p)}</div>
                    {currentPlan && p.id === currentPlan.id && <span className="badge plain" style={{ position: 'absolute', top: 10, right: 10, fontSize: 10 }}>atual</span>}
                  </button>
                  <div style={{ padding: '0 10px 10px' }}>
                    <window.Button variant="ghost" size="sm" block onClick={() => setShowPlanFeatures(true)}>Ver funcionalidades</window.Button>
                  </div>
                </div>
              );
            })}
          </div>
          {hasPlanImpact && <div style={{ padding: '11px 12px', marginBottom: 14, border: '1px solid color-mix(in srgb, var(--warning) 35%, transparent)', borderRadius: 'var(--r-sm)', background: 'var(--warning-soft)', color: 'var(--text)', fontSize: 12.5, lineHeight: 1.55 }}>
            <div style={{ fontWeight: 700, color: 'var(--warning)', marginBottom: 3 }}>O que muda ao ativar o {selPlan && selPlan.name}</div>
            {maxPlanUsers === 1 && <div>• Somente <b>{firstAdminName}</b>, o primeiro administrador criado, continuará com acesso.</div>}
            {blockedUsers > 0 && <div>• {blockedUsers} usuário(s) excedente(s) ficarão sem acesso, mas serão preservados.</div>}
            {archivedTeam > 0 && <div>• {archivedTeam} membro(s) da equipe ficarão arquivados enquanto este plano estiver ativo.</div>}
            {excessTeam > 0 && <div>• Sua equipe excede o limite em {excessTeam}; os dados serão preservados e novos cadastros ficarão bloqueados até a adequação.</div>}
            {lostFeatures.length > 0 && <div>• Recursos indisponíveis: {lostFeatures.join(', ')}.</div>}
            <div>• Agendamentos e históricos não serão apagados. Um upgrade restaura os recursos e acessos conforme o novo limite.</div>
          </div>}
          <div className="muted" style={{ fontSize: 12, marginBottom: 7 }}>Por quanto tempo:</div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(108px,1fr))', gap: 8, marginBottom: 16 }}>
            {durations.map(m => {
              const isSel = m === selMonths; const disc = discounts[String(m)] || 0;
              return (
                <button key={m} type="button" onClick={() => setSelMonths(m)}
                  style={{ textAlign: 'left', cursor: 'pointer', background: isSel ? 'var(--primary-soft)' : 'var(--surface)', border: '1px solid ' + (isSel ? 'var(--primary)' : 'var(--border-strong)'), borderRadius: 'var(--r-sm)', padding: '9px 11px' }}>
                  <div style={{ fontWeight: 600, fontSize: 12.5 }}>{monthLabel(m)}</div>
                  <div className="mono" style={{ fontSize: 14, fontWeight: 700, marginTop: 2 }}>{DB.money(priceFor(selPlan, m))}</div>
                  {disc > 0 ? <div style={{ fontSize: 10.5, color: 'var(--success)', marginTop: 1 }}>{disc}% off</div> : <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 1 }}>&nbsp;</div>}
                </button>
              );
            })}
          </div>
          {availableCredit > 0 && (
            <div style={{ fontSize: 12.5, marginBottom: 12, padding: '9px 12px', background: 'var(--success-soft)', color: 'var(--success)', borderRadius: 'var(--r-sm)' }}>
              Crédito disponível: {DB.money(availableCredit)}{periodCredit > 0 ? ` (${remainingDays} dia(s) restantes do plano ${currentPlan.name})` : ''}. {DB.money(credit)} será abatido e você paga {DB.money(netTotal)}{creditRemaining > 0 ? `; o saldo de ${DB.money(creditRemaining)} fica para a próxima renovação.` : '.'}
            </div>
          )}
          <window.Button variant="primary" icon="wallet" onClick={startCheckout}>{checkoutActionLabel}</window.Button>
        </div>
      ) : (
        <div className="card card-pad" style={{ marginBottom: 16 }}>
          <div style={{ fontWeight: 600, fontSize: 14 }}>Pagamento online</div>
          <div className="muted" style={{ fontSize: 12.5 }}>Em breve você poderá assinar um plano direto por aqui (PIX, boleto e cartão).</div>
        </div>
      ))}

      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="row between wrap" style={{ padding: 14, gap: 12, borderBottom: '1px solid var(--border)' }}>
          <div className="row" style={{ gap: 8 }}><h3 style={{ fontSize: 14.5 }}>Histórico de pagamentos</h3><span className="badge plain">{payments.length}</span></div>
        </div>
        {payments.length === 0 ? <window.Empty icon="wallet" title="Nenhum pagamento" desc="Ainda não há pagamentos registrados para a sua empresa." /> : (
          <div style={{ overflowX: 'auto' }}>
            <table className="tbl">
              <thead><tr><th>Data</th><th>Período</th><th>Forma</th><th>Status</th><th>Nota fiscal</th><th style={{ textAlign: 'right' }}>Valor</th></tr></thead>
              <tbody>
                {payments.map(p => (
                  <tr key={p.id}>
                    <td className="muted">{fmtDate(p.date)}</td>
                    <td>{p.months > 0 ? monthLabel(p.months) : '—'}</td>
                    <td><span className="badge plain">{typeLabel[p.type] || p.type}</span></td>
                    <td><window.Badge cls={(p.status === 'RECEIVED' || p.status === 'CONFIRMED') ? 'green' : ((p.status === 'OVERDUE' || p.status === 'CANCELLED') ? 'red' : 'amber')} dot>{payStatusLabel[p.status] || p.status}</window.Badge></td>
                    <td>
                      {p.taxInvoiceUrl
                        ? <window.Button variant="outline" size="sm" icon="download" onClick={() => window.open(p.taxInvoiceUrl, '_blank')}>Baixar nota</window.Button>
                        : <span className="muted">—</span>}
                    </td>
                    <td className="mono" style={{ textAlign: 'right', fontWeight: 700 }}>{DB.money(p.value)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </div>

      {showPlanFeatures && (
        <window.Modal wide title="Compare os planos" sub="Veja o que está incluído em cada opção" onClose={() => setShowPlanFeatures(false)}
          footer={<><div style={{ flex: 1 }} /><window.Button variant="primary" onClick={() => setShowPlanFeatures(false)}>Fechar</window.Button></>}>
          <div style={{ overflowX: 'auto', margin: '-4px' }}>
            <table className="tbl" style={{ minWidth: 650 }}>
              <thead>
                <tr>
                  <th style={{ minWidth: 210 }}>Funcionalidade</th>
                  {plans.map(p => <th key={p.id} style={{ textAlign: 'center', minWidth: 135 }}><div style={{ fontSize: 13 }}>{p.name}</div><div className="mono" style={{ color: 'var(--primary)', marginTop: 3 }}>{DB.money(p.price)}<span style={{ fontSize: 10, fontWeight: 400 }}>/mês</span></div></th>)}
                </tr>
              </thead>
              <tbody>
                {planComparisonRows.map(row => (
                  <tr key={row.label}>
                    <td style={{ fontWeight: 600 }}>{row.label}</td>
                    {plans.map(p => {
                      const value = row.value(p);
                      return <td key={p.id} style={{ textAlign: 'center', color: value === false ? 'var(--text-3)' : 'var(--text)' }}>
                        {typeof value === 'boolean'
                          ? <span aria-label={value ? 'Incluído' : 'Não incluído'} style={{ display: 'inline-flex', color: value ? 'var(--success)' : 'var(--text-3)' }}><window.Icon name={value ? 'check' : 'x'} size={17} stroke={2.4} /></span>
                          : value}
                      </td>;
                    })}
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </window.Modal>
      )}

      {showCheckout && (
        <window.Modal title="Confirmar pagamento" sub={(selPlan ? selPlan.name + ' · ' : '') + monthLabel(selMonths) + ' · ' + DB.money(netTotal)} onClose={() => setShowCheckout(false)}
          footer={<><div style={{ flex: 1 }} /><window.Button variant="ghost" onClick={() => setShowCheckout(false)}>Cancelar</window.Button>
            <window.Button variant="primary" icon="check" disabled={submitting} onClick={confirmCheckout}>{submitting ? 'Gerando...' : 'Gerar cobrança'}</window.Button></>}>
          <div className="form-grid">
            <window.Field label="Nome / Razão social" full><window.Input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} placeholder="Nome de quem paga" /></window.Field>
            <window.Field label="CPF ou CNPJ" full><window.Input value={form.doc} onChange={(e) => setForm({ ...form, doc: e.target.value })} placeholder="Somente números" /></window.Field>
            <window.Field label="E-mail" full><window.Input type="email" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} placeholder="para o recibo (opcional)" /></window.Field>
          </div>
          {credit > 0 && <p style={{ fontSize: 12.5, color: 'var(--success)', margin: '0 0 4px' }}>{DB.money(credit)} de crédito abatido — total a pagar: {DB.money(netTotal)}.{creditRemaining > 0 ? ` Saldo de ${DB.money(creditRemaining)} guardado para a próxima renovação.` : ''}</p>}
          {trialRemainingDays > 0 && <p style={{ fontSize: 12.5, color: 'var(--success)', margin: '0 0 4px' }}>Seus {trialRemainingDays} dia(s) restantes de teste serão preservados e somados ao período comprado.</p>}
          {hasPlanImpact && <div style={{ padding: '10px 12px', marginTop: 10, borderRadius: 'var(--r-sm)', background: 'var(--warning-soft)', fontSize: 12.5, lineHeight: 1.55 }}>
            <div style={{ fontWeight: 700, color: 'var(--warning)', marginBottom: 3 }}>Confirme que está ciente das limitações:</div>
            {maxPlanUsers === 1 && <div>• Apenas <b>{firstAdminName}</b> continuará com acesso.</div>}
            {blockedUsers > 0 && <div>• {blockedUsers} usuário(s) ficarão bloqueados, sem exclusão dos dados.</div>}
            {archivedTeam > 0 && <div>• A equipe será arquivada e ficará disponível novamente após upgrade.</div>}
            {excessTeam > 0 && <div>• A equipe atual excede em {excessTeam} o limite deste plano.</div>}
            {lostFeatures.length > 0 && <div>• Você deixará de acessar: {lostFeatures.join(', ')}.</div>}
            <div>• Histórico e agendamentos serão preservados.</div>
          </div>}
          <p className="muted" style={{ fontSize: 12, marginTop: 4 }}>O QR Code PIX aparecerá nesta tela, sem redirecionamento. Se preferir boleto ou cartão, use o link de outras formas de pagamento. O acesso é estendido assim que o pagamento for confirmado.</p>
        </window.Modal>
      )}
    </div>
  );
}

Object.assign(window, { FinanceScreen, TasksScreen: TasksScreenFull, HistoryScreen, BillingScreen });
