import { useEffect, useMemo, useRef, useState } from 'react';
import { usePagination } from '../hooks/usePagination.js';
import Pagination from '../components/Pagination.jsx';
import PageIcon from '../components/PageIcon.jsx';
import EmptyState from '../components/EmptyState.jsx';
import { useLocation, useNavigate } from 'react-router-dom';
import { api } from '../api.js';
import { useInvestisseur } from '../context/InvestisseurContext.jsx';
import Modal from '../components/Modal.jsx';
import ConfirmModal from '../components/ConfirmModal.jsx';
import SoldeChart from '../components/SoldeChart.jsx';
import DistributionChart from '../components/DistributionChart.jsx';
import { fmtEUR, fmtDate, today } from '../utils/format.js';
import DepotsMensuelTable from '../components/DepotsMensuelTable.jsx';
import * as XLSX from 'xlsx';

/* ── Helpers export ──────────────────────────────────────────── */
function dlBlob(content, filename, type) {
  const blob = new Blob([content], { type });
  const url  = URL.createObjectURL(blob);
  const a    = document.createElement('a');
  a.href = url; a.download = filename; a.click();
  URL.revokeObjectURL(url);
}

/* ── Indicateur de progression ── */
function TrendBadge({ current, prev, invert = false }) {
  if (prev == null || prev === 0) return null;
  const diff = current - prev;
  const pct  = (diff / prev) * 100;
  const up   = diff > 0;
  const neutral = diff === 0;
  const good = neutral ? null : (invert ? !up : up);
  const color = neutral ? 'var(--text-muted)' : good ? '#16a34a' : '#dc2626';
  const bg    = neutral ? 'var(--surface-2)' : good ? 'rgba(34,197,94,0.12)' : 'rgba(239,68,68,0.12)';
  const arrow = neutral ? '→' : up ? '↗' : '↘';
  const label = `${up ? '+' : ''}${Math.abs(pct) < 10 ? pct.toFixed(1) : Math.round(pct)}%`;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 3,
      padding: '2px 8px', borderRadius: 20,
      background: bg, color, fontSize: '0.76em', fontWeight: 600,
      whiteSpace: 'nowrap',
    }}>
      {arrow} {label}
    </span>
  );
}



function mouvToCSV(rows) {
  const BOM = '﻿'; const sep = ';';
  const q = v => `"${String(v ?? '').replace(/"/g, '""')}"`;
  const headers = ['Date','Plateforme','Type','Montant (€)','Libellé','Référence','Source'];
  const data = rows.map(r => [
    r.date_operation, r.plateforme_nom||'', r.type,
    String(r.montant).replace('.',','), r.libelle||'', r.reference||'', r.source||'',
  ]);
  return BOM + [headers,...data].map(r => r.map(q).join(sep)).join('\r\n');
}

function mouvToXLS(rows) {
  const data = rows.map(r => ({
    'Date':           r.date_operation,
    'Plateforme':     r.plateforme_nom || '',
    'Détenteur':      r.plateforme_detenteur_nom || '',
    'Type':           r.type === 'depot' ? 'Dépôt' : 'Retrait',
    'Montant (€)':    r.montant,
  }));
  const ws = XLSX.utils.json_to_sheet(data);
  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, 'Mouvements');
  return XLSX.write(wb, { type: 'array', bookType: 'xlsx' });
}
function platToCSV(rows) {
  const totalSolde = rows.reduce((s, p) => s + p.solde_net, 0);
  const BOM = '﻿'; const sep = ';';
  const q = v => `"${String(v ?? '').replace(/"/g, '""')}"`;
  const headers = ['Plateforme','Dépôts (€)','Retraits (€)','Diff. Dépôts vs Retraits (€)','Solde porte-monnaie (€)','Poids (%)'];
  const data = rows.map(p => {
    const poids = totalSolde !== 0 ? ((p.solde_net / totalSolde) * 100).toFixed(2) : '0.00';
    return [
      p.nom,
      String(p.depots).replace('.',','), String(p.retraits).replace('.',','),
      String(p.solde_net).replace('.',','),
      String(p.solde_portefeuille ?? 0).replace('.',','),
      poids.replace('.',','),
    ];
  });
  return BOM + [headers, ...data].map(r => r.map(q).join(sep)).join('\r\n');
}

function platToXLS(rows) {
  const data = rows.map(p => ({
    'Plateforme':     p.nom,
    'Dépôts (€)':     p.depots,
    'Retraits (€)':   p.retraits,
    'Net (€)':        p.net,
  }));
  const ws = XLSX.utils.json_to_sheet(data);
  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, 'Plateformes');
  return XLSX.write(wb, { type: 'array', bookType: 'xlsx' });
}
function mouvToJSON(rows) {
  const data = rows.map(r => ({
    date_operation: r.date_operation,
    plateforme_nom: r.plateforme_nom || '',
    type:           r.type,
    montant:        r.montant,
    libelle:        r.libelle || null,
    reference:      r.reference || null,
    source:         r.source || null,
  }));
  return JSON.stringify(data, null, 2);
}

function platToJSON(rows) {
  const totalSolde = rows.reduce((s, p) => s + p.solde_net, 0);
  const data = rows.map(p => ({
    plateforme:          p.nom,
    depots:              p.depots,
    retraits:            p.retraits,
    solde_net:           p.solde_net,
    solde_portefeuille:  p.solde_portefeuille ?? 0,
    poids_pct:           totalSolde !== 0 ? +((p.solde_net / totalSolde) * 100).toFixed(2) : 0,
  }));
  return JSON.stringify(data, null, 2);
}

function ExportDropdown({ disabled, onCSV, onXLS, onJSON }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    if (!open) return;
    const h = e => { if (!ref.current?.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  const choose = fn => { setOpen(false); fn(); };
  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button type="button" className="icon-btn" disabled={disabled}
        onClick={() => setOpen(o => !o)} title="Exporter">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
        </svg>
      </button>
      {open && (
        <div className="export-dropdown" role="menu">
          <button role="menuitem" onClick={() => choose(onCSV)}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
              <line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="16" y2="17"/>
            </svg>
            <span><strong>Format CSV</strong><small>Compatible Excel, LibreOffice</small></span>
          </button>
          <button role="menuitem" onClick={() => choose(onXLS)}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="M9 13l2 2 4-4"/>
            </svg>
            <span><strong>Format Excel</strong><small>Fichier .xlsx Microsoft Excel</small></span>
          </button>
          <button role="menuitem" onClick={() => choose(onJSON)}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
              <path d="M8 13h1.5a1 1 0 0 1 1 1v1a1 1 0 0 0 1 1 1 1 0 0 0-1 1v1a1 1 0 0 1-1 1H8"/>
              <path d="M16 13h-1.5a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1H16"/>
            </svg>
            <span><strong>Format JSON</strong><small>Réimportable, structuré</small></span>
          </button>
        </div>
      )}
    </div>
  );
}

/* ── Panneau de détail d'un mouvement ────────────────────────── */
function DetailPanel({ row, onEdit, onDeleteCorrection }) {
  if (!row) return (
    <div className="dr-detail dr-detail-empty">
      <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ opacity: 0.25, marginBottom: 8 }}>
        <circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
      </svg>
      <span>Sélectionnez un mouvement</span>
    </div>
  );

  const isCorrection = row.type === 'correction';
  const isAuto       = row.source === 'auto_remboursement';

  const typeLabel = isCorrection ? 'Correction de solde'
    : row.type === 'depot' ? 'Dépôt' : 'Retrait';

  const editLabel = isCorrection ? 'Modifier la correction de solde'
    : row.type === 'depot' ? 'Modifier le dépôt' : 'Modifier le retrait';

  const isPos = row.montant >= 0;
  const sign  = isPos ? '+' : '−';
  const montantFmt = `${sign} ${fmtEUR(Math.abs(row.montant))}`;
  const montantColor = undefined;

  const fields = [
    { label: 'Plateforme',        value: row.plateforme_nom },
    { label: 'Type de mouvement', value: typeLabel },
    { label: 'Montant',           value: montantFmt, color: montantColor },
    { label: 'Date',              value: fmtDate(row.date_operation) },
    !isCorrection && row.libelle   && { label: 'Libellé',   value: row.libelle },
    !isCorrection && row.reference && { label: 'Référence', value: row.reference },
    row.notes && { label: 'Notes', value: row.notes },
  ].filter(Boolean);

  return (
    <div className="dr-detail">
      <div className="dr-detail-title">Détails du mouvement</div>

      {isAuto && (
        <div style={{
          display: 'flex', alignItems: 'flex-start', gap: 8,
          margin: '5px 5px 12px',
          padding: '9px 12px', borderRadius: 6,
          background: 'color-mix(in srgb, var(--primary) 10%, transparent)',
          border: '1px solid color-mix(in srgb, var(--primary) 25%, transparent)',
          fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
          lineHeight: 1.4,
        }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
            strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
            style={{ marginTop: 1, flexShrink: 0, color: 'var(--primary)' }}>
            <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
          </svg>
          <span>
            Retrait automatique — généré lors d'un remboursement sur compte courant.
            Modifiable via la page Remboursements ou depuis la fiche détail de l'investissement.
          </span>
        </div>
      )}

      <div className="dr-detail-fields">
        {fields.map(f => (
          <div className="dr-detail-field" key={f.label}>
            <span className="dr-detail-label">{f.label}</span>
            <span className="dr-detail-value" style={f.color ? { color: f.color, fontWeight: 600 } : undefined}>{f.value}</span>
          </div>
        ))}
      </div>

      <div className="dr-detail-footer">
        {isAuto ? null : isCorrection ? (
          <button className="dr-detail-edit-btn" onClick={() => onDeleteCorrection(row._correctionId)}>
            Supprimer la correction
          </button>
        ) : (
          <button className="dr-detail-edit-btn" onClick={() => onEdit(row)}>
            {editLabel}
          </button>
        )}
      </div>
    </div>
  );
}

