/* Console partenaire — activité (design dev-pages-a.jsx / dev-pages-b.jsx) : Tableau de bord, Opérations,
   détail d'opération, Solde et dépôts (recharge), Consommation. Toutes les valeurs viennent de l'API. */
const SVC_API = {assurance:'assurance', billetterie:'billet', esim:'esim', sms:'sms'};
const MV_T = {debit:['Débit','mute'], recharge:['Recharge','ok'], remboursement:['Remboursement','info'], ajustement:['Ajustement','warn']};
const moisCourant = () => { const s = new Date().toLocaleDateString('fr-FR', {month:'long', year:'numeric'}); return s.charAt(0).toUpperCase() + s.slice(1); };

// Toutes les pages d'une liste paginée (plafond de sécurité)
async function toutesLesPages(route, max=40){
  const sep = route.includes('?') ? '&' : '?'; let page = 1, l = [], total = 0, tronque = false;
  while(true){ const r = await api('GET', `${route}${sep}page=${page}`); l = l.concat(r.donnees || []); total = r.total ?? l.length;
    if(!r.page_suivante) break; if(page >= max){ tronque = true; break; } page = r.page_suivante; }
  return {donnees:l, total, tronque};
}
function exporterCsv(nom, entetes, lignes){
  const esc = v => { const s = v == null ? '' : String(v); return /[";\n]/.test(s) ? `"${s.replace(/"/g,'""')}"` : s; };
  const txt = '﻿' + [entetes, ...lignes].map(l=>l.map(esc).join(';')).join('\n');
  const a = document.createElement('a'); a.href = URL.createObjectURL(new Blob([txt], {type:'text/csv;charset=utf-8'})); a.download = nom; a.click(); setTimeout(()=>URL.revokeObjectURL(a.href), 5000);
}
const Barres = ({vals, h=110, gap=5, titre, dernier=true, couleur}) => { const max = Math.max(1, ...vals.map(v=>v[0]));
  return <div style={{display:'flex', alignItems:'flex-end', gap, height:h}} role="img" aria-label={titre}>{vals.map((v,i)=><span key={i} title={v[1]} style={{flex:1, height:Math.max(2, v[0]/max*100)+'%', borderRadius:'6px 6px 2px 2px', background:couleur || (dernier && i===vals.length-1?V.acc:'var(--bar)')}}></span>)}</div>; };

function BalanceCard({s, onTop, compact}){
  const m = useMob(); if(!s) return <Card><Skel h={compact?150:190} r={16}/></Card>;
  const isLow = s.solde_bas;
  return <Card style={{display:'flex', flexDirection:'column', gap:compact?12:16, position:'relative', overflow:'hidden', ...(isLow?{border:'1px solid var(--warnLine)'}:{})}}>
    <div style={{display:'flex', alignItems:'center', gap:10}}><Tile icon="wallet" s={40}/><span style={{flex:1}}><span style={{display:'block', fontWeight:800, fontSize:14, color:V.ink}}>Solde prépayé</span><span style={{display:'block', fontSize:12, color:V.ink3}}>Unique pour tous les services</span></span>
      {isLow ? <Badge tone="warn" icon="warning">Solde bas</Badge> : <Badge tone="ok" dot>Disponible</Badge>}</div>
    <div><Money n={s.solde ?? s.montant ?? 0} s={compact?(m?26:30):(m?30:38)}/><span style={{display:'block', marginTop:6, fontSize:12.5, fontWeight:500, color:V.ink3}}>{s.seuil_alerte ? `Alerte sous ${fmt(s.seuil_alerte)} F CFA` : 'Aucune alerte de solde bas'}{s.en_verification > 0 ? ` · ${fmt(s.en_verification)} F CFA retenus en vérification` : ''}</span></div>
    <div style={{display:'flex', alignItems:'center', gap:6, flexWrap:'wrap'}}><span style={{fontSize:12, fontWeight:600, color:V.ink3, marginRight:2}}>Débité par</span>{Object.entries(SVCS).map(([k,v])=>{ const on = (s.services_actifs || ['assurance']).includes(k) || (k==='assurance' && v.on); return <span key={k} title={v.n + (on?'':' · bientôt')} style={{display:'inline-flex', alignItems:'center', gap:5, height:26, padding:'0 9px', borderRadius:99, background:on?v.bg:V.sub, color:on?v.c:V.ink3, fontSize:11.5, fontWeight:700}}><I n={v.i} s={13}/>{v.s}</span>; })}</div>
    {onTop && <div style={{display:'flex', gap:8}}><Btn icon="plus" onClick={onTop}>Recharger</Btn></div>}
  </Card>;
}
function UseCard({s}){
  if(!s) return <Card><Skel h={190} r={16}/></Card>;
  const d = s.debits_du_mois || {}; const lignes = Object.entries(SVC_API).map(([k, sv])=>[sv, d[k] || 0]); const tot = lignes.reduce((a,u)=>a+u[1],0);
  return <Card style={{display:'flex', flexDirection:'column', gap:12}}><div style={{display:'flex', alignItems:'center'}}><H s={16} style={{flex:1}}>Débits par service</H><span style={{fontSize:12, fontWeight:600, color:V.ink3}}>{moisCourant()}</span></div>
    <Money n={tot} s={22}/>
    {lignes.map(([k,v])=>{ const sv = SVCS[k]; return <div key={k}><div style={{display:'flex', alignItems:'center', gap:8, marginBottom:6}}><I n={sv.i} s={16} c={sv.c}/><span style={{flex:1, fontWeight:700, fontSize:13}}>{sv.s}</span>{!sv.on && <Badge>Bientôt</Badge>}<span style={{fontWeight:800, fontSize:13}}>{v?fmt(v):'—'}</span></div>
      <span style={{display:'block', height:6, borderRadius:99, background:V.sub}}><span style={{display:'block', height:'100%', width:(tot?v/tot*100:0)+'%', borderRadius:99, background:sv.c}}></span></span></div>; })}
  </Card>;
}

const ETAPES_DEM = {cle_sandbox:['key','Votre première clé lms_test_…','keys'], premier_devis:['calculator','POST /assurance/auto/devis','docs'], contrat_test:['file-text','POST /assurance/auto/contrats','docs'],
  webhook:['webhooks-logo','Recevoir contrat.actif, depot.valide…','hooks'], profil_entreprise:['buildings','Informations légales et documents','prod'], production:['rocket-launch','Après validation par LeMultiservice','prod']};
function Dash(){
  const A = useApp(), m = useMob(); const [per, setPer] = React.useState(0); const periode = per ? '7j' : 'jour';
  const [d, recharger, etat] = useDonnees(() => api('GET', `tableau-de-bord?mode=${A.mode}&periode=${periode}`), [A.mode, periode]);
  const [s] = useDonnees(() => api('GET', `solde?mode=${A.mode}`), [A.mode]);
  const [st] = useDonnees(() => api('GET', 'statut'), []);
  const [appels] = useDonnees(() => toutesLesPages(`journal?mode=${A.mode}&periode=${periode}`, 8), [A.mode, periode]);
  if(etat.erreur) return <Page><PageHead title={`Bonjour ${A.moi.membre.prenom}`}/><ErreurCarte e={etat.erreur} recharger={recharger}/></Page>;
  const dem = d?.demarrage; const items = (dem?.etapes || []).map(e=>[...(ETAPES_DEM[e.code] || ['circle','', 'dash']), e]);
  const done = dem ? dem.faites : 0, total = dem ? dem.total : 6;
  const vol = d?.volume || {};
  // Appels bucketisés : 12 tranches de 2 h aujourd'hui, ou 7 jours
  let bars = [], axe;
  if(appels){ const l = appels.donnees; const now = new Date();
    if(!per){ const base = new Date(now.getFullYear(), now.getMonth(), now.getDate()); bars = Array.from({length:12}, (_,i)=>{ const n = l.filter(x=>{ const t = new Date(x.date); return t>=base && Math.floor((t-base)/72e5)===i; }).length; return [n, `${String(i*2).padStart(2,'0')} h – ${String(i*2+2).padStart(2,'0')} h · ${n} appel${n>1?'s':''}`]; });
      axe = <><span>00 h</span><span>06 h</span><span>12 h</span><span>18 h</span><span>24 h</span></>; }
    else { bars = Array.from({length:7}, (_,i)=>{ const j = new Date(now.getFullYear(), now.getMonth(), now.getDate()-6+i); const n = l.filter(x=>new Date(x.date).toDateString()===j.toDateString()).length; return [n, `${j.toLocaleDateString('fr-FR', {weekday:'long', day:'numeric'})} · ${n} appel${n>1?'s':''}`, j]; });
      axe = bars.map((b,i)=><span key={i}>{b[2].toLocaleDateString('fr-FR', {weekday:'short'}).replace('.','')}</span>); } }
  const stats = [['Appels API', fmt(vol.appels||0), per?'7 derniers jours':'Aujourd\'hui'], ['Opérations réussies', fmt(vol.operations_reussies||0), 'Contrats émis'], ['Taux d\'erreur', pct(vol.taux_erreur), `${fmt(vol.erreurs||0)} erreur${vol.erreurs>1?'s':''}`]];
  const incidents = (st?.incidents || []).slice(0, 3);
  return <Page>
    <PageHead title={`Bonjour ${A.moi.membre.prenom}`} sub={A.mode==='test'?'Vous êtes en sandbox. Les soldes et opérations affichés sont ceux de test.':'Production · les opérations débitent votre solde réel.'}
      actions={<><Btn kind="secondary" icon="book-open-text" onClick={()=>A.go('docs')}>Documentation</Btn>{A.peut('finance') && <Btn icon="plus" onClick={()=>A.go('bal', 'recharger')}>Recharger</Btn>}</>}/>
    {!A.moi.entreprise.production_ouverte && <Card pad={0} style={{overflow:'hidden'}}>
      <div style={{display:'flex', alignItems:'center', gap:14, padding:'18px 20px', borderBottom:`1px solid ${V.line}`, flexWrap:'wrap'}}>
        <span style={{flex:1, minWidth:200}}><H s={17}>Intégration</H><Muted style={{marginTop:3}}>{dem ? `${done} étape${done>1?'s':''} sur ${total} · de l'inscription à la production` : 'Chargement…'}</Muted></span>
        <div style={{width:m?'100%':220, height:8, borderRadius:99, background:V.sub, overflow:'hidden'}}><span style={{display:'block', height:'100%', width:(total?done/total*100:0)+'%', background:V.acc, borderRadius:99, transition:'width .4s'}}></span></div></div>
      {!dem ? <SkelRows n={2}/> : <div style={{display:'grid', gridTemplateColumns:m?'1fr':'repeat(3,1fr)'}}>
        {items.map((x,i)=>{ const e = x[3], next = !e.fait && items.slice(0,i).every(y=>y[3].fait);
          return <button key={e.code} className="lb lr" onClick={()=>A.go(x[2])} style={{display:'flex', alignItems:'flex-start', gap:12, padding:'16px 20px', border:0, borderTop:(m?i:i>=3)?`1px solid ${V.line}`:'none', borderLeft:!m&&i%3?`1px solid ${V.line}`:'none', background:next?'var(--nextBg)':'transparent', cursor:'pointer', textAlign:'left', fontFamily:V.body}}>
            <span style={{width:26, height:26, borderRadius:99, flex:'none', display:'grid', placeItems:'center', background:e.fait?V.acc:next?V.card:V.sub, border:e.fait?0:`1.5px solid ${next?V.acc:V.line}`}}>{e.fait?<I n="check" s={14} c="#fff"/>:<span style={{fontWeight:800, fontSize:11.5, color:next?V.accD:V.ink3}}>{i+1}</span>}</span>
            <span style={{flex:1, minWidth:0}}><span style={{display:'block', fontWeight:700, fontSize:13.5, color:e.fait?V.ink3:V.ink, textDecoration:e.fait?'line-through':'none'}}>{e.libelle}</span><span style={{display:'block', marginTop:2, fontSize:12, color:V.ink3, fontFamily:x[1].startsWith('POST')?V.mono:V.body}}>{x[1]}</span></span>
            {next && <I n="arrow-right" s={16} c={V.accD}/>}</button>; })}</div>}
    </Card>}
    <Grid cols="minmax(0,1.2fr) minmax(0,1fr)"><BalanceCard s={s} compact onTop={A.peut('finance') ? ()=>A.go('bal', 'recharger') : null}/><UseCard s={s}/></Grid>
    <Grid cols="minmax(0,1.6fr) minmax(0,1fr)">
      <Card>
        <div style={{display:'flex', alignItems:'center', gap:10, marginBottom:18}}><H s={17} style={{flex:1}}>Volume</H><Seg opts={['Aujourd\'hui','7 jours']} i={per} set={setPer}/></div>
        {!d ? <Skel h={60}/> : <div style={{display:'grid', gridTemplateColumns:'repeat(3,1fr)', gap:12}}>{stats.map((x,i)=><div key={x[0]} style={{minWidth:0}}><Lbl style={{fontSize:10}}>{x[0]}</Lbl><span style={{display:'block', marginTop:6, fontFamily:V.disp, fontWeight:800, fontSize:m?20:26, letterSpacing:'-0.02em', color:i===2&&vol.taux_erreur>0?V.warn:V.ink}}>{x[1]}</span><span style={{display:'block', fontSize:12, color:V.ink3, fontWeight:600}}>{x[2]}</span></div>)}</div>}
        <div style={{marginTop:20}}>{appels ? <Barres vals={bars} gap={per?10:5} titre="Appels par période"/> : <Skel h={110}/>}</div>
        <div style={{display:'flex', justifyContent:'space-between', marginTop:6, fontSize:11, color:V.ink3, fontWeight:600}}>{axe}</div>
        {appels && !appels.donnees.length && <Muted style={{fontSize:12, marginTop:8}}>Aucun appel sur la période.</Muted>}{appels?.tronque && <Muted style={{fontSize:11.5, marginTop:6}}>Graphique calculé sur les {fmt(appels.donnees.length)} appels les plus récents.</Muted>}
      </Card>
      <Card>
        <div style={{display:'flex', alignItems:'center', marginBottom:10}}><H s={17} style={{flex:1}}>Incidents récents</H><Btn kind="ghost" sm onClick={()=>A.go('help')}>Statut</Btn></div>
        {!st ? <Skel h={120}/> : incidents.length ? incidents.map((x,i)=>{ const ok = x.statut==='resolu'; return <div key={i} style={{display:'flex', gap:10, padding:'11px 0', borderTop:i?`1px solid ${V.line}`:'none'}}>
          <I n={ok?'check-circle':x.gravite==='maintenance'?'wrench':'warning'} s={18} c={TN[ok?'ok':x.gravite==='indisponible'?'bad':'warn'][1]} fill/><span style={{flex:1, minWidth:0}}><span style={{display:'block', fontWeight:700, fontSize:13.5, color:V.ink}}>{x.titre}</span><span style={{display:'block', fontSize:12, color:V.ink3}}>{ok ? 'Résolu' : x.message}</span></span><span style={{fontSize:11.5, fontWeight:600, color:V.ink3, whiteSpace:'nowrap'}}>{ok ? new Date(x.cree_le).toLocaleDateString('fr-FR', {day:'numeric', month:'short'}) : 'En cours'}</span></div>; })
          : <div style={{display:'flex', alignItems:'center', gap:10, padding:'10px 0'}}><I n="check-circle" s={20} c={V.acc} fill/><span><span style={{display:'block', fontWeight:700, fontSize:13.5}}>Aucun incident</span><span style={{display:'block', fontSize:12, color:V.ink3}}>Sur les 7 derniers jours</span></span></div>}
      </Card>
    </Grid>
    <Tbl chargement={!d} head={<div style={{display:'flex', alignItems:'center', padding:'16px 20px'}}><H s={17} style={{flex:1}}>Dernières opérations</H><Btn kind="ghost" sm iconR="arrow-right" onClick={()=>A.go('ops')}>Tout voir</Btn></div>}
      cols={[{k:'r',l:'Référence',w:'1.4fr'},{k:'p',l:'Immatriculation',w:'1fr'},{k:'i',l:'Assureur',w:'1fr',hideM:1},{k:'d',l:'Débité',w:'1fr',r:1},{k:'s',l:'Statut',w:'.9fr'}]}
      rows={(d?.dernieres_operations||[]).map(o=>({r:<Mono c={V.ink}>{o.reference}</Mono>, p:<Mono>{o.immatriculation}</Mono>, i:<Assureur a={o.assureur}/>, d:o.montant_debite?<Money n={o.montant_debite} s={14}/>:<span style={{color:V.ink3}}>—</span>, s:ST[o.statut]?<StatusB st={o.statut}/>:<Badge>{o.statut}</Badge>}))}
      onRow={i=>A.go('op', d.dernieres_operations[i].id)} empty={<EmptyS icon="list-checks" title="Aucune opération" sub={A.mode==='test'?'Faites un devis puis émettez un contrat de test avec votre clé sandbox.':'Vos contrats de production apparaîtront ici.'} action={<Btn kind="secondary" icon="book-open-text" onClick={()=>A.go('docs')}>Voir le guide</Btn>}/>}/>
  </Page>;
}

const FILTRES_OPS = [['','Tous'],['actif','Actifs'],['en_cours','En cours'],['a_verifier','À vérifier'],['echec','Échecs'],['annule','Annulés']];
function Ops(){
  const A = useApp(); const toast = useToast();
  const lireQ = () => { try { const v = sessionStorage.getItem('lms_q') || ''; sessionStorage.removeItem('lms_q'); return v; } catch(e){ return ''; } };
  const [f, setF] = React.useState(0), [q, setQ] = React.useState(lireQ), [qd, setQd] = React.useState(q), [page, setPage] = React.useState(1), [exp, setExp] = React.useState(false);
  React.useEffect(()=>{ const t = setTimeout(()=>{ setQd(q); setPage(1); }, 350); return ()=>clearTimeout(t); }, [q]);
  React.useEffect(()=>{ const h = () => { const v = lireQ(); if(v){ setQ(v); setF(0); } }; addEventListener('lms-recherche', h); return ()=>removeEventListener('lms-recherche', h); }, []);
  const st = FILTRES_OPS[f][0];
  const [d, recharger, etat] = useDonnees(() => api('GET', `operations?mode=${A.mode}&statut=${st}&q=${encodeURIComponent(qd)}&page=${page}`), [A.mode, st, qd, page]);
  const exporter = async () => { setExp(true); try { const r = await toutesLesPages(`operations?mode=${A.mode}&statut=${st}&q=${encodeURIComponent(qd)}`);
    exporterCsv(`operations-${A.mode}-${jourIso(Date.now())}.csv`, ['Identifiant','Référence','Assuré','Immatriculation','Assureur','Date d\'effet','Échéance','Prix client','Débité','Statut','Créé le'], r.donnees.map(o=>[o.id,o.reference,o.assure,o.immatriculation,(ASSUREURS[o.assureur]||[o.assureur])[0],o.date_effet,o.date_echeance,o.prix_client,o.montant_debite,(ST[o.statut]||[o.statut])[0],o.cree_le]));
    if(r.tronque) toast('Export limité aux 1 000 premières opérations'); } catch(e){ toast(e.message, 'bad'); } setExp(false); };
  const l = d?.donnees || [];
  return <Page>
    <PageHead title="Opérations" sub="Toutes les opérations passées via l'API, par service." actions={<Btn kind="secondary" icon="download-simple" disabled={exp || !d?.total} onClick={exporter}>{exp?'Export…':'Exporter'}</Btn>}/>
    <Tabs opts={[['Assurance auto'],['Assurance voyage','Bientôt',1],['Billetterie','Bientôt',1],['eSIM','Bientôt',1],['SMS','Bientôt',1]]} i={0} set={()=>{}}/>
    <div style={{display:'flex', gap:8, flexWrap:'wrap', alignItems:'center'}}>
      <div style={{flex:'1 1 240px'}}><Inp value={q} onChange={e=>setQ(e.target.value)} placeholder="Référence, assuré, immatriculation…" aria-label="Rechercher une opération" lead={<I n="magnifying-glass" s={17}/>} trail={q ? <button aria-label="Effacer" onClick={()=>setQ('')} style={{border:0, background:'none', cursor:'pointer', color:V.ink3, display:'flex'}}><I n="x" s={16}/></button> : null} style={{height:40}}/></div>
      <div style={{overflowX:'auto', maxWidth:'100%'}}><Seg opts={FILTRES_OPS.map(x=>x[1])} i={f} set={k=>{setF(k); setPage(1);}}/></div></div>
    {d?.a_verifier > 0 && !st && <Notice tone="warn" title={`${d.a_verifier} contrat${d.a_verifier>1?'s':''} à vérifier`}>L'assureur n'a pas confirmé l'émission dans le délai. Aucune action de votre part : nous vous notifierons par <Mono>contrat.actif</Mono> ou <Mono>contrat.echec</Mono>.</Notice>}
    {etat.erreur ? <ErreurCarte e={etat.erreur} recharger={recharger}/> :
    <Tbl chargement={!d || (etat.chargement && !l.length)} cols={[{k:'r',l:'Réf. partenaire',w:'1.35fr'},{k:'a',l:'Assuré',w:'1fr'},{k:'p',l:'Immat.',w:'.85fr'},{k:'i',l:'Assureur',w:'1fr',hideM:1},{k:'d',l:'Période',w:'1.55fr',hideM:1},{k:'px',l:'Prix client',w:'.95fr',r:1,hideM:1},{k:'db',l:'Débité',w:'.95fr',r:1},{k:'s',l:'Statut',w:'.9fr'}]}
      rows={l.map(o=>({r:<Mono c={V.ink}>{o.reference}</Mono>, a:<Ellipse>{o.assure || '—'}</Ellipse>, p:<Mono>{o.immatriculation}</Mono>, i:<Assureur a={o.assureur}/>,
        d:<span style={{color:V.ink2, fontSize:12.5, whiteSpace:'nowrap'}}>{dateCourte(o.date_effet)} → {o.date_echeance ? dateCourte(o.date_echeance) : '—'}</span>, px:<Money n={o.prix_client||0} s={13.5} muted/>, db:o.montant_debite?<Money n={o.montant_debite} s={13.5}/>:<span style={{color:V.ink3}}>—</span>, s:ST[o.statut]?<StatusB st={o.statut}/>:<Badge>{o.statut}</Badge>}))}
      onRow={i=>A.go('op', l[i].id)} empty={<EmptyS icon="list-checks" title="Aucune opération" sub={qd||st ? 'Aucun contrat ne correspond à ce filtre.' : 'Vos contrats émis par l\'API apparaîtront ici.'}/>} foot={d?.total > 0 && <Pager n={d.total} per={d.par_page||25} page={page} set={setPage}/>}/>}
  </Page>;
}

const ICONE_CHRONO = (t) => /demand/i.test(t) ? 'paper-plane-tilt' : /débit/i.test(t) ? 'wallet' : /rembours/i.test(t) ? 'arrow-u-up-left' : /webhook/i.test(t) ? 'webhooks-logo' : /annul/i.test(t) ? 'prohibit' : /résili/i.test(t) ? 'file-x' : /avenant/i.test(t) ? 'pencil-simple' : /remorque/i.test(t) ? 'truck-trailer' : /échec|refus/i.test(t) ? 'x-circle' : /émis|actif|attestation/i.test(t) ? 'seal-check' : 'circle';
const montantsTexte = (t) => String(t).replace(/(\d{4,}) F/g, (x, n) => `${fmt(+n)} F CFA`);
function OpDetail({id}){
  const A = useApp(), m = useMob();
  const [d, recharger, etat] = useDonnees(() => api('GET', `operations/${encodeURIComponent(id)}`), [id]);
  if(etat.erreur) return <Page><PageHead back="Opérations" onBack={()=>A.go('ops')} title="Opération"/><ErreurCarte e={etat.erreur} recharger={recharger}/></Page>;
  if(!d) return <Page><Skel w={120} h={30}/><Skel w="40%" h={34}/><Grid cols="minmax(0,1.5fr) minmax(0,1fr)"><Card><Skel h={260}/></Card><Card><Skel h={260}/></Card></Grid></Page>;
  const c = d.contrat, mt = d.montants || {}; const docs = c.documents || {}; const echec = c.statut === 'echec';
  const assure = [c.assure?.prenom, c.assure?.nom].filter(Boolean).join(' ') || [c.souscripteur?.prenom, c.souscripteur?.nom].filter(Boolean).join(' ');
  const liens = [['Attestation d\'assurance','file-pdf',docs.attestation],['Carte brune','file-text',docs.carte_brune],['Facture','receipt',docs.facture],['Conditions particulières','file-text',docs.conditions],...(c.remorque?.lien_attestation ? [['Attestation de la remorque','file-pdf',c.remorque.lien_attestation]] : [])].filter(x=>x[2] !== undefined);
  return <Page>
    <PageHead back="Opérations" onBack={()=>A.go('ops')} title={<span style={{display:'flex', alignItems:'center', gap:12, flexWrap:'wrap'}}><Mono s={m?17:21} c={V.ink}>{c.reference}</Mono>{ST[c.statut]?<StatusB st={c.statut}/>:<Badge>{c.statut}</Badge>}</span>} sub={`${assure || '—'} · ${c.immatriculation} · ${(ASSUREURS[c.assureur]||[c.assureur])[0]}`}
      actions={<><CopieId id={c.id}/>{docs.attestation ? <Btn kind="secondary" icon="download-simple" onClick={()=>window.open(docs.attestation, '_blank', 'noopener')}>Attestation</Btn> : null}</>}/>
    {c.mode==='test' && <Notice tone="warn" icon="flask">Contrat sandbox : les documents portent la mention « SPÉCIMEN » et n'ont aucune valeur.</Notice>}
    {c.erreur && <Notice tone="bad" title={c.erreur.code}>{c.erreur.message}</Notice>}
    {c.statut==='a_verifier' && <Notice tone="warn" title="Émission non confirmée">L'assureur n'a pas encore confirmé. Le montant est retenu ; ne rejouez pas la souscription, nous vous notifierons par <Mono>contrat.actif</Mono> ou <Mono>contrat.echec</Mono>.</Notice>}
    <Grid cols="minmax(0,1.5fr) minmax(0,1fr)">
      <div style={{display:'flex', flexDirection:'column', gap:16, minWidth:0}}>
        <Card><H s={16} style={{marginBottom:16}}>Chronologie</H>
          {(d.chronologie||[]).map((t,i,a)=>{ const ton = TN[t.ton] ? t.ton : 'mute'; return <div key={i} style={{display:'flex', gap:12}}>
            <div style={{display:'flex', flexDirection:'column', alignItems:'center'}}><span style={{width:30, height:30, borderRadius:99, display:'grid', placeItems:'center', background:TN[ton][0], flex:'none'}}><I n={ICONE_CHRONO(t.evenement)} s={15} c={TN[ton][1]}/></span>{i<a.length-1 && <span style={{flex:1, width:2, background:V.line, margin:'4px 0'}}></span>}</div>
            <div style={{paddingBottom:i<a.length-1?16:0, paddingTop:5, minWidth:0}}><span style={{display:'block', fontWeight:700, fontSize:13.5}}>{montantsTexte(t.evenement)}</span><Mono s={11.5} c={V.ink3}>{D(t.date).replace(' ', ' · ')}</Mono></div></div>; })}
          {!(d.chronologie||[]).length && <Muted>Aucun événement.</Muted>}
        </Card>
        <Tbl head={<div style={{padding:'16px 20px 12px'}}><H s={16}>Requêtes API liées</H></div>} cols={[{k:'t',l:'Heure',w:'.8fr'},{k:'e',l:'Endpoint',w:'2fr'},{k:'c',l:'Statut',w:'.6fr'},{k:'d',l:'Durée',w:'.7fr',r:1}]}
          rows={(d.requetes||[]).map(r=>({t:<Mono>{new Date(r.date).toLocaleTimeString('fr-FR')}</Mono>, e:<span style={{display:'flex', gap:6, minWidth:0}}><Method m={r.methode}/><Ellipse style={{fontFamily:V.mono, fontSize:12.5, color:V.ink}}>{r.endpoint}</Ellipse></span>, c:<Http code={r.http}/>, d:<Mono>{fmt(r.duree_ms)} ms</Mono>}))}
          onRow={i=>A.go('logs', d.requetes[i].id)} empty={<Muted style={{padding:'14px 20px'}}>Aucune requête liée.</Muted>}/>
      </div>
      <div style={{display:'flex', flexDirection:'column', gap:16, minWidth:0}}>
        <Card><H s={16} style={{marginBottom:6}}>Montants</H>
          <KV rows={[['Prix client',fmt(mt.prix_client||0)+' F CFA'],['Remise partenaire','− '+fmt(mt.remise_partenaire||0)+' F CFA',V.accD],['Frais de service','+ '+fmt(mt.frais_service||0)+' F CFA']]}/>
          <div style={{display:'flex', alignItems:'baseline', marginTop:8, paddingTop:12, borderTop:`1px solid ${V.line}`}}><span style={{flex:1, fontWeight:700, fontSize:13.5}}>Montant débité</span><Money n={mt.montant_debite||0} s={20}/></div></Card>
        <Card><H s={16} style={{marginBottom:6}}>Contrat</H>
          <KV rows={[['Police',c.police||'—',null,1],['Attestation',c.attestation||'—',null,1],['Période',`${dateCourte(c.date_effet)} → ${dateCourte(c.date_echeance)}`],['Véhicule',[c.vehicule?.marque, c.vehicule?.modele].filter(Boolean).join(' ') || '—'],['Garanties',(c.garanties||[]).length+' garantie'+((c.garanties||[]).length>1?'s':'')],...(c.remorque ? [['Remorque',c.remorque.immatriculation,null,1]] : []),...(c.annule_le ? [['Annulé le',D(c.annule_le)]] : [])]}/>
          {(c.garanties||[]).length > 0 && <div style={{display:'flex', gap:6, flexWrap:'wrap', marginTop:8}}>{c.garanties.map(g=><span key={g} style={{fontFamily:V.mono, fontSize:11.5, padding:'4px 9px', borderRadius:8, background:V.sub, color:V.ink2}}>{g}</span>)}</div>}</Card>
        <Card><H s={16} style={{marginBottom:10}}>Documents</H>
          {echec ? <Muted>Aucun document : le contrat n'a pas été émis.</Muted>
          : docs.specimen ? <Muted>Sandbox : pas de document officiel, les liens de l'assureur sont vides (<Mono>specimen: true</Mono>).</Muted>
          : liens.map((x,i)=><div key={x[0]} style={{display:'flex', alignItems:'center', gap:10, padding:'10px 0', borderTop:i?`1px solid ${V.line}`:'none'}}><I n={x[1]} s={20} c={x[2]?V.bad:V.ink3}/><span style={{flex:1, fontWeight:700, fontSize:13.5, color:x[2]?V.ink:V.ink3}}>{x[0]}</span>{x[2] ? <Btn kind="ghost" sm icon="download-simple" label={`Télécharger ${x[0]}`} onClick={()=>window.open(x[2], '_blank', 'noopener')}/> : <span style={{fontSize:12, color:V.ink3}}>Indisponible</span>}</div>)}</Card>
        <Card><H s={16} style={{marginBottom:6}}>Mouvements de solde</H>
          {(d.mouvements||[]).length ? <KV rows={d.mouvements.map(x=>[`${DC(x.date)} · ${(MV_T[x.type]||[x.type])[0]}`, (x.montant<0?'− ':'+ ')+fmt(x.montant)+' F CFA', x.montant<0?V.bad:V.accD])}/> : <Muted>Aucun mouvement.</Muted>}</Card>
      </div>
    </Grid>
  </Page>;
}
function CopieId({id}){ const [c, copy] = useCopy(); return <Btn kind="secondary" icon={c?'check':'copy'} onClick={()=>copy(id)}>{c?'Copié':id.slice(0,8)+'…'}</Btn>; }

const PERIODES_REL = () => { const n = new Date(); const d0 = new Date(n.getFullYear(), n.getMonth(), 1), d1 = new Date(n.getFullYear(), n.getMonth()-1, 1), f1 = new Date(n.getFullYear(), n.getMonth(), 0);
  const nom = d => { const s = d.toLocaleDateString('fr-FR', {month:'long', year:'numeric'}); return s.charAt(0).toUpperCase()+s.slice(1); };
  return [['tout','Toutes les dates',null,null],['mois',nom(d0),jourIso(d0),null],['prec',nom(d1),jourIso(d1),jourIso(f1)],['30j','30 derniers jours',jourIso(Date.now()-30*864e5),null]]; };
function Balances({param}){
  const A = useApp(), m = useMob(); const toast = useToast();
  const [modal, setModal] = React.useState(param==='recharger' && A.peut('finance')), [tab, setTab] = React.useState(0), [fs, setFs] = React.useState(''), [ft, setFt] = React.useState(''), [fp, setFp] = React.useState('tout'), [page, setPage] = React.useState(1), [exp, setExp] = React.useState(false);
  const [s, rS] = useDonnees(() => api('GET', `solde?mode=${A.mode}`), [A.mode]);
  const per = PERIODES_REL().find(x=>x[0]===fp); const filtre = `mode=${A.mode}&service=${fs}&type=${ft}${per[2]?`&depuis=${per[2]}`:''}${per[3]?`&jusqua=${per[3]}`:''}`;
  const [mv, rM, {erreur:eM}] = useDonnees(() => api('GET', `mouvements?${filtre}&page=${page}`), [filtre, page]);
  const [deps, rD] = useDonnees(() => api('GET', `depots?mode=${A.mode}`), [A.mode]);
  const [seuil, setSeuil] = React.useState(''), [occ, setOcc] = React.useState(false);
  React.useEffect(()=>{ if(s) setSeuil(fmt(s.seuil_alerte||0)); }, [s]);
  const tout = () => { rS(); rM(); rD(); A.rafraichir(); };
  const exporter = async () => { setExp(true); try { const r = await toutesLesPages(`mouvements?${filtre}`);
    exporterCsv(`releve-${A.mode}-${jourIso(Date.now())}.csv`, ['Date','Type','Service','Libellé','Objet','Montant','Solde après'], r.donnees.map(x=>[x.date, (MV_T[x.type]||[x.type])[0], x.service, x.libelle, x.reference, x.montant, x.solde_apres])); } catch(e){ toast(e.message, 'bad'); } setExp(false); };
  const enregistrerSeuil = async () => { setOcc(true); try { await api('PATCH', 'solde/seuil', {mode:A.mode, seuil_alerte:+seuil.replace(/\D/g,'')||0}); toast('Seuil enregistré'); rS(); A.rafraichir(); } catch(e){ toast(e.message, 'bad'); } setOcc(false); };
  const svcNom = k => SVCS[SVC_API[k]] ? SVCS[SVC_API[k]] : null;
  return <Page>
    <PageHead title="Solde et dépôts" sub="Un seul solde prépayé. Chaque opération le débite, quel que soit le service." actions={A.peut('finance') && <Btn icon="plus" onClick={()=>setModal(true)}>Recharger</Btn>}/>
    <Grid cols="minmax(0,1.2fr) minmax(0,1fr)"><BalanceCard s={s} onTop={A.peut('finance') ? ()=>setModal(true) : null}/><UseCard s={s}/></Grid>
    {s?.solde_bas && <Notice tone="warn" title="Solde sous votre seuil d'alerte" action={A.peut('finance') && <Btn sm onClick={()=>setModal(true)}>Recharger</Btn>}>Les opérations seront refusées avec <Mono>solde_insuffisant</Mono> quand le solde ne couvrira plus une opération.</Notice>}
    <Tabs opts={[['Relevé'],['Dépôts', deps ? String(deps.total ?? deps.donnees.length) : null],['Alerte de solde bas']]} i={tab} set={setTab}/>
    {tab===0 && (eM ? <ErreurCarte e={eM} recharger={rM}/> : <Tbl chargement={!mv} head={<div style={{display:'flex', gap:8, padding:'14px 16px', flexWrap:'wrap'}}>
        <div style={{width:m?'100%':200}}><Sel label="Service" opts={[['','Tous les services'],['assurance','Assurance'],['compte','Compte']]} value={fs} onChange={v=>{setFs(v); setPage(1);}} style={{height:40}}/></div>
        <div style={{width:m?'48%':180}}><Sel label="Type" opts={[['','Tous les types'],['recharge','Recharge'],['debit','Débit'],['remboursement','Remboursement'],['ajustement','Ajustement']]} value={ft} onChange={v=>{setFt(v); setPage(1);}} style={{height:40}}/></div>
        <div style={{width:m?'48%':190}}><Sel label="Période" opts={PERIODES_REL().map(x=>[x[0],x[1]])} value={fp} onChange={v=>{setFp(v); setPage(1);}} style={{height:40}}/></div>
        <span style={{flex:1}}></span><Btn kind="secondary" sm icon="file-csv" disabled={exp || !mv?.total} onClick={exporter}>{exp?'Export…':'CSV'}</Btn></div>}
      cols={[{k:'d',l:'Date',w:'.9fr'},{k:'t',l:'Type',w:'1fr'},{k:'s',l:'Service',w:'1fr'},{k:'r',l:'Libellé · objet',w:'1.8fr',hideM:1},{k:'a',l:'Montant',w:'1.1fr',r:1},{k:'b',l:'Solde après',w:'1.1fr',r:1}]}
      rows={(mv?.donnees||[]).map(x=>{ const sv = svcNom(x.service); return {d:<span style={{color:V.ink2, whiteSpace:'nowrap'}}>{DC(x.date)}</span>, t:<Badge tone={(MV_T[x.type]||[0,'mute'])[1]}>{(MV_T[x.type]||[x.type])[0]}</Badge>, s:sv?<span style={{display:'flex', alignItems:'center', gap:7}}><I n={sv.i} s={16} c={sv.c}/>{sv.s}</span>:<span style={{color:V.ink3}}>Compte</span>,
        r:<span style={{display:'flex', flexDirection:'column', minWidth:0}}><Ellipse style={{fontSize:13, fontWeight:600}}>{x.libelle}</Ellipse>{x.reference && <Mono s={11.5}>{x.reference.length>20 ? x.reference.slice(0,12)+'…' : x.reference}</Mono>}</span>, a:<Money n={x.montant} s={14} sign/>, b:<Money n={x.solde_apres} s={14} muted/>}; })}
      empty={<EmptyS icon="receipt" title="Aucun mouvement" sub="Rien sur cette période pour ces filtres."/>} foot={mv?.total > 0 && <Pager n={mv.total} per={mv.par_page||25} page={page} set={setPage}/>}/>)}
    {tab===1 && <Tbl chargement={!deps} cols={[{k:'r',l:'Référence',w:'1.2fr'},{k:'m',l:'Moyen',w:'1fr'},{k:'d',l:'Date',w:'.9fr',hideM:1},{k:'a',l:'Montant',w:'1fr',r:1},{k:'st',l:'Statut',w:'1.3fr'}]}
      rows={(deps?.donnees||[]).map(x=>({r:<Mono c={V.ink}>{x.id}</Mono>, m:{virement:'Virement bancaire', sandbox:'Sandbox', wave:'Wave', orange_money:'Orange Money', carte:'Carte bancaire'}[x.moyen] || x.moyen, d:<span style={{color:V.ink2}}>{dateCourte(x.cree_le)}</span>, a:<Money n={x.statut==='valide' && x.montant_credite ? x.montant_credite : x.montant_declare} s={14}/>,
        st:<span style={{display:'flex', flexDirection:'column', gap:3}}>{ST[x.statut]?<StatusB st={x.statut}/>:<Badge>{x.statut}</Badge>}{x.statut==='rejete' && (x.motif_detail||x.motif_rejet) && <span style={{fontSize:11.5, color:V.bad, fontWeight:600}}>{x.motif_detail||x.motif_rejet}</span>}</span>}))}
      empty={<EmptyS icon="bank" title="Aucun dépôt" sub={A.mode==='test'?'Rechargez votre solde de test : il est crédité aussitôt.':'Rechargez votre solde par virement bancaire.'} action={A.peut('finance') && <Btn icon="plus" onClick={()=>setModal(true)}>Recharger</Btn>}/>}/>}
    {tab===2 && <Card style={{display:'flex', alignItems:'center', gap:14, flexWrap:'wrap'}}><Tile icon="bell-ringing" s={40}/><span style={{flex:1, minWidth:200}}><span style={{display:'block', fontWeight:700, fontSize:14}}>Seuil d'alerte</span><span style={{display:'block', fontSize:12.5, color:V.ink3}}>Email, SMS et webhook <Mono>solde.bas</Mono> aux membres Finance et Propriétaire. 0 = pas d'alerte.</span></span>
      <div style={{width:m?'100%':220}}><Inp value={seuil} disabled={!A.peut('finance')} onChange={e=>setSeuil(e.target.value.replace(/\D/g,'').replace(/\B(?=(\d{3})+(?!\d))/g,' '))} trail="F CFA" aria-label="Seuil d'alerte"/></div>
      {A.peut('finance') && <Btn disabled={occ || !s || +seuil.replace(/\D/g,'') === s.seuil_alerte} onClick={enregistrerSeuil}>{occ?'Enregistrement…':'Enregistrer'}</Btn>}</Card>}
    {modal && <Recharge onClose={()=>setModal(false)} solde={s} onFait={tout}/>}
  </Page>;
}

function Recharge({onClose, solde, onFait}){
  const A = useApp(); const test = A.mode==='test';
  const [meth, setMeth] = React.useState(test ? 'sandbox' : null), [amt, setAmt] = React.useState(test ? '1 000 000' : '5 000 000'), [proof, setProof] = React.useState(null), [done, setDone] = React.useState(null), [err, setErr] = React.useState(null), [load, setLoad] = React.useState(false);
  const [co] = useDonnees(() => test ? Promise.resolve(null) : api('GET', 'depots/coordonnees'), []);
  const M = [['virement','Virement bancaire','bank','Validé sous 24 h ouvrées',true],['wave','Wave','../commun/logos/wave.png','Bientôt'],['orange_money','Orange Money','../commun/logos/orange-money.png','Bientôt'],['carte','Carte bancaire','credit-card','Bientôt']];
  const n = +amt.replace(/\D/g,'')||0;
  const envoyer = async () => { setErr(null); setLoad(true);
    try { const fd = new FormData(); fd.append('mode', A.mode); fd.append('moyen', test ? 'sandbox' : meth); fd.append('montant', String(n)); if(co?.reference_virement) fd.append('reference', co.reference_virement); if(proof) fd.append('preuve', proof);
      const r = await api('POST', 'depots', fd); setDone(r); onFait && onFait(); } catch(e){ setErr(e); } setLoad(false); };
  if(done) return <Modal title={done.statut==='valide'?'Recharge réussie':'Dépôt enregistré'} icon={done.statut==='valide'?'check-circle':'hourglass'} tone={done.statut==='valide'?undefined:'warn'} onClose={onClose} foot={<Btn onClick={onClose}>Fermer</Btn>}>
    {done.statut==='valide' ? <Muted><b style={{color:V.ink}}>{fmt(done.montant_credite || n)} F CFA</b> ont été crédités sur votre solde.</Muted>
      : <Muted>Votre dépôt <Mono c={V.ink}>{done.id}</Mono> de <b style={{color:V.ink}}>{fmt(n)} F CFA</b> est <b style={{color:V.warn}}>en attente</b>. Nous le validons dès réception du virement ; vous serez notifié par email et par le webhook <Mono>depot.valide</Mono>.</Muted>}
    {test && <Notice tone="warn" icon="flask">Sandbox : aucun paiement réel n'a été effectué.</Notice>}</Modal>;
  const coVides = co && !co.iban && !co.beneficiaire;
  return <Modal title="Recharger le solde" icon="plus-circle" onClose={onClose} w={560} foot={meth && <>{!test && <Btn kind="secondary" onClick={()=>{setMeth(null); setErr(null);}}>Retour</Btn>}<Btn disabled={!n || n<1000 || load || (meth==='virement' && !proof)} onClick={envoyer}>{load?'Envoi…':test?`Créditer ${fmt(n)} F CFA`:'Envoyer la preuve'}</Btn></>}>
    <Field label="Montant" hint={solde ? `Solde actuel : ${fmt(solde.solde)} F CFA. Utilisable pour tous les services.` : 'Utilisable pour tous les services.'}><Inp value={amt} onChange={e=>setAmt(e.target.value.replace(/\D/g,'').replace(/\B(?=(\d{3})+(?!\d))/g,' '))} trail="F CFA" inputMode="numeric" aria-label="Montant" style={{fontFamily:V.disp, fontWeight:800, fontSize:18, height:52}}/></Field>
    {(!meth || test) && <div style={{display:'flex', gap:6, flexWrap:'wrap', marginTop:-6}}>{(test?['500 000','1 000 000','5 000 000','10 000 000']:['1 000 000','5 000 000','10 000 000','25 000 000']).map(v=><button key={v} className="lb" onClick={()=>setAmt(v)} style={{height:32, padding:'0 12px', borderRadius:10, border:`1px solid ${amt===v?V.acc:V.line}`, background:amt===v?V.mint:V.card, color:amt===v?V.accD:V.ink2, fontFamily:V.body, fontWeight:700, fontSize:12.5, cursor:'pointer'}}>{v}</button>)}</div>}
    {test ? <Notice tone="info" icon="flask">Sandbox : aucun paiement réel, le solde de test est crédité aussitôt.</Notice>
    : !meth ? <div><Lbl>Moyen de paiement</Lbl><div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:8, marginTop:10}}>{M.map(x=><button key={x[0]} className="lb lc" disabled={!x[4]} onClick={()=>setMeth(x[0])} style={{display:'flex', alignItems:'center', gap:12, padding:14, borderRadius:18, border:`1px solid ${V.line}`, background:V.card, cursor:x[4]?'pointer':'not-allowed', opacity:x[4]?1:.6, textAlign:'left', fontFamily:V.body}}>
      {x[2].includes('/') ? <img src={x[2]} alt="" style={{width:38, height:38, borderRadius:12, objectFit:'cover'}}/> : <Tile icon={x[2]} s={38}/>}<span style={{flex:1, minWidth:0}}><span style={{display:'block', fontWeight:700, fontSize:13.5, color:V.ink}}>{x[1]}</span><span style={{display:'block', fontSize:11.5, color:V.ink3}}>{x[3]}</span></span></button>)}</div></div>
    : <>{!co ? <Skel h={120} r={18}/> : <div style={{padding:16, borderRadius:18, background:V.sub, border:`1px solid ${V.line}`}}>
        {coVides ? <Notice tone="info" icon="bank" title="Coordonnées bancaires bientôt disponibles">LeMultiservice les communique à l'ouverture de votre production. Écrivez au support si vous devez faire un virement dès maintenant.</Notice>
          : <KV rows={[['Bénéficiaire',co.beneficiaire||'—'],['Banque',co.banque||'—'],['IBAN',co.iban||'—',null,1],['Code SWIFT',co.swift||'—',null,1]]}/>}
        <div style={{display:'flex', alignItems:'center', gap:10, marginTop:10, padding:'12px 14px', borderRadius:14, background:V.warnBg}}><span style={{flex:1, minWidth:0}}><span style={{display:'block', fontWeight:700, fontSize:11, letterSpacing:'.08em', textTransform:'uppercase', color:V.warn}}>Référence à indiquer sur le virement</span><Mono s={15} c={V.ink}>{co.reference_virement}</Mono></span><CopyBtn text={co.reference_virement}/></div>
        {co.delai && <Muted style={{fontSize:12, marginTop:8}}>{co.delai}.</Muted>}</div>}
      {proof ? <div style={{display:'flex', alignItems:'center', gap:10, padding:'12px 14px', borderRadius:16, border:`1px solid ${V.line}`}}><I n={/pdf$/i.test(proof.name)?'file-pdf':'file-image'} s={22} c={V.bad}/><span style={{flex:1, minWidth:0}}><Ellipse style={{display:'block', fontWeight:700, fontSize:13.5}}>{proof.name}</Ellipse><span style={{fontSize:11.5, color:V.ink3}}>{(proof.size/1048576).toLocaleString('fr-FR', {maximumFractionDigits:1})} Mo</span></span><Btn kind="ghost" sm icon="x" label="Retirer" onClick={()=>setProof(null)}/></div>
        : <Drop onFile={f=>{ if(f.size > 10*1048576) setErr({message:'Fichier trop lourd : 10 Mo maximum.'}); else { setErr(null); setProof(f); } }} accept=".pdf,.jpg,.jpeg,.png" hint="Avis de virement · PDF, JPG ou PNG · 10 Mo maximum"/>}</>}
    {err && <Notice tone="bad">{err.message}</Notice>}
  </Modal>;
}

function Conso(){
  const A = useApp(), m = useMob(); const toast = useToast(); const [per, setPer] = React.useState(0); const jours = per ? 30 : 7;
  const depuis = jourIso(Date.now() - (jours-1)*864e5);
  const [j, rJ, {erreur:eJ}] = useDonnees(async () => { const [tous, err] = await Promise.all([api('GET', `journal?mode=${A.mode}&periode=${per?'30j':'7j'}`), api('GET', `journal?mode=${A.mode}&periode=${per?'30j':'7j'}&resultat=erreur`)]); return {appels:tous.total||0, erreurs:err.total||0}; }, [A.mode, per]);
  const [mv, rM, {erreur:eM}] = useDonnees(async () => { const [deb, remb] = await Promise.all([toutesLesPages(`mouvements?mode=${A.mode}&type=debit&depuis=${depuis}`), toutesLesPages(`mouvements?mode=${A.mode}&type=remboursement&depuis=${depuis}`)]); return {deb, remb}; }, [A.mode, per]);
  const serie = Array.from({length:jours}, (_,i)=>{ const d = new Date(Date.now() - (jours-1-i)*864e5); return {jour:jourIso(d), d, debit:0, rembourse:0, ops:0}; });
  const idx = Object.fromEntries(serie.map((x,i)=>[x.jour,i]));
  if(mv){ mv.deb.donnees.forEach(x=>{ const k = idx[jourIso(x.date)]; if(k!=null){ serie[k].debit += -x.montant; if(/^off_/.test(x.reference||'')) serie[k].ops++; } });
    mv.remb.donnees.forEach(x=>{ const k = idx[jourIso(x.date)]; if(k!=null) serie[k].rembourse += x.montant; }); }
  const debTot = serie.reduce((a,x)=>a+x.debit,0), rembTot = serie.reduce((a,x)=>a+x.rembourse,0), opsTot = serie.reduce((a,x)=>a+x.ops,0);
  const parSvc = {}; if(mv) mv.deb.donnees.forEach(x=>{ const k = SVC_API[x.service] || x.service; parSvc[k] = parSvc[k] || {n:0, montant:0}; parSvc[k].montant += -x.montant; if(/^off_/.test(x.reference||'')) parSvc[k].n++; });
  const exporter = () => exporterCsv(`consommation-${A.mode}-${jours}j.csv`, ['Jour','Contrats émis','Débité (F CFA)','Remboursé (F CFA)','Net (F CFA)'], serie.map(x=>[x.jour, x.ops, x.debit, x.rembourse, x.debit - x.rembourse]));
  const kpi = (l, v, sub, money) => <Card pad={18} style={{minWidth:0}}><Lbl>{l}</Lbl>{money!=null ? <div style={{marginTop:8}}><Money n={money} s={m?18:22}/></div> : <span style={{display:'block', marginTop:8, fontFamily:V.disp, fontWeight:800, fontSize:m?20:24, letterSpacing:'-0.02em', color:V.ink}}>{v}</span>}{sub && <span style={{display:'block', marginTop:4, fontSize:12, fontWeight:600, color:V.ink3}}>{sub}</span>}</Card>;
  const erreur = eJ || eM;
  return <Page max={1240}>
    <PageHead title="Consommation" sub="Appels et montants débités, par service." actions={<><Seg opts={['7 jours','30 jours']} i={per} set={setPer}/><Btn kind="secondary" icon="download-simple" disabled={!mv} onClick={exporter}>Exporter</Btn></>}/>
    {erreur ? <ErreurCarte e={erreur} recharger={()=>{rJ(); rM();}}/> : <>
    <div style={{display:'grid', gridTemplateColumns:m?'1fr 1fr':'repeat(4,1fr)', gap:m?12:16}}>
      {j ? kpi('Appels API', fmt(j.appels), `${jours} derniers jours`) : <Card pad={18}><Skel h={56}/></Card>}
      {mv ? kpi('Contrats émis', fmt(opsTot), 'Souscriptions débitées') : <Card pad={18}><Skel h={56}/></Card>}
      {mv ? kpi('Montant débité', null, rembTot ? `${fmt(rembTot)} F CFA remboursés depuis` : 'Aucun remboursement', debTot) : <Card pad={18}><Skel h={56}/></Card>}
      {j ? kpi('Taux d\'erreur', pct(j.appels ? j.erreurs/j.appels*100 : 0), `${fmt(j.erreurs)} erreur${j.erreurs>1?'s':''}`) : <Card pad={18}><Skel h={56}/></Card>}
    </div>
    <Card><div style={{display:'flex', alignItems:'center', gap:14, marginBottom:18, flexWrap:'wrap'}}><H s={16} style={{flex:1}}>Montant débité par jour</H>{Object.entries(SVCS).map(([k,sv])=><span key={k} style={{display:'flex', alignItems:'center', gap:6, fontSize:12, fontWeight:600, color:sv.on?V.ink2:V.ink3}}><span style={{width:10, height:10, borderRadius:3, background:sv.on?sv.c:V.line}}></span>{sv.s}</span>)}</div>
      {!mv ? <Skel h={200}/> : <div style={{display:'flex', alignItems:'flex-end', gap:m?2:4, height:200}} role="img" aria-label="Montant débité par jour">{(()=>{ const max = Math.max(1, ...serie.map(x=>x.debit)); return serie.map((x,i)=><span key={x.jour} title={`${x.d.toLocaleDateString('fr-FR', {day:'numeric', month:'long'})} · ${fmt(x.debit)} F CFA · ${x.ops} contrat${x.ops>1?'s':''}`} style={{flex:1, height:Math.max(1.5, x.debit/max*100)+'%', borderRadius:'5px 5px 1px 1px', background:x.debit?SVCS.assurance.c:V.line, opacity:x.debit?.4+(i/serie.length)*.6:1}}></span>); })()}</div>}
      <div style={{display:'flex', justifyContent:'space-between', marginTop:6, fontSize:11, color:V.ink3, fontWeight:600}}>{[serie[0], serie[Math.floor(jours/2)], serie[jours-1]].map((x,i)=><span key={i}>{x.d.toLocaleDateString('fr-FR', {day:'numeric', month:'short'})}</span>)}</div>
      {mv && !debTot && <Muted style={{marginTop:10, fontSize:12.5}}>Aucun débit sur la période.</Muted>}
      {(mv?.deb.tronque || mv?.remb.tronque) && <Muted style={{marginTop:6, fontSize:11.5}}>Calcul limité aux 1 000 derniers mouvements de la période.</Muted>}</Card>
    <Tbl chargement={!mv} cols={[{k:'s',l:'Service',w:'1.6fr'},{k:'o',l:'Contrats émis',w:'1fr',r:1},{k:'a',l:'Montant débité',w:'1.2fr',r:1}]}
      rows={Object.keys(SVCS).map(k=>({s:<span style={{display:'flex', alignItems:'center', gap:10}}><SvcIcon k={k} s={30}/>{SVCS[k].n}{!SVCS[k].on && <Badge>Bientôt</Badge>}</span>, o:SVCS[k].on?fmt(parSvc[k]?.n||0):<span style={{color:V.ink3}}>—</span>, a:SVCS[k].on?<Money n={parSvc[k]?.montant||0} s={14}/>:<span style={{color:V.ink3}}>—</span>}))}/>
    </>}
  </Page>;
}

Object.assign(window, {SVC_API, MV_T, toutesLesPages, exporterCsv, Barres, BalanceCard, UseCard, Dash, Ops, OpDetail, CopieId, Balances, Recharge, Conso});