/* ── Constantes ──────────────────────────────────────────────── */
const MOIS_FR = ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'];

const empty = { investisseur_id: '', plateforme_id: '', date_operation: today(), type: 'depot', montant: '', libelle: '', reference: '', notes: '' };

/** Modal de correction de solde porte-monnaie (micro-écarts flat_tax) */
function CorrectionModal({ open, onClose, onConfirm,
  plateforme_nom, type_operation, computedBalance, date }) {
  const isRetrait = type_operation === 'retrait';
  // Défaut : 0 pour un retrait (vider le PM), solde calculé pour un dépôt
  const [declared, setDeclared] = useState('');

  useEffect(() => {
    if (open) setDeclared(isRetrait ? '0' : String(Math.round(computedBalance * 100) / 100));
  }, [open, isRetrait, computedBalance]);

  const diff = Math.round((Number(declared) - computedBalance) * 100) / 100;
  const hasDiff = Math.abs(diff) >= 0.005;

  const handleConfirm = () => {
    onConfirm(hasDiff ? diff : 0);
  };

  if (!open) return null;
  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 1000,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      background: 'rgba(0,0,0,0.4)',
    }}>
      <div style={{
        background: 'var(--surface)', borderRadius: 10,
        padding: '24px 28px', width: 420, maxWidth: '90vw',
        boxShadow: '0 8px 32px rgba(0,0,0,0.18)',
        border: '1px solid var(--border)',
      }}>
        <div style={{ marginBottom: 6, fontWeight: 700, fontSize: 'var(--fs-base)' }}>
          Vérification du solde porte-monnaie
        </div>
        <div style={{
          fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
          marginBottom: 18, lineHeight: 1.5,
        }}>
          Après ce {isRetrait ? 'retrait' : 'dépôt'} sur <strong>{plateforme_nom}</strong>,
          quel est le solde indiqué sur la plateforme ?
          {' '}Si différent du solde calculé, une correction sera créée automatiquement.
        </div>

        <div style={{ marginBottom: 12 }}>
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'center',
            padding: '8px 12px', borderRadius: 6, marginBottom: 6,
            background: 'var(--surface-2)', fontSize: 'var(--fs-sm)',
          }}>
            <span style={{ color: 'var(--text-muted)' }}>Solde calculé</span>
            <span style={{ fontWeight: 600 }}>{Math.round(computedBalance * 100) / 100} €</span>
          </div>
          <label style={{ fontSize: 'var(--fs-sm)', fontWeight: 500, display: 'block', marginBottom: 4 }}>
            Solde constaté sur la plateforme (€)
          </label>
          <input
            type="number" step="0.01"
            value={declared}
            onChange={e => setDeclared(e.target.value)}
            style={{ width: '100%', fontSize: 'var(--fs-base)' }}
            autoFocus
          />
        </div>

        {hasDiff && (
          <div style={{
            padding: '8px 12px', borderRadius: 6, marginBottom: 12,
            background: diff > 0
              ? 'color-mix(in srgb, var(--success) 12%, transparent)'
              : 'color-mix(in srgb, var(--danger) 12%, transparent)',
            border: `1px solid ${diff > 0 ? 'color-mix(in srgb, var(--success) 30%, transparent)' : 'color-mix(in srgb, var(--danger) 30%, transparent)'}`,
            fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
          }}>
            Une correction de <strong style={{ color: diff > 0 ? 'var(--success)' : 'var(--danger)' }}>
              {diff > 0 ? '+' : ''}{diff} €
            </strong> sera créée.
          </div>
        )}
        {!hasDiff && declared !== '' && (
          <div style={{
            padding: '8px 12px', borderRadius: 6, marginBottom: 12,
            background: 'color-mix(in srgb, var(--primary) 10%, transparent)',
            fontSize: 'var(--fs-sm)', color: 'var(--text-muted)',
          }}>
            Solde identique au calcul — aucune correction nécessaire.
          </div>
        )}

        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 8 }}>
          <button onClick={onClose}>Passer</button>
          <button className="primary" onClick={handleConfirm}>
            {hasDiff ? 'Créer la correction' : 'OK'}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ── Composant principal ─────────────────────────────────────── */
export default function DepotsRetraits() {
  const { activeId, activeView, investisseurs } = useInvestisseur();
  const { search } = useLocation();
  const navigate   = useNavigate();
  const [activeTab,   setActiveTab]   = useState('plateformes');
  const [listFocused, setListFocused] = useState(false);
  const [allRows,     setAllRows]     = useState([]);
  const [plats,       setPlats]       = useState([]);
  const [dashData,    setDashData]    = useState(null);
  const [filter,      setFilter]      = useState({ type: '', plateforme_ids: [], year: '', month: '' });
  const [modalOpen,   setModalOpen]   = useState(false);
  const [editingId,   setEditingId]   = useState(null);
  const [form,        setForm]        = useState(empty);
  const [err,         setErr]         = useState(null);
  const [loading,     setLoading]     = useState(false);
  const [selectedRow, setSelectedRow] = useState(null);
  const [drPlatYear,  setDrPlatYear]  = useState(String(new Date().getFullYear()));
  const [allRemb,     setAllRemb]     = useState([]);
  const [allInv,      setAllInv]      = useState([]);

  const [allCorrections, setAllCorrections] = useState([]);
  const [deleteConfirm, setDeleteConfirm] = useState(null);

  /* Lecture des params de navigation depuis la page Plateformes */
  useEffect(() => {
    const p = new URLSearchParams(search);
    if (p.get('tab') !== 'mouvements') return;
    setActiveTab('mouvements');
    const newFilter = { type: '', plateforme_ids: [], year: '', month: '' };
    if (p.get('type'))      newFilter.type         = p.get('type');
    if (p.get('year'))      newFilter.year         = p.get('year');
    if (p.get('month'))     newFilter.month        = p.get('month');
    const platIds = p.get('plat_ids');
    if (platIds)            newFilter.plateforme_ids = platIds.split(',').filter(Boolean);
    setFilter(newFilter);
    navigate('/depots-retraits', { replace: true });
  }, []); // eslint-disable-line
  const [openMenu, setOpenMenu] = useState(null); // { row, x, y }
  const [platFilterOpen, setPlatFilterOpen] = useState(false);
  const platFilterRef = useRef(null);
  useEffect(() => {
    if (!platFilterOpen) return;
    const h = e => { if (!platFilterRef.current?.contains(e.target)) setPlatFilterOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [platFilterOpen]);

  // État du modal de correction solde
  const [corrModal, setCorrModal] = useState({
    open: false, plateforme_nom: '', type_operation: 'retrait',
    computedBalance: 0, date: '', platId: null, investisseurId: null,
  });

  const load = async () => {
    if (!activeId && activeView !== 'all') return;
    setLoading(true);
    setAllRows([]);
    setDashData(null);
    const scopeParams = activeView === 'all' ? { scope: 'all' } : undefined;
    try {
      const [r, p, d, remb, inv, corr] = await Promise.all([
        api.get('/depots-retraits', scopeParams),
        api.get('/plateformes'),
        api.get('/dashboard', scopeParams),
        api.get('/remboursements', scopeParams),
        api.get('/investissements', scopeParams),
        api.get('/corrections', scopeParams),
      ]);
      setAllRows(r); setPlats(p); setDashData(d); setAllRemb(remb); setAllInv(inv); setAllCorrections(corr);
    } catch (e) {
      console.error('DepotsRetraits load error:', e);
    } finally { setLoading(false); }
  };

  useEffect(() => { load(); /* eslint-disable-next-line */ }, [activeId, activeView]);

  // Ferme tous les menus contextuels au scroll
  useEffect(() => {
    const closeAll = () => {
      setOpenMenu(null);
    };
    window.addEventListener('scroll', closeAll, true);
    return () => window.removeEventListener('scroll', closeAll, true);
  }, []);

  /* Filtrage */
  // kpiRows : plateforme + année sélectionnée (sans filtre type — pour que les KPIs
  // affichent toujours dépôts ET retraits même quand un type est actif)
  const kpiRows = allRows.filter(r => {
    if (filter.plateforme_ids.length > 0 && !filter.plateforme_ids.includes(String(r.plateforme_id))) return false;
    if (drPlatYear && r.date_operation.slice(0, 4) !== drPlatYear)                       return false;
    return true;
  });

  // chartRows : kpiRows + filtre type (pour les graphiques)
  const chartRows = kpiRows.filter(r => {
    if (filter.type && r.type !== filter.type) return false;
    return true;
  });

  // rows : pour le tableau Mouvements (tous les filtres y compris mois)
  const rows = chartRows.filter(r => {
    const [y, m] = r.date_operation.split('-');
    if (filter.year  && y !== filter.year)                   return false;
    if (filter.month && m !== filter.month.padStart(2,'0'))  return false;
    return true;
  });

  // Corrections normalisées pour affichage dans le tableau mouvements
  const normalizedCorrections = useMemo(() => allCorrections.map(c => ({
    id: `corr_${c.id}`,
    _correctionId: c.id,
    plateforme_id: c.plateforme_id,
    plateforme_nom: c.plateforme_nom,
    plateforme_detenteur_nom: c.investisseur_nom,
    date_operation: c.date,
    type: 'correction',
    montant: c.montant,
    libelle: c.notes || '',
    notes: c.notes || '',
    source: 'correction',
    investisseur_id: c.investisseur_id,
  })), [allCorrections]);

  // Corrections filtrées (plateforme + année + mois, sans filtre type)
  const filteredCorrections = useMemo(() => normalizedCorrections.filter(c => {
    if (filter.plateforme_ids.length > 0 && !filter.plateforme_ids.includes(String(c.plateforme_id))) return false;
    const [y, m] = c.date_operation.split('-');
    if (filter.year  && y !== filter.year)                  return false;
    if (filter.month && m !== filter.month.padStart(2,'0')) return false;
    return true;
  }), [normalizedCorrections, filter.plateforme_ids, filter.year, filter.month]);

  // tableRows : dépôts/retraits + corrections, triés par date desc
  const tableRows = useMemo(() => {
    const drPart   = filter.type === 'correction' ? [] : rows;
    const corrPart = (filter.type === 'depot' || filter.type === 'retrait') ? [] : filteredCorrections;
    return [...drPart, ...corrPart].sort((a, b) =>
      b.date_operation.localeCompare(a.date_operation) || String(b.id).localeCompare(String(a.id))
    );
  }, [rows, filteredCorrections, filter.type]);

  /* ── Pagination mouvements ── */
  const {
    pagedItems: pagedTableRows, page: drPage, setPage: setDrPage,
    pageSize: drPageSize, setPageSize: setDrPageSize,
    totalPages: drTotalPages, totalItems: drTotalItems, PAGE_SIZES,
  } = usePagination(tableRows, 'cl_pagesize_dr', [filter]);

  // Auto-sélectionne le premier mouvement quand la liste change dans l'onglet mouvements
  // (déclaré après `tableRows` pour éviter la temporal dead zone)
  useEffect(() => {
    if (activeTab === 'mouvements') {
      setSelectedRow(prev => {
        if (prev && tableRows.find(r => String(r.id) === String(prev.id))) return prev;
        return tableRows[0] || null;
      });
    }
    // eslint-disable-next-line
  }, [tableRows, activeTab]);
  const years = [...new Set(allRows.map(r => r.date_operation.slice(0,4)))].sort().reverse();

  /* Données tableau Plateformes — filtrées par drPlatYear si actif */
  const drPlatData = useMemo(() => {
    if (!drPlatYear) return dashData?.cashByPlatform ?? [];

    // Mouvements de l'année sélectionnée (dépôts/retraits de l'année)
    const src = allRows.filter(r => r.date_operation.slice(0, 4) === drPlatYear);
    const map = {};
    for (const r of src) {
      const key = String(r.plateforme_id);
      if (!map[key]) map[key] = { plateforme_id: r.plateforme_id, nom: r.plateforme_nom, detenteur_nom: r.plateforme_detenteur_nom || null, depots: 0, retraits: 0, solde_net: 0, solde_portefeuille: 0 };
      if (r.type === 'depot') map[key].depots += r.montant;
      else map[key].retraits += r.montant;
    }

    // Solde porte-monnaie = cumul jusqu'au 31/12 de l'année sélectionnée
    // = dépôts/retraits + remboursements crédités au portefeuille
    // Règle fiscale : flat_tax → net_recu (PFU prélevé à la source) ;
    //                 hors France → capital + cashback + interets_bruts (pas de PFU à la source)
    const yearEnd = `${drPlatYear}-12-31`;
    const cumul = {};

    // 1. Dépôts et retraits manuels (hors auto-retraits — générés par remboursements
    //    sur compte courant, ils ne représentent pas un mouvement du porte-monnaie)
    for (const r of allRows) {
      if (r.date_operation > yearEnd) continue;
      if (r.source === 'auto_remboursement') continue;
      const key = String(r.plateforme_id);
      cumul[key] = (cumul[key] ?? 0) + (r.type === 'depot' ? r.montant : -r.montant);
    }

    // 2. Remboursements crédités au portefeuille
    //    Les remboursements vers compte_courant ne crédient pas le porte-monnaie.
    //    Pour flat_tax : net_recu (PFU déjà prélevé à la source).
    //    Pour hors France : capital + cashback + interets_bruts (pas de PFU à la source ;
    //    interets_bruts est déjà net de la retenue locale si applicable).
    for (const r of allRemb) {
      if (!r.date_remb || r.date_remb > yearEnd) continue;
      if (r.methode_remboursement !== 'portefeuille') continue;
      const key = String(r.plateforme_id);
      const plat = plats.find(p => p.id === r.plateforme_id);
      const credit = plat?.fiscalite === 'flat_tax'
        ? (r.net_recu ?? 0)
        : (r.capital ?? 0) + (r.cashback ?? 0) + (r.interets_bruts ?? 0);
      cumul[key] = (cumul[key] ?? 0) + credit;
    }

    // 3. Déduire le capital investi (capital_total = montant_investi + réinvestissements)
    //    Le capital revient progressivement via net_recu (étape 2) au fil des remboursements
    for (const inv of allInv) {
      if (!inv.date_souscription || inv.date_souscription > yearEnd) continue;
      const key = String(inv.plateforme_id);
      cumul[key] = (cumul[key] ?? 0) - (inv.capital_total ?? inv.montant_investi);
    }

    return Object.values(map).map(p => {
      p.solde_net = p.depots - p.retraits;
      p.solde_portefeuille = cumul[String(p.plateforme_id)] ?? 0;
      return p;
    }).sort((a, b) => b.solde_net - a.solde_net);
  }, [allRows, allRemb, allInv, dashData, drPlatYear]);

  const drPlatTotals = useMemo(() =>
    drPlatData.reduce((acc, p) => {
      acc.depots             += p.depots;
      acc.retraits           += p.retraits;
      acc.solde_net          += p.solde_net;
      acc.solde_portefeuille += p.solde_portefeuille ?? 0;
      return acc;
    }, { depots: 0, retraits: 0, solde_net: 0, solde_portefeuille: 0 }),
    [drPlatData]
  );

  // Totaux KPI depuis kpiRows (réagit à plateforme + année)
  const totals = kpiRows.reduce((acc, r) => {
    if (r.type === 'depot') acc.depots += r.montant; else acc.retraits += r.montant;
    return acc;
  }, { depots: 0, retraits: 0 });

  // Solde porte-monnaie pour le KPI :
  // - sans filtre année → live depuis le backend (inclut déjà les remboursements au portefeuille)
  // - avec filtre année → cumul jusqu'au 31/12 de l'année (dépôts/retraits + remboursements au portefeuille)
  const kpiSoldePortefeuille = (() => {
    if (!drPlatYear) {
      if (filter.plateforme_ids.length === 1 && dashData) {
        const p = dashData.cashByPlatform?.find(c => c.plateforme_id === Number(filter.plateforme_ids[0]));
        return p?.solde_portefeuille ?? null;
      }
      return dashData?.solde_portefeuille_total ?? null;
    }
    const yearEnd = `${drPlatYear}-12-31`;
    let balance = 0;
    for (const r of allRows) {
      if (r.date_operation > yearEnd) continue;
      if (r.source === 'auto_remboursement') continue;
      if (filter.plateforme_ids.length > 0 && !filter.plateforme_ids.includes(String(r.plateforme_id))) continue;
      balance += r.type === 'depot' ? r.montant : -r.montant;
    }
    for (const r of allRemb) {
      if (!r.date_remb || r.date_remb > yearEnd) continue;
      if (r.methode_remboursement !== 'portefeuille') continue;
      if (filter.plateforme_ids.length > 0 && !filter.plateforme_ids.includes(String(r.plateforme_id))) continue;
      balance += r.net_recu ?? 0;
    }
    for (const inv of allInv) {
      if (!inv.date_souscription || inv.date_souscription > yearEnd) continue;
      if (filter.plateforme_ids.length > 0 && !filter.plateforme_ids.includes(String(inv.plateforme_id))) continue;
      balance -= inv.capital_total ?? inv.montant_investi;
    }
    return balance;
  })();

  // Totaux KPI et solde porte-monnaie pour l'année N-1 (TrendBadge)
  const prevYear = String(Number(drPlatYear || new Date().getFullYear()) - 1);
  const prevTotals = (() => {
    const src = allRows.filter(r => r.date_operation.slice(0, 4) === prevYear);
    return src.reduce((acc, r) => {
      if (r.type === 'depot') acc.depots += r.montant; else acc.retraits += r.montant;
      return acc;
    }, { depots: 0, retraits: 0 });
  })();

  const prevKpiSoldePortefeuille = (() => {
    const yearEnd = `${prevYear}-12-31`;
    let balance = 0;
    for (const r of allRows) {
      if (r.date_operation > yearEnd) continue;
      if (r.source === 'auto_remboursement') continue;
      if (filter.plateforme_ids.length > 0 && !filter.plateforme_ids.includes(String(r.plateforme_id))) continue;
      balance += r.type === 'depot' ? r.montant : -r.montant;
    }
    for (const r of allRemb) {
      if (!r.date_remb || r.date_remb > yearEnd) continue;
      if (r.methode_remboursement !== 'portefeuille') continue;
      if (filter.plateforme_ids.length > 0 && !filter.plateforme_ids.includes(String(r.plateforme_id))) continue;
      balance += r.net_recu ?? 0;
    }
    for (const inv of allInv) {
      if (!inv.date_souscription || inv.date_souscription > yearEnd) continue;
      if (filter.plateforme_ids.length > 0 && !filter.plateforme_ids.includes(String(inv.plateforme_id))) continue;
      balance -= inv.capital_total ?? inv.montant_investi;
    }
    return balance;
  })();

  const multiDetenteur = new Set(plats.map(p => p.investisseur_id)).size > 1;

  /** Investisseur par défaut selon la vue active */
  const defaultInvestisseurId = () => {
    if (activeView === 'all') {
      const principal = investisseurs.find(i => i.is_principal);
      return String((principal || investisseurs[0])?.id || '');
    }
    return String(activeId || '');
  };

  /** Retourne l'investisseur_id de la plateforme, ou le défaut si absent */
  const investisseurForPlat = (platId) => {
    const plat = plats.find(p => String(p.id) === String(platId));
    return plat?.investisseur_id ? String(plat.investisseur_id) : defaultInvestisseurId();
  };

  /* Ouverture auto depuis le bouton global "Nouveau dépôt/retrait" */
  const pendingNew = useRef(false);
  useEffect(() => {
    if (new URLSearchParams(search).get('new') !== '1') return;
    navigate('/depots-retraits', { replace: true });
    if (plats.length > 0) {
      setEditingId(null);
      const firstId = plats[0]?.id || '';
      setForm({ ...empty, plateforme_id: firstId, investisseur_id: investisseurForPlat(firstId) });
      setErr(null);
      setModalOpen(true);
    } else {
      pendingNew.current = true;
    }
  }, [search]); // eslint-disable-line

  useEffect(() => {
    if (!pendingNew.current || !plats.length) return;
    pendingNew.current = false;
    setEditingId(null);
    const firstId = plats[0]?.id || '';
    setForm({ ...empty, plateforme_id: firstId, investisseur_id: investisseurForPlat(firstId) });
    setErr(null);
    setModalOpen(true);
  }, [plats]); // eslint-disable-line

  /* CRUD */
  const openNew = () => {
    setEditingId(null);
    const firstId = plats[0]?.id || '';
    setForm({ ...empty, plateforme_id: firstId, investisseur_id: investisseurForPlat(firstId) });
    setErr(null); setModalOpen(true);
  };

  const openEdit = (row) => {
    if (row.source === 'auto_remboursement') return; // retrait auto — non éditable ici
    setEditingId(row.id);
    setForm({
      investisseur_id: String(row.investisseur_id || ''),
      plateforme_id: row.plateforme_id,
      date_operation: row.date_operation,
      type: row.type,
      montant: row.montant,
      libelle: row.libelle || '',
      reference: row.reference || '',
      notes: row.notes || '',
    });
    setErr(null); setModalOpen(true);
  };

  const openCorrection = async (row) => {
    // row = objet ligne existante, ou null pour utiliser les données du formulaire ouvert
    const platId   = row ? row.plateforme_id  : form.plateforme_id;
    const investId = row ? (row.investisseur_id || null) : (Number(form.investisseur_id) || null);
    const type     = row ? row.type            : form.type;
    const date     = row ? row.date_operation  : form.date_operation;
    const plat     = plats.find(p => String(p.id) === String(platId));

    // Solde calculé par le backend à la date exacte du mouvement
    let computedBalance = 0;
    try {
      const result = await api.get('/dashboard/solde-historique', { plateforme_id: platId, date });
      computedBalance = result.solde ?? 0;
    } catch (e) {
      console.error('solde-historique error:', e);
    }

    // Ferme le modal d'édition sans sauvegarder
    setModalOpen(false);
    setEditingId(null);
    setCorrModal({
      open: true,
      plateforme_nom:  plat?.nom || '',
      type_operation:  type,
      computedBalance,
      date,
      platId:          Number(platId),
      investisseurId:  investId ? Number(investId) : null,
    });
  };

  const close = () => { setModalOpen(false); setEditingId(null); setForm(empty); setErr(null); };

  const submit = async (e) => {
    e?.preventDefault?.(); setErr(null);
    try {
      const payload = {
        investisseur_id: Number(form.investisseur_id) || undefined,
        plateforme_id: Number(form.plateforme_id),
        date_operation: form.date_operation,
        type: form.type,
        montant: Number(form.montant),
        libelle: form.libelle || undefined,
        reference: form.reference || undefined,
        notes: form.notes || undefined,
      };
      if (editingId) {
        await api.put(`/depots-retraits/${editingId}`, payload);
        close(); await load();
        return;
      }

      // Nouvelle opération
      await api.post('/depots-retraits', payload);
      close();

      // Vérifier si la plateforme est flat_tax → proposer correction de solde
      const plat = plats.find(p => String(p.id) === String(form.plateforme_id));
      if (plat?.fiscalite === 'flat_tax') {
        // Solde attendu = solde actuel ± montant de l'opération (avant reload)
        const currentPlatData = dashData?.cashByPlatform?.find(
          p => String(p.plateforme_id) === String(form.plateforme_id)
        );
        const currentBalance = currentPlatData?.solde_portefeuille ?? 0;
        const montant = Number(form.montant);
        const expectedBalance = Math.round(
          (form.type === 'retrait' ? currentBalance - montant : currentBalance + montant) * 100
        ) / 100;

        setCorrModal({
          open: true,
          plateforme_nom: plat.nom,
          type_operation: form.type,
          computedBalance: expectedBalance,
          date: form.date_operation,
          platId: Number(form.plateforme_id),
          investisseurId: Number(form.investisseur_id) || null,
        });
      } else {
        await load();
      }
    } catch (e) { setErr(e.message); }
  };

  const handleCorrectionConfirm = async (diff) => {
    if (Math.abs(diff) >= 0.005) {
      try {
        await api.post('/corrections', {
          investisseur_id: corrModal.investisseurId,
          plateforme_id:   corrModal.platId,
          date:            corrModal.date,
          montant:         diff,
          notes:           `Correction solde porte-monnaie — ${corrModal.type_operation === 'retrait' ? 'retrait' : 'dépôt'}`,
        });
      } catch (e) {
        console.error('Correction creation failed:', e);
      }
    }
    setCorrModal(m => ({ ...m, open: false }));
    await load();
  };

  const openRowMenu = (e, row) => {
    e.stopPropagation();
    const rect = e.currentTarget.getBoundingClientRect();
    setOpenMenu({ row, x: rect.right, y: rect.bottom });
  };

  const deleteRow = (rowId) => {
    setDeleteConfirm({
      message: 'Supprimer définitivement ce mouvement ?',
      onConfirm: async () => {
        await api.del(`/depots-retraits/${rowId}`);
        setDeleteConfirm(null);
        setSelectedRow(null);
        await load();
      },
    });
  };

  const deleteCorrection = (corrId) => {
    setDeleteConfirm({
      message: 'Supprimer définitivement cette correction de solde ?',
      onConfirm: async () => {
        await api.del(`/corrections/${corrId}`);
        setDeleteConfirm(null);
        setSelectedRow(null);
        await load();
      },
    });
  };

  const onDelete = () => {
    if (!editingId) return;
    setDeleteConfirm({
      message: 'Supprimer définitivement ce mouvement ?',
      onConfirm: async () => {
        await api.del(`/depots-retraits/${editingId}`);
        setDeleteConfirm(null);
        close(); setSelectedRow(null); await load();
      },
    });
  };

  if (!loading && plats.length === 0) return (
    <>
      <div className="topbar"><h2>Dépôts / Retraits</h2></div>
      <EmptyState />
    </>
  );
  if (!loading && !modalOpen && !corrModal.open && allRows.length === 0) return (
    <>
      <div className="topbar"><h2>Dépôts / Retraits</h2></div>
      <EmptyState
        icon="💶"
        title="Aucun dépôt enregistré"
        message="Vos plateformes sont configurées, mais aucun dépôt n'a encore été enregistré. Ajoutez un dépôt pour commencer à suivre votre solde."
        cta="Ajouter un dépôt"
        to="/depots-retraits?new=1"
      />
    </>
  );
  return (
    <>
      <div className="topbar">
        <h2><PageIcon name="depots-retraits" />Dépôts / Retraits</h2>
        {(filter.type || filter.plateforme_ids.length > 0 || filter.year || filter.month) && (
          <button className="dr-clear-btn"
            onClick={() => setFilter({ type: '', plateforme_ids: [], year: '', month: '' })}>
            ✕ Effacer les filtres
          </button>
        )}
      </div>

      {/* ── Graphiques ── */}
      {!listFocused && <div className="charts-row">
        <SoldeChart rows={chartRows} />
        <DistributionChart rows={chartRows} />
      </div>}

      {/* ── KPIs ── */}
      {!listFocused && <div className="dr-kpi-row">
        <div
          className={`kpi dr-kpi-clickable${filter.type === 'depot' ? ' dr-kpi-active dr-kpi-active-success' : ''}`}
          onClick={() => setFilter(f => ({ ...f, type: f.type === 'depot' ? '' : 'depot' }))}
          title="Filtrer sur les dépôts"
        >
          <div className="label">Dépôts</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, margin: '4px 0 0' }}>
            <span style={{ fontSize: '1.35rem', fontWeight: 700 }}>{fmtEUR(totals.depots)}</span>
            <TrendBadge current={totals.depots} prev={prevTotals.depots} />
          </div>
          <div style={{ fontSize: '0.8em', color: 'var(--text-muted)', marginTop: 5 }}>{fmtEUR(prevTotals.depots)} en {prevYear}</div>
        </div>
        <div
          className={`kpi dr-kpi-clickable${filter.type === 'retrait' ? ' dr-kpi-active dr-kpi-active-danger' : ''}`}
          onClick={() => setFilter(f => ({ ...f, type: f.type === 'retrait' ? '' : 'retrait' }))}
          title="Filtrer sur les retraits"
        >
          <div className="label">Retraits</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, margin: '4px 0 0' }}>
            <span style={{ fontSize: '1.35rem', fontWeight: 700 }}>{fmtEUR(totals.retraits)}</span>
            <TrendBadge current={totals.retraits} prev={prevTotals.retraits} invert={true} />
          </div>
          <div style={{ fontSize: '0.8em', color: 'var(--text-muted)', marginTop: 5 }}>{fmtEUR(prevTotals.retraits)} en {prevYear}</div>
        </div>
        <div
          className={`kpi${(filter.type || filter.plateforme_ids.length > 0 || filter.year || filter.month) ? ' dr-kpi-clickable dr-kpi-active dr-kpi-active-neutral' : ''}`}
          onClick={() => setFilter({ type: '', plateforme_ids: [], year: '', month: '' })}
          title={(filter.type || filter.plateforme_ids.length > 0 || filter.year || filter.month) ? 'Effacer les filtres' : undefined}
          style={(filter.type || filter.plateforme_ids.length > 0 || filter.year || filter.month) ? { cursor: 'pointer' } : {}}
        >
          <div className="label">Diff. Dépôts vs Retraits</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, margin: '4px 0 0' }}>
            <span style={{ fontSize: '1.35rem', fontWeight: 700 }}>{fmtEUR(totals.depots - totals.retraits)}</span>
            <TrendBadge current={totals.depots - totals.retraits} prev={prevTotals.depots - prevTotals.retraits} />
          </div>
          <div style={{ fontSize: '0.8em', color: 'var(--text-muted)', marginTop: 5 }}>{fmtEUR(prevTotals.depots - prevTotals.retraits)} en {prevYear}</div>
        </div>
        <div className="kpi" title="Cash disponible sur les porte-monnaie des plateformes (hors compte courant)">
          <div className="label">Porte-monnaie</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, margin: '4px 0 0' }}>
            <span style={{ fontSize: '1.35rem', fontWeight: 700 }}>{kpiSoldePortefeuille !== null ? fmtEUR(kpiSoldePortefeuille) : '…'}</span>
            {kpiSoldePortefeuille !== null && <TrendBadge current={kpiSoldePortefeuille} prev={prevKpiSoldePortefeuille} />}
          </div>
          <div style={{ fontSize: '0.8em', color: 'var(--text-muted)', marginTop: 5 }}>{fmtEUR(prevKpiSoldePortefeuille)} en {prevYear}</div>
        </div>
      </div>}

      {/* ── Onglets ── */}
      {!listFocused && <div className="dr-tabs">
        <button className={`dr-tab${activeTab === 'plateformes' ? ' active' : ''}`}
          onClick={() => setActiveTab('plateformes')}>Plateformes</button>
        <button className={`dr-tab${activeTab === 'vision-mensuelle' ? ' active' : ''}`}
          onClick={() => setActiveTab('vision-mensuelle')}>Vision mensuelle</button>
        <button className={`dr-tab${activeTab === 'mouvements'  ? ' active' : ''}`}
          onClick={() => { setActiveTab('mouvements'); setSelectedRow(r => r ?? (rows[0] || null)); }}>Mouvements</button>
      </div>}

      {/* ====== ONGLET PLATEFORMES ====== */}
      {activeTab === 'plateformes' && (
        <div style={{ padding: '0 24px' }}>
        <div className="card" style={{ marginBottom: 16 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
            <h3 style={{ margin: 0 }}>
              Répartition par plateforme
              {drPlatYear && <span style={{ marginLeft: 8 }}>pour l'année {drPlatYear}</span>}
            </h3>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <select value={drPlatYear} onChange={e => setDrPlatYear(e.target.value)}
                style={{
                  fontSize: 'var(--fs-xs)', padding: '4px 8px', height: 30,
                  borderRadius: 6, border: '1px solid var(--border)',
                  background: 'var(--surface-2)', color: 'var(--text-muted)',
                  cursor: 'pointer', outline: 'none',
                }}>
                <option value="">Toutes les années</option>
                {years.map(y => <option key={y} value={y}>{y}</option>)}
              </select>
              <button
                type="button"
                className="icon-btn"
                title={listFocused ? 'Réduire' : 'Agrandir'}
                onClick={() => { const next = !listFocused; setListFocused(next); setDrPageSize(next ? 25 : 15); }}
              >
                {listFocused ? (
                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/>
                    <line x1="10" y1="14" x2="3" y2="21"/><line x1="21" y1="3" x2="14" y2="10"/>
                  </svg>
                ) : (
                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/>
                    <line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/>
                  </svg>
                )}
              </button>
              <ExportDropdown
                disabled={drPlatData.length === 0}
                onCSV={() => dlBlob(platToCSV(drPlatData), 'plateformes.csv', 'text/csv;charset=utf-8')}
                onXLS={() => dlBlob(platToXLS(drPlatData), 'plateformes.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')}
                onJSON={() => dlBlob(platToJSON(drPlatData), 'plateformes.json', 'application/json')}
              />
            </div>
          </div>
          {!dashData ? (
            <div className="text-muted" style={{ padding: '12px 0' }}>Chargement…</div>
          ) : (
            <table>
              <thead>
                <tr><th>Plateforme</th>{multiDetenteur && <th>Détenteur</th>}<th className="num">Dépôts</th><th className="num">Retraits</th><th className="num">Diff. Dépôts vs Retraits</th><th className="num">Porte-monnaie</th><th className="num">Poids</th><th style={{ width: 28 }}></th></tr>
              </thead>
              <tbody>
                {drPlatData.length === 0 && (
                  <tr><td colSpan={multiDetenteur ? 8 : 7} className="text-muted" style={{ textAlign: 'center' }}>Aucune plateforme</td></tr>
                )}
                {(() => {
                  const totalSolde = drPlatTotals.solde_net;
                  return drPlatData.map(p => {
                    const isActive = filter.plateforme_ids.includes(String(p.plateforme_id));
                    const poids = totalSolde !== 0 ? (p.solde_net / totalSolde) * 100 : 0;
                    return (
                      <tr key={p.plateforme_id}
                        className={`dr-row${isActive ? ' dr-row-selected' : ''}`}
                        style={{ cursor: 'pointer' }}
                        onClick={() => setFilter(f => ({
                          ...f,
                          plateforme_ids: isActive
                            ? f.plateforme_ids.filter(id => id !== String(p.plateforme_id))
                            : [...f.plateforme_ids, String(p.plateforme_id)]
                        }))}>
                        <td>
                          <span style={{ fontWeight: isActive ? 600 : undefined }}>{p.nom}</span>
                        </td>
                        {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{p.detenteur_nom || '—'}</td>}
                        <td className="num">{fmtEUR(p.depots)}</td>
                        <td className="num">{fmtEUR(p.retraits)}</td>
                        <td className="num">{fmtEUR(p.solde_net)}</td>
                        <td className="num">{fmtEUR(p.solde_portefeuille ?? 0)}</td>
                        <td className="num">
                          <div style={{ display: 'flex', alignItems: 'center', gap: 6, justifyContent: 'flex-end' }}>
                            <div style={{ width: 48, height: 5, borderRadius: 3, background: 'var(--surface-2)', overflow: 'hidden' }}>
                              <div style={{ width: `${Math.max(0, poids)}%`, height: '100%', background: 'var(--primary)', borderRadius: 3 }} />
                            </div>
                            <span style={{ minWidth: 36, textAlign: 'right', fontSize: 'var(--fs-sm)' }}>
                              {poids.toFixed(1)} %
                            </span>
                          </div>
                        </td>
                        <td style={{ textAlign: 'center', color: 'var(--text-muted)', fontSize: 11 }}>
                          {isActive ? '✕' : ''}
                        </td>
                      </tr>
                    );
                  });
                })()}
              </tbody>
              {drPlatData.length > 1 && (
                <tfoot>
                  <tr style={{ borderTop: '2px solid var(--border)', fontWeight: 700, background: 'var(--surface-2)' }}>
                    <td>Total — {drPlatData.length} plateformes</td>
                    {multiDetenteur && <td />}
                    <td className="num">{fmtEUR(drPlatTotals.depots)}</td>
                    <td className="num">{fmtEUR(drPlatTotals.retraits)}</td>
                    <td className="num">{fmtEUR(drPlatTotals.solde_net)}</td>
                    <td className="num">{fmtEUR(drPlatTotals.solde_portefeuille)}</td>
                    <td className="num"><span style={{ fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>100 %</span></td>
                    <td />
                  </tr>
                </tfoot>
              )}
            </table>
          )}
        </div>
        </div>
      )}

      {/* ====== ONGLET VISION MENSUELLE ====== */}
      {activeTab === 'vision-mensuelle' && (
        <div style={{ padding: '0 24px' }}>
          <DepotsMensuelTable allRows={allRows} plats={plats} expandButton={
            <button
              type="button"
              className="icon-btn"
              title={listFocused ? 'Réduire' : 'Agrandir'}
              style={{ marginLeft: 4 }}
              onClick={() => { const next = !listFocused; setListFocused(next); setDrPageSize(next ? 25 : 15); }}
            >
              {listFocused ? (
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/>
                  <line x1="10" y1="14" x2="3" y2="21"/><line x1="21" y1="3" x2="14" y2="10"/>
                </svg>
              ) : (
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/>
                  <line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/>
                </svg>
              )}
            </button>
          } />
        </div>
      )}

      {/* ====== ONGLET MOUVEMENTS ====== */}
      {activeTab === 'mouvements' && (
        <div className="dr-mouvements-layout">

          {/* Colonne gauche 2/3 */}
          <div className="dr-mouvements-list">
            <div className="card" style={{ display: 'flex', flexDirection: 'column' }}>

              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
                <h3 style={{ margin: 0 }}>Mouvements</h3>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                  <button
                    type="button"
                    className="icon-btn"
                    title={listFocused ? 'Réduire' : 'Agrandir'}
                    onClick={() => { const next = !listFocused; setListFocused(next); setDrPageSize(next ? 25 : 15); }}
                  >
                    {listFocused ? (
                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                        <polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/>
                        <line x1="10" y1="14" x2="3" y2="21"/><line x1="21" y1="3" x2="14" y2="10"/>
                      </svg>
                    ) : (
                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                        <polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/>
                        <line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/>
                      </svg>
                    )}
                  </button>
                  <ExportDropdown
                    disabled={rows.length === 0}
                    onCSV={() => dlBlob(mouvToCSV(rows), 'mouvements.csv', 'text/csv;charset=utf-8')}
                    onXLS={() => dlBlob(mouvToXLS(rows), 'mouvements.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')}
                    onJSON={() => dlBlob(mouvToJSON(rows), 'mouvements.json', 'application/json')}
                  />
                </div>
              </div>

              {/* Filtres */}
              <div className="row" style={{ marginBottom: 12, gap: 8 }}>
                <div style={{ flex: 1, minWidth: 100 }}>
                  <label>Type de mouvement</label>
                  <select value={filter.type} onChange={e => setFilter({ ...filter, type: e.target.value })}>
                    <option value="">Tous</option>
                    <option value="depot">Dépôts</option>
                    <option value="retrait">Retraits</option>
                    <option value="correction">Corrections de solde</option>
                  </select>
                </div>
                <div style={{ flex: 2, minWidth: 160, position: 'relative' }} ref={platFilterRef}>
                  <label>Plateforme</label>
                  <button
                    type="button"
                    className="plat-multiselect-trigger"
                    onClick={() => setPlatFilterOpen(o => !o)}
                  >
                    <span>
                      {filter.plateforme_ids.length === 0
                        ? 'Toutes'
                        : filter.plateforme_ids.length === 1
                          ? plats.find(p => String(p.id) === filter.plateforme_ids[0])
                            ? (plats.find(p => String(p.id) === filter.plateforme_ids[0]).nom + (multiDetenteur && plats.find(p => String(p.id) === filter.plateforme_ids[0]).investisseur_nom ? ` — ${plats.find(p => String(p.id) === filter.plateforme_ids[0]).investisseur_nom}` : ''))
                            : '1 sélectionnée'
                          : `${filter.plateforme_ids.length} sélectionnées`}
                    </span>
                    <svg width="10" height="10" viewBox="0 0 10 6" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><polyline points="1,1 5,5 9,1"/></svg>
                  </button>
                  {platFilterOpen && (
                    <div className="plat-multiselect-dropdown">
                      <label className="plat-multiselect-item" onClick={() => setFilter(f => ({ ...f, plateforme_ids: [] }))}>
                        <input type="checkbox" readOnly checked={filter.plateforme_ids.length === 0} style={{ width: 'auto' }} />
                        <span>Toutes</span>
                      </label>
                      {plats.map(p => {
                        const sid = String(p.id);
                        const checked = filter.plateforme_ids.includes(sid);
                        return (
                          <label key={p.id} className="plat-multiselect-item"
                            onClick={() => setFilter(f => ({
                              ...f,
                              plateforme_ids: checked
                                ? f.plateforme_ids.filter(id => id !== sid)
                                : [...f.plateforme_ids, sid]
                            }))}>
                            <input type="checkbox" readOnly checked={checked} style={{ width: 'auto' }} />
                            <span>{p.nom}{multiDetenteur && p.investisseur_nom ? ` — ${p.investisseur_nom}` : ''}</span>
                          </label>
                        );
                      })}
                    </div>
                  )}
                </div>
                <div style={{ flex: 1, minWidth: 100 }}>
                  <label>Année</label>
                  <select value={filter.year} onChange={e => setFilter({ ...filter, year: e.target.value })}>
                    <option value="">Toutes</option>
                    {years.map(y => <option key={y} value={y}>{y}</option>)}
                  </select>
                </div>
                <div style={{ flex: 1, minWidth: 110 }}>
                  <label>Mois</label>
                  <select value={filter.month} onChange={e => setFilter({ ...filter, month: e.target.value })}>
                    <option value="">Tous</option>
                    {MOIS_FR.map((m, i) => <option key={i+1} value={String(i+1).padStart(2,'0')}>{m}</option>)}
                  </select>
                </div>
              </div>

              {/* Tableau */}
              <div style={{ overflowY: 'auto' }}>
                <table>
                  <thead>
                    <tr>
                      <th>Date</th><th>Plateforme</th>{multiDetenteur && <th>Détenteur</th>}<th>Type de mouvement</th><th className="num">Montant</th><th style={{ width: 36 }}></th>
                    </tr>
                  </thead>
                  <tbody>
                    {tableRows.length === 0 && (
                      <tr><td colSpan={multiDetenteur ? 6 : 5} className="text-muted" style={{ textAlign: 'center', padding: 24 }}>
                        {loading ? 'Chargement…' : 'Aucun mouvement'}
                      </td></tr>
                    )}
                    {pagedTableRows.map(r => {
                      if (r.type === 'correction') {
                        const isPos = r.montant >= 0;
                        return (
                          <tr key={r.id}
                            className={`dr-row${String(selectedRow?.id) === String(r.id) ? ' dr-row-selected' : ''}`}
                            onClick={() => setSelectedRow(String(selectedRow?.id) === String(r.id) ? null : r)}
                            style={{ cursor: 'pointer' }}>
                            <td>{fmtDate(r.date_operation)}</td>
                            <td>{r.plateforme_nom}</td>
                            {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.plateforme_detenteur_nom || '—'}</td>}
                            <td>
                              <span style={{
                                display: 'inline-block',
                                fontSize: 11, fontWeight: 600, letterSpacing: '0.03em',
                                padding: '2px 7px', borderRadius: 4,
                                background: 'color-mix(in srgb, var(--text-muted) 12%, transparent)',
                                color: 'var(--text-muted)',
                              }}>correction</span>
                            </td>
                            <td className={`num ${isPos ? 'success' : 'danger'}`}>
                              {isPos ? '+' : '−'} {fmtEUR(Math.abs(r.montant))}
                            </td>
                            <td style={{ width: 36, textAlign: 'center' }} onClick={e => e.stopPropagation()}>
                              <button
                                style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px 6px', borderRadius: 4, color: 'var(--text-muted)', lineHeight: 1, fontSize: 16 }}
                                onClick={e => openRowMenu(e, r)}>⋮</button>
                            </td>
                          </tr>
                        );
                      }
                      const isAuto = r.source === 'auto_remboursement';
                      return (
                        <tr key={r.id}
                          className={`dr-row${selectedRow?.id === r.id ? ' dr-row-selected' : ''}`}
                          onClick={() => setSelectedRow(selectedRow?.id === r.id ? null : r)}
                          style={{ cursor: 'pointer', opacity: isAuto ? 0.8 : 1 }}>
                          <td>{fmtDate(r.date_operation)}</td>
                          <td>{r.plateforme_nom}</td>
                          {multiDetenteur && <td className="text-muted" style={{ fontSize: 'var(--fs-sm)' }}>{r.plateforme_detenteur_nom || '—'}</td>}
                          <td>
                            <span className={`badge ${r.type}`}>{r.type}</span>
                            {isAuto && (
                              <span style={{
                                marginLeft: 6,
                                fontSize: 10, fontWeight: 600, letterSpacing: '0.03em',
                                padding: '1px 5px', borderRadius: 3,
                                background: 'color-mix(in srgb, var(--primary) 15%, transparent)',
                                color: 'var(--primary)',
                                verticalAlign: 'middle',
                              }}>AUTO</span>
                            )}
                          </td>
                          <td className={`num ${r.type === 'depot' ? 'success' : 'danger'}`}
                            style={ isAuto ? { fontStyle: 'italic' } : undefined }>
                            {r.type === 'depot' ? '+' : '−'} {fmtEUR(r.montant)}
                          </td>
                          <td style={{ width: 36, textAlign: 'center' }} onClick={e => e.stopPropagation()}>
                            {!isAuto && (
                              <button
                                style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px 6px', borderRadius: 4, color: 'var(--text-muted)', lineHeight: 1, fontSize: 16 }}
                                onClick={e => openRowMenu(e, r)}>⋮</button>
                            )}
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
                <Pagination
                  page={drPage} setPage={setDrPage}
                  pageSize={drPageSize} setPageSize={setDrPageSize}
                  totalPages={drTotalPages} totalItems={drTotalItems}
                  PAGE_SIZES={PAGE_SIZES}
                />
              </div>
            </div>
          </div>

          {/* Colonne droite 1/3 */}
          {tableRows.length > 0 && (
            <div className="dr-mouvements-detail">
              <DetailPanel
                row={selectedRow}
                onEdit={openEdit}
                onDeleteCorrection={deleteCorrection}
              />
            </div>
          )}
        </div>
      )}

      {/* Modal création / édition */}
      <Modal
        open={modalOpen}
        title={editingId ? 'Modifier le mouvement' : 'Nouveau mouvement'}
        onClose={close}
        footer={
          <div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
            <div style={{ display: 'flex', gap: 8 }}>
              {editingId && <button className="danger" onClick={onDelete}>Supprimer</button>}
              {editingId && <button onClick={() => openCorrection(null)}>Correction de solde</button>}
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              <button onClick={close}>Annuler</button>
              <button className="primary" onClick={submit}>{editingId ? 'Enregistrer' : 'Créer'}</button>
            </div>
          </div>
        }
      >
        <form onSubmit={submit}>
          {err && <div className="error">{err}</div>}
          <div className="form-grid">
            <div>
              <label>Plateforme *</label>
              <select required value={form.plateforme_id} onChange={e => {
                const platId = e.target.value;
                setForm(f => ({ ...f, plateforme_id: platId, investisseur_id: investisseurForPlat(platId) }));
              }}>
                <option value="">—</option>
                {plats.map(p => <option key={p.id} value={p.id}>{p.nom}{multiDetenteur && p.investisseur_nom ? ` — ${p.investisseur_nom}` : ''}</option>)}
              </select>
            </div>
            <div>
              <label>Date *</label>
              <input type="date" required value={form.date_operation} onChange={e => setForm({ ...form, date_operation: e.target.value })} />
            </div>
            <div>
              <label>Type *</label>
              <select required value={form.type} onChange={e => setForm({ ...form, type: e.target.value })}>
                <option value="depot">Dépôt</option>
                <option value="retrait">Retrait</option>
              </select>
            </div>
            <div>
              <label>Montant (€) *</label>
              <input type="number" step="0.01" min="0" required value={form.montant} onChange={e => setForm({ ...form, montant: e.target.value })} />
            </div>
            <div style={{ gridColumn: '1 / -1' }}>
              <label>Libellé</label>
              <input value={form.libelle} onChange={e => setForm({ ...form, libelle: e.target.value })} placeholder="Ex. Virement initial" />
            </div>
            <div>
              <label>Référence</label>
              <input value={form.reference} onChange={e => setForm({ ...form, reference: e.target.value })} />
            </div>
            <div style={{ gridColumn: '1 / -1' }}>
              <label>Détenteur *</label>
              <select required value={form.investisseur_id} onChange={e => setForm({ ...form, investisseur_id: e.target.value })}>
                <option value="">—</option>
                {investisseurs.filter(i => i.type !== 'entreprise').length > 0 && (
                  <optgroup label="Famille">
                    {investisseurs.filter(i => i.type !== 'entreprise').map(i => (
                      <option key={i.id} value={i.id}>
                        {i.nom}{i.is_principal ? ' (principal)' : ''}
                      </option>
                    ))}
                  </optgroup>
                )}
                {investisseurs.filter(i => i.type === 'entreprise').length > 0 && (
                  <optgroup label="Entreprises">
                    {investisseurs.filter(i => i.type === 'entreprise').map(i => (
                      <option key={i.id} value={i.id}>{i.nom}</option>
                    ))}
                  </optgroup>
                )}
              </select>
            </div>
            <div style={{ gridColumn: '1 / -1' }}>
              <label>Notes</label>
              <input value={form.notes} onChange={e => setForm({ ...form, notes: e.target.value })} />
            </div>
          </div>
        </form>
      </Modal>

      {openMenu && (
        <>
          <div style={{ position: 'fixed', inset: 0, zIndex: 299 }} onClick={() => setOpenMenu(null)} />
          <div style={{
            position: 'fixed', left: openMenu.x, top: openMenu.y,
            transform: 'translateX(-100%) translateY(4px)',
            zIndex: 300,
            background: 'var(--surface)', border: '1px solid var(--border)',
            borderRadius: 8, boxShadow: '0 4px 20px rgba(0,0,0,0.15)',
            padding: '4px 0', minWidth: 140,
          }}>
            {openMenu.row.type !== 'correction' && (
              <button
                style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '8px 14px', background: 'none', border: 'none', cursor: 'pointer', fontSize: 'var(--fs-sm)', color: 'var(--text)', textAlign: 'left' }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}
                onClick={() => { setOpenMenu(null); openEdit(openMenu.row); }}>
                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
                Modifier
              </button>
            )}
            {openMenu.row.type !== 'correction' && openMenu.row.source !== 'auto_remboursement' && (
              <button
                style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '8px 14px', background: 'none', border: 'none', cursor: 'pointer', fontSize: 'var(--fs-sm)', color: 'var(--text)', textAlign: 'left' }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}
                onClick={() => { setOpenMenu(null); openCorrection(openMenu.row); }}>
                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
                Correction de solde
              </button>
            )}
            <button
              style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '8px 14px', background: 'none', border: 'none', cursor: 'pointer', fontSize: 'var(--fs-sm)', color: 'var(--danger)', textAlign: 'left' }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
              onMouseLeave={e => e.currentTarget.style.background = 'none'}
              onClick={() => {
                const row = openMenu.row;
                setOpenMenu(null);
                if (row.type === 'correction') deleteCorrection(row._correctionId);
                else deleteRow(row.id);
              }}>
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
              Supprimer
            </button>
          </div>
        </>
      )}

      <CorrectionModal
        open={corrModal.open}
        plateforme_nom={corrModal.plateforme_nom}
        type_operation={corrModal.type_operation}
        computedBalance={corrModal.computedBalance}
        date={corrModal.date}
        onClose={() => { setCorrModal(m => ({ ...m, open: false })); load(); }}
        onConfirm={handleCorrectionConfirm}
      />

      {deleteConfirm && (
        <ConfirmModal
          open={true}
          message={deleteConfirm?.message}
          onConfirm={deleteConfirm?.onConfirm}
          onCancel={() => setDeleteConfirm(null)}
        />
      )}
    </>
  );
}
