import { useEffect, useRef, useState } from 'react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { api } from '../api.js';
import { useInvestisseur } from '../context/InvestisseurContext.jsx';
import { useUi } from '../context/UiContext.jsx';
import Modal from '../components/Modal.jsx';
import ConfirmModal from '../components/ConfirmModal.jsx';
import CountrySelect, { COUNTRIES, FlagIcon } from '../components/CountrySelect.jsx';
import { fmtEUR, fmtPct, fmtDate, today } from '../utils/format.js';

const emptyForm = {
  investisseur_id: '', plateforme_id: '', nom_projet: '', emetteur: '',
  date_souscription: today(), date_premiere_echeance: '', date_cible: '', date_debut_simul: '',
  montant_investi: '', taux_interet: '', duree_mois: '',
  type_remb: 'in_fine', freq_interets: 'mensuel',
  statut: 'en_cours', reference: '', notes: '', pays_exposition: 'FR',
  echeance_fin_de_mois: false,
  methode_remboursement: '', nom_compte_courant: '', compte_id: '',
};

/**
 * Catégorie par défaut lors d'un changement de plateforme :
 * - 0 catégories → ''
 * - 1 catégorie  → cette catégorie
 * - 2+           → première de la liste (pas d'historique disponible sur la page détail)
 */

function addMonthsFE(isoDate, n) {
  if (!isoDate || !n) return '';
  const [y, m, d] = isoDate.split('-').map(Number);
  const dt = new Date(Date.UTC(y, m - 1 + n, d));
  return dt.toISOString().slice(0, 10);
}

/** Retourne le dernier jour du mois de isoDate. Ex : '2024-01-28' → '2024-01-31' */
function lastDayOfMonthFE(isoDate) {
  if (!isoDate) return isoDate;
  const [y, m] = isoDate.split('-').map(Number);
  const dt = new Date(Date.UTC(y, m, 0));
  return dt.toISOString().slice(0, 10);
}

/**
 * Ajoute n mois et positionne sur le dernier jour du mois cible.
 * Évite les débordements JS (31 jan + 1 = 3 mar → ici = 29 fév).
 */
function addMonthsFinDeMoisFE(isoDate, n) {
  if (!isoDate || (n === undefined || n === null)) return '';
  const [y, m] = isoDate.split('-').map(Number);
  const dt = new Date(Date.UTC(y, m - 1 + Number(n) + 1, 0));
  return dt.toISOString().slice(0, 10);
}

/** Retourne le numéro du jour dans le mois (1-31) ou 0 si absent. */
function dayOfMonth(isoDate) {
  if (!isoDate || isoDate.length < 10) return 0;
  return parseInt(isoDate.slice(8, 10), 10);
}

function typeLabel(type_remb, freq_interets) {
  if (type_remb === 'in_fine') return `In fine · ${freq_interets === 'trimestriel' ? 'Trimestriel' : 'Mensuel'}`;
  if (type_remb === 'amortissable') return `Amortissable · ${freq_interets === 'trimestriel' ? 'Trimestriel' : 'Mensuel'}`;
  if (type_remb === 'differe') return 'Différé (versement unique)';
  return '—';
}

const STATUT_META = {
  en_cours:  { label: 'En cours',   color: '#3b82f6', bg: 'rgba(59,130,246,0.12)'  },
  rembourse: { label: 'Remboursé',  color: '#22c55e', bg: 'rgba(34,197,94,0.12)'   },
  en_retard: { label: 'En retard',  color: '#ef4444', bg: 'rgba(239,68,68,0.12)'    },
  procedure: { label: 'Procédure',  color: '#f97316', bg: 'rgba(249,115,22,0.12)'  },
  cloture:   { label: 'Clôturé',   color: '#6b7280', bg: 'rgba(107,114,128,0.12)' },
};

/**
 * XIRR — taux de rendement interne annualisé sur flux datés.
 * cashflows : [{ amount: number, date: string 'YYYY-MM-DD' }]
 *   le premier flux doit être négatif (investissement initial).
 * Retourne le taux annualisé (ex: 0.112 = 11.2%) ou null si non convergent.
 */
function xirr(cashflows) {
  if (!cashflows || cashflows.length < 2) return null;
  const t0 = new Date(cashflows[0].date).getTime();
  const years = cashflows.map(cf => (new Date(cf.date).getTime() - t0) / (365.25 * 864e5));
  const amounts = cashflows.map(cf => cf.amount);

  const npv  = r => amounts.reduce((s, a, i) => s + a / Math.pow(1 + r, years[i]), 0);
  const dnpv = r => amounts.reduce((s, a, i) => s - years[i] * a / Math.pow(1 + r, years[i] + 1), 0);

  let rate = 0.1;
  for (let i = 0; i < 200; i++) {
    const f  = npv(rate);
    const df = dnpv(rate);
    if (Math.abs(df) < 1e-12) break;
    const next = rate - f / df;
    if (Math.abs(next - rate) < 1e-8) return isFinite(next) ? next : null;
    rate = next;
    if (rate < -0.999) rate = -0.999; // garde-fou
  }
  return null;
}

export default function InvestissementDetail() {
  const { id } = useParams();
  const navigate = useNavigate();
  const [searchParams, setSearchParams] = useSearchParams();
  const { activeId, investisseurs } = useInvestisseur();
  const { displayMode } = useUi();
  const netMode = displayMode === 'net';

  const [inv, setInv] = useState(null);
  const [plats, setPlats] = useState([]);
  const [comptesCourants, setComptesCourants] = useState([]);
  const [comptesInvestisseur, setComptesInvestisseur] = useState([]);
  const [comptesRembInvestisseur, setComptesRembInvestisseur] = useState([]);
  const [pfuRates, setPfuRates] = useState([]);
  // Modal édition investissement
  const [modalOpen, setModalOpen] = useState(false);
  const [form, setForm] = useState(emptyForm);
  const [err, setErr] = useState(null);
  // Mémorise la dernière souscription valide (utile pour isAutoCalc malgré les onChange "" intermédiaires)
  const lastValidSouscriptionRef = useRef('');

  // Charge les comptes du détenteur quand l'investisseur du formulaire change
  useEffect(() => {
    if (!form.investisseur_id || !modalOpen) { setComptesInvestisseur([]); return; }
    api.get(`/investissements/comptes-par-investisseur/${form.investisseur_id}`)
      .then(comptes => {
        setComptesInvestisseur(comptes);
        if (form.methode_remboursement === 'compte_courant' && !form.compte_id) {
          const def = comptes.find(c => c.type === 'compte_courant') ?? comptes[0];
          if (def) setForm(f => ({ ...f, compte_id: String(def.id), nom_compte_courant: def.nom }));
        }
      })
      .catch(() => setComptesInvestisseur([]));
  }, [form.investisseur_id, modalOpen]); /* eslint-disable-next-line */
  // Origine de la navigation (ex. 'dashboard') — permet de revenir en arrière après la modale remboursement
  const fromRef = useRef(null);
  // Modal saisie / édition remboursement
  const [rembModalOpen, setRembModalOpen] = useState(false);
  const [editingRembId, setEditingRembId] = useState(null);
  const [rembForm, setRembForm] = useState({});
  const [rembErr, setRembErr] = useState(null);

  // Charge les comptes du détenteur pour le formulaire remboursement
  useEffect(() => {
    if (!rembModalOpen || !inv?.investisseur_id) { setComptesRembInvestisseur([]); return; }
    api.get(`/investissements/comptes-par-investisseur/${inv.investisseur_id}`)
      .then(comptes => {
        setComptesRembInvestisseur(comptes);
        if (rembForm.methode_remboursement === 'compte_courant' && !rembForm.compte_id) {
          const def = comptes.find(c => c.type === 'compte_courant') ?? comptes[0];
          if (def) setRembForm(f => ({ ...f, compte_id: String(def.id) }));
        }
      })
      .catch(() => setComptesRembInvestisseur([]));
  }, [rembModalOpen]); /* eslint-disable-next-line */
  const [confirmingRembDelete, setConfirmingRembDelete] = useState(false);
  const [confirmingInvDelete, setConfirmingInvDelete] = useState(false);
  const [confirmingHistDelete, setConfirmingHistDelete] = useState(null); // id de l'entrée à supprimer
  const [loading, setLoading] = useState(true);
  const [recalculating, setRecalculating] = useState(false);
  // Modal réinvestissement
  const [reinvModalOpen, setReinvModalOpen] = useState(false);
  const [reinvForm, setReinvForm] = useState({ montant: '', date_reinvestissement: today(), note: '' });
  const [reinvErr, setReinvErr] = useState(null);
  const [confirmingReinvDelete, setConfirmingReinvDelete] = useState(null);
  const [openMenu, setOpenMenu] = useState(null);
  const [rowDeleteConfirm, setRowDeleteConfirm] = useState(null);
  const [cardMenu, setCardMenu] = useState(null);
  const [fiscaliteOverrideConfirm, setFiscaliteOverrideConfirm] = useState(false);
  const [rembMenu, setRembMenu] = useState(null);
  const [reinvMenu, setReinvMenu] = useState(null);
  const [simulMenu, setSimulMenu] = useState(null);
  const [editDpeModal, setEditDpeModal] = useState(false);
  const [editDpeValue, setEditDpeValue] = useState('');
  const [editDpeErr, setEditDpeErr] = useState(null);
  const [editDpeSaving, setEditDpeSaving] = useState(false);
  const [bulkRembModal, setBulkRembModal] = useState(false);
  const [bulkRembItems, setBulkRembItems] = useState([]);
  const [bulkRembProcessing, setBulkRembProcessing] = useState(false);
  const [bulkRembProgress, setBulkRembProgress] = useState(0);
  const [bulkRembDone, setBulkRembDone] = useState(false);
  const [reinvTab, setReinvTab] = useState('manuel'); // 'manuel' | 'auto'

  const load = async () => {
    setLoading(true);
    try {
      const [data, p, cats, comptes] = await Promise.all([
        api.get(`/investissements/${id}`),
        api.get('/plateformes'),
        api.get('/investissements/comptes-courants'),
      ]);
      setInv(data);
      setPlats(p);
      setComptesCourants(comptes);
    } finally { setLoading(false); }
  };

  useEffect(() => { if (activeId) load(); /* eslint-disable-next-line */ }, [id, activeId]);
  useEffect(() => { api.get('/pfu').then(setPfuRates).catch(() => {}); }, []);

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

  // Ouverture automatique de la modale remboursement si remb-date est dans l'URL
  // Attend que pfuRates soit chargé pour calculer les prélèvements indicatifs
  useEffect(() => {
    const rembDate = searchParams.get('remb-date');
    if (!rembDate || !inv || !pfuRates.length) return;
    // Capturer l'origine en premier (avant l'ouverture de la modale)
    const from = searchParams.get('from');
    if (from) fromRef.current = from;
    setSearchParams(p => { p.delete('remb-date'); p.delete('from'); return p; }, { replace: true });
    const simul = inv.simul || [];
    const s = simul.find(x => x.date_prevue === rembDate);
    if (s) {
      openRembFromSimul(s);
    } else {
      // Pas d'entrée simul exacte — ouvre avec la date et les prélèvements estimés
      const plat  = plats.find(p => p.id === inv?.plateforme_id);
      const rates = getRatesForYear(rembDate);
      const methode = plat && plat.methode_remboursement !== 'choix_investisseur'
        ? plat.methode_remboursement
        : (inv?.methode_remboursement || 'portefeuille');
      setEditingRembId(null);
      setRembForm(prev => ({
        ...prev,
        date_remb:             rembDate,
        prelev_sociaux:        rates ? round2((prev.interets_bruts || 0) * rates.prelev_sociaux / 100) : 0,
        prelev_forfaitaire:    rates ? round2((prev.interets_bruts || 0) * rates.impot_revenu  / 100) : 0,
        methode_remboursement: methode,
      }));
      setRembErr(null);
      setRembModalOpen(true);
    }
  /* eslint-disable-next-line */
  }, [inv, pfuRates, searchParams.get('remb-date')]);

  // ── Helpers PFU / calculs remboursement ────────────────────────
  const round2 = n => Math.round(n * 100) / 100;
  const getLastKnownRates = () =>
    pfuRates.reduce((best, r) => r.annee > best.annee ? r : best, pfuRates[0]);

  const getRatesForYear = (dateStr) => {
    if (!dateStr || !pfuRates.length) return null;
    const year = parseInt(dateStr.slice(0, 4), 10);
    return pfuRates.find(r => r.annee === year) ?? getLastKnownRates();
  };
  const computeInteretsNets = (f) =>
    round2((Number(f.interets_bruts) || 0) - (Number(f.prelev_sociaux) || 0) - (Number(f.prelev_forfaitaire) || 0));
  const computeNet = (f) =>
    round2((Number(f.capital) || 0) + (Number(f.cashback) || 0) + computeInteretsNets(f));

  const setRembField = (k, v) => {
    setRembForm(prev => {
      const next = { ...prev, [k]: v };
      const plat = plats.find(p => p.id === inv?.plateforme_id);
      const hasLocalTax = plat?.fiscalite === 'avec_fiscalite_locale' && plat?.taux_fiscalite_locale;

      // Fiscalité locale : calcule taxe_locale et interets_bruts depuis le brut avant retenue
      if (k === 'interets_bruts_avant_local') {
        const taux = hasLocalTax ? (plat.taux_fiscalite_locale || 0) : 0;
        next.taxe_locale    = round2(Number(v) * taux / 100);
        next.interets_bruts = round2(Number(v) - next.taxe_locale);
      }
      // Correction manuelle de la taxe locale : recalcule interets_bruts
      if (k === 'taxe_locale') {
        next.interets_bruts = round2(Number(next.interets_bruts_avant_local) - Number(v));
      }

      if (k === 'date_remb' || k === 'interets_bruts' || k === 'interets_bruts_avant_local' || k === 'taxe_locale') {
        const dateStr = k === 'date_remb' ? v : next.date_remb;
        const bruts   = Number(next.interets_bruts) || 0;
        const rates   = getRatesForYear(dateStr);
        if (rates) {
          next.prelev_sociaux     = round2(bruts * rates.prelev_sociaux / 100);
          next.prelev_forfaitaire = round2(bruts * rates.impot_revenu  / 100);
        }
      }
      return next;
    });
  };

  const openRembFromSimul = (s) => {
    const bruts = s.interets_prevus || 0;
    const rates = getRatesForYear(s.date_prevue);
    const plat  = plats.find(p => p.id === inv?.plateforme_id);
    const methode = plat && plat.methode_remboursement !== 'choix_investisseur'
      ? plat.methode_remboursement
      : (inv?.methode_remboursement || 'portefeuille');
    const hasLocalTax = plat?.fiscalite === 'avec_fiscalite_locale' && plat?.taux_fiscalite_locale;
    const taxe_locale     = hasLocalTax ? round2(bruts * plat.taux_fiscalite_locale / 100) : 0;
    const brutsApresLocal = hasLocalTax ? round2(bruts - taxe_locale) : bruts;
    const baseRates       = brutsApresLocal;
    setEditingRembId(null);
    setRembForm({
      date_remb:                s.date_prevue,
      capital:                  s.capital_prevu  || 0,
      cashback:                 0,
      interets_bruts_avant_local: hasLocalTax ? bruts : 0,
      taxe_locale,
      interets_bruts:           brutsApresLocal,
      prelev_sociaux:           rates ? round2(baseRates * rates.prelev_sociaux / 100) : 0,
      prelev_forfaitaire:       rates ? round2(baseRates * rates.impot_revenu  / 100) : 0,
      statut:                   'paye',
      notes:                    '',
      methode_remboursement:    methode,
      compte_id:                methode === 'compte_courant' ? (inv?.compte_id || '') : '',
    });
    setRembErr(null);
    setRembModalOpen(true);
  };

  const openEditRemb = (r) => {
    setEditingRembId(r.id);
    setRembForm({
      date_remb:             r.date_remb,
      capital:               r.capital            ?? 0,
      cashback:              r.cashback           ?? 0,
      interets_bruts_avant_local: r.interets_bruts_avant_local ?? 0,
      taxe_locale:           r.taxe_locale        ?? 0,
      interets_bruts:        r.interets_bruts     ?? 0,
      prelev_sociaux:        r.prelev_sociaux     ?? 0,
      prelev_forfaitaire:    r.prelev_forfaitaire ?? 0,
      statut:                r.statut,
      notes:                 r.notes || '',
      methode_remboursement: r.methode_remboursement || 'portefeuille',
      compte_id: r.compte_id || '',
    });
    setRembErr(null);
    setRembModalOpen(true);
  };

  const openNewRemb = () => {
    const plat = plats.find(p => p.id === inv?.plateforme_id);
    const methode = plat && plat.methode_remboursement !== 'choix_investisseur'
      ? plat.methode_remboursement
      : (inv?.methode_remboursement || 'portefeuille');
    setEditingRembId(null);
    setRembForm({
      date_remb:             today(),
      capital:               0,
      cashback:              0,
      interets_bruts_avant_local: 0,
      taxe_locale:           0,
      interets_bruts:        0,
      prelev_sociaux:        0,
      prelev_forfaitaire:    0,
      statut:                'paye',
      notes:                 '',
      methode_remboursement: methode,
      compte_id:             methode === 'compte_courant' ? (inv?.compte_id || '') : '',
    });
    setRembErr(null);
    setRembModalOpen(true);
  };

  const closeRembModal = () => {
    const goBack = fromRef.current === 'dashboard';
    fromRef.current = null;
    setRembModalOpen(false); setEditingRembId(null); setRembErr(null); setConfirmingRembDelete(false);
    if (goBack) navigate('/');
  };

  const recalculateSchedule = async () => {
    setRecalculating(true);
    try {
      await api.post('/simul/recalculate', { investissement_id: Number(id) });
      await load();
    } catch (e) {
      alert('Erreur lors du recalcul : ' + e.message);
    } finally {
      setRecalculating(false);
    }
  };

  const saveEditDpe = async () => {
    setEditDpeErr(null);
    if (!editDpeValue) { setEditDpeErr('Veuillez saisir une date.'); return; }
    setEditDpeSaving(true);
    try {
      const payload = {
        plateforme_id:          inv.plateforme_id,
        investisseur_id:        inv.investisseur_id,
        nom_projet:             inv.nom_projet,
        emetteur:               inv.emetteur || undefined,
        date_souscription:      inv.date_souscription,
        date_premiere_echeance: editDpeValue,
        date_cible:             inv.date_cible || undefined,
        date_debut_simul:       inv.date_debut_simul || undefined,
        montant_investi:        inv.montant_investi,
        taux_interet:           inv.taux_interet ?? undefined,
        duree_mois:             inv.duree_mois ?? undefined,
        type_remb:              inv.type_remb || undefined,
        freq_interets:          inv.freq_interets,
        statut:                 inv.statut,
        reference:              inv.reference || undefined,
        notes:                  inv.notes || undefined,
        echeance_fin_de_mois:   inv.echeance_fin_de_mois ?? 0,
        methode_remboursement:  inv.methode_remboursement ?? null,
        nom_compte_courant:     inv.nom_compte_courant ?? null,
      };
      await api.put(`/investissements/${id}`, payload);
      await api.post('/simul/recalculate', { investissement_id: Number(id) });
      await load();
      setEditDpeModal(false);
    } catch (e) {
      setEditDpeErr(e.message || 'Erreur lors de la sauvegarde.');
    } finally {
      setEditDpeSaving(false);
    }
  };

  const openBulkRembModal = () => {
    const todayStr = today();
    const plat = plats.find(p => p.id === inv?.plateforme_id);
    const methode = plat && plat.methode_remboursement !== 'choix_investisseur'
      ? plat.methode_remboursement
      : (inv?.methode_remboursement || 'portefeuille');
    const hasLocalTax = plat?.fiscalite === 'avec_fiscalite_locale' && plat?.taux_fiscalite_locale;
    // Correspondance stricte (sans le fallback capRestant === 0)
    const localRembByExactDate = new Map((inv.remboursements || []).map(r => [r.date_remb, r]));
    const localRembByMonth     = new Map((inv.remboursements || []).map(r => [r.date_remb.slice(0, 7), r]));
    const isMatched = (s) =>
      localRembByExactDate.has(s.date_prevue) ||
      localRembByMonth.has(s.date_prevue?.slice(0, 7));
    const pending = (inv.simul || []).filter(s => s.date_prevue < todayStr && !isMatched(s));
    const items = pending.map(s => {
      const bruts = s.interets_prevus || 0;
      const rates = getRatesForYear(s.date_prevue);
      const taxe_locale     = hasLocalTax ? round2(bruts * plat.taux_fiscalite_locale / 100) : 0;
      const brutsApresLocal = hasLocalTax ? round2(bruts - taxe_locale) : bruts;
      const baseRates       = brutsApresLocal;
      return {
        _simul: s,
        payload: {
          investissement_id:          Number(id),
          date_remb:                  s.date_prevue,
          capital:                    s.capital_prevu  || 0,
          cashback:                   0,
          interets_bruts_avant_local: hasLocalTax ? bruts : 0,
          taxe_locale,
          interets_bruts:             brutsApresLocal,
          prelev_sociaux:             rates ? round2(baseRates * rates.prelev_sociaux / 100) : 0,
          prelev_forfaitaire:         rates ? round2(baseRates * rates.impot_revenu  / 100) : 0,
          statut:                     'paye',
          methode_remboursement:      methode,
        },
      };
    });
    setBulkRembItems(items);
    setBulkRembProgress(0);
    setBulkRembDone(false);
    setBulkRembModal(true);
  };

  const runBulkRemb = async () => {
    setBulkRembProcessing(true);
    setBulkRembProgress(0);
    try {
      for (let i = 0; i < bulkRembItems.length; i++) {
        await api.post('/remboursements', bulkRembItems[i].payload);
        setBulkRembProgress(i + 1);
      }
      setBulkRembDone(true);
      await load();
    } catch (e) {
      alert('Erreur lors du traitement : ' + e.message);
    } finally {
      setBulkRembProcessing(false);
    }
  };

  const submitRemb = async (e) => {
    e?.preventDefault?.();
    setRembErr(null);
    const payload = {
      investissement_id:     Number(id),
      date_remb:             rembForm.date_remb,
      capital:               Number(rembForm.capital            || 0),
      cashback:              Number(rembForm.cashback           || 0),
      interets_bruts_avant_local: Number(rembForm.interets_bruts_avant_local || 0),
      taxe_locale:           Number(rembForm.taxe_locale        || 0),
      interets_bruts:        Number(rembForm.interets_bruts     || 0),
      prelev_sociaux:        Number(rembForm.prelev_sociaux     || 0),
      prelev_forfaitaire:    Number(rembForm.prelev_forfaitaire || 0),
      statut:                rembForm.statut,
      notes:                 rembForm.notes || undefined,
      methode_remboursement: rembForm.methode_remboursement || 'portefeuille',
      compte_id: rembForm.methode_remboursement === 'compte_courant' && rembForm.compte_id ? Number(rembForm.compte_id) : null,
    };
    try {
      if (editingRembId) await api.put(`/remboursements/${editingRembId}`, payload);
      else               await api.post('/remboursements', payload);
      const goBack = fromRef.current === 'dashboard';
      closeRembModal();
      if (!goBack) await load();
    } catch (e) { setRembErr(e.message); }
  };

  const deleteRemb = async () => {
    try {
      await api.del(`/remboursements/${editingRembId}`);
      const goBack = fromRef.current === 'dashboard';
      closeRembModal();
      if (!goBack) await load();
    } catch (e) { setConfirmingRembDelete(false); setRembErr(e.message); }
  };

  const openEdit = () => {
    const plat = plats.find(p => p.id === inv.plateforme_id);
    const platMethode = plat?.methode_remboursement;
    const resolvedMethode = platMethode && platMethode !== 'choix_investisseur'
      ? platMethode
      : (inv.methode_remboursement || '');
    setForm({
      investisseur_id: String(inv.investisseur_id || ''),
      plateforme_id: inv.plateforme_id,
      nom_projet: inv.nom_projet,
      emetteur: inv.emetteur || '',
      date_souscription: inv.date_souscription,
      date_premiere_echeance: inv.date_premiere_echeance || '',
      date_cible: inv.date_cible || '',
      date_debut_simul: inv.date_debut_simul || '',
      montant_investi: inv.montant_investi,
      taux_interet: inv.taux_interet ?? '',
      duree_mois: inv.duree_mois ?? '',
      type_remb: inv.type_remb || 'in_fine',
      freq_interets: inv.freq_interets || 'mensuel',
      statut: inv.statut,
      reference: inv.reference || '',
      notes: inv.notes || '',
      echeance_fin_de_mois: !!inv.echeance_fin_de_mois,
      methode_remboursement: resolvedMethode,
      nom_compte_courant: inv.nom_compte_courant || '',
      compte_id: inv.compte_id || '',
      pays_exposition: inv.pays_exposition || 'FR',
    });
    lastValidSouscriptionRef.current = inv.date_souscription || '';
    setErr(null);
    setModalOpen(true);
  };

  const close = () => { setModalOpen(false); setErr(null); setConfirmingInvDelete(false); };

  const submit = async (e) => {
    e?.preventDefault?.();
    setErr(null);
    try {
      const showFinDeMois = (form.type_remb === 'in_fine' || form.type_remb === 'differe')
        && dayOfMonth(form.date_premiere_echeance) > 27;

      const payload = {
        investisseur_id: Number(form.investisseur_id) || undefined,
        plateforme_id: Number(form.plateforme_id),
        nom_projet: form.nom_projet,
        emetteur: form.emetteur || undefined,
        date_souscription: form.date_souscription,
        date_premiere_echeance: form.date_premiere_echeance || '',
        date_cible: form.date_cible || '',
        date_debut_simul: form.date_debut_simul || '',
        montant_investi: Number(form.montant_investi),
        taux_interet: form.taux_interet === '' ? undefined : Number(form.taux_interet),
        duree_mois: form.duree_mois === '' ? undefined : Number(form.duree_mois),
        type_remb: form.type_remb || '',
        freq_interets: form.type_remb === 'differe' ? 'in_fine' : (form.freq_interets || 'mensuel'),
        statut: form.statut,
        reference: form.reference || undefined,
        notes: form.notes || undefined,
        echeance_fin_de_mois: (showFinDeMois && form.echeance_fin_de_mois) ? 1 : 0,
        methode_remboursement: form.methode_remboursement || null,
        nom_compte_courant: form.methode_remboursement === 'compte_courant' ? (form.nom_compte_courant || null) : null,
        compte_id: form.methode_remboursement === 'compte_courant' && form.compte_id ? Number(form.compte_id) : null,
        pays_exposition: form.pays_exposition || 'FR',
      };
      await api.put(`/investissements/${id}`, payload);
      close();
      await load();
    } catch (e) { setErr(e.message); }
  };

  const onDelete = async () => {
    await api.del(`/investissements/${id}`);
    navigate('/investissements');
  };

  const deleteHist = async (hid) => {
    try {
      await api.del(`/investissements/${id}/historique/${hid}`);
      setConfirmingHistDelete(null);
      await load();
    } catch (e) { /* silencieux */ }
  };

  const openReinvModal = (tab = 'manuel') => {
    setReinvForm({ montant: '', date_reinvestissement: today(), note: '' });
    setReinvErr(null);
    setReinvTab(tab);
    setReinvModalOpen(true);
  };
  const closeReinvModal = () => { setReinvModalOpen(false); setReinvErr(null); setConfirmingReinvDelete(null); };

  const submitReinv = async (e) => {
    e?.preventDefault?.();
    setReinvErr(null);
    const montant = Number(reinvForm.montant);
    if (!montant || montant <= 0) { setReinvErr('Le montant doit être positif.'); return; }
    if (!reinvForm.date_reinvestissement) { setReinvErr('La date est requise.'); return; }
    try {
      await api.post('/reinvestissements', {
        investissement_id: Number(id),
        montant,
        date_reinvestissement: reinvForm.date_reinvestissement,
        note: reinvForm.note || null,
      });
      closeReinvModal();
      await load();
    } catch (e) { setReinvErr(e.message); }
  };

  const deleteReinv = async (reinvId) => {
    try {
      await api.del(`/reinvestissements/${reinvId}`);
      setConfirmingReinvDelete(null);
      await load();
    } catch (e) { setReinvErr(e.message); }
  };

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

  if (loading) return <div style={{ padding: 32 }}>Chargement…</div>;
  if (!inv) return <div style={{ padding: 32 }}>Investissement introuvable.</div>;

  const remb = inv.remboursements || [];
  const simul = inv.simul || [];
  const historique = inv.historique || [];
  const reinvs = inv.reinvestissements || [];
  // Capital total = montant initial + réinvestissements
  const capitalTotal      = inv.capital_total ?? inv.montant_investi;
  const autoReinvActive   = !!inv?.auto_reinvest;
  const hasReinvests      = reinvs.length > 0 || autoReinvActive;
  const multiDetenteur    = new Set(plats.map(p => p.investisseur_id)).size > 1;
  // Calculés depuis les remboursements chargés (la route /:id ne retourne pas ces agrégats)
  const capitalRembourse  = remb.reduce((s, r) => s + (r.capital        || 0), 0);
  const capRestant        = Math.max(0, capitalTotal - capitalRembourse);
  const interetsPercus    = remb.reduce((s, r) => s + (r.interets_bruts || 0), 0);
  const interetsPercusNet = remb.reduce((s, r) => s + (r.interets_nets  || 0), 0);

  // Rapprochement projections ↔ remboursements réels
  // Priorité : date exacte, sinon même mois (YYYY-MM)
  // Si tout le capital est remboursé (capRestant = 0), fallback sur le dernier remboursement
  const rembByExactDate = new Map(remb.map(r => [r.date_remb, r]));
  const rembByMonth     = new Map(remb.map(r => [r.date_remb.slice(0, 7), r]));
  const lastRemb = remb.length > 0
    ? remb.reduce((latest, r) => r.date_remb > latest.date_remb ? r : latest, remb[0])
    : null;
  const matchRemb = (date_prevue) =>
    rembByExactDate.get(date_prevue)
    ?? rembByMonth.get(date_prevue?.slice(0, 7))
    ?? (capRestant === 0 ? lastRemb : null);

  // ── Export dossier JSON ────────────────────────────────────────
  const exportDossier = () => {
    const ts   = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
    const name = `Dossier_Investissement_${inv.id}_${ts}.json`;
    const payload = {
      version:     '1.0',
      type:        'dossier_investissement',
      exported_at: new Date().toISOString(),
      investissement: {
        nom_projet:              inv.nom_projet,
        emetteur:                inv.emetteur,
        date_souscription:       inv.date_souscription,
        date_premiere_echeance:  inv.date_premiere_echeance,
        date_cible:              inv.date_cible,
        date_debut_simul:        inv.date_debut_simul,
        montant_investi:         inv.montant_investi,
        taux_interet:            inv.taux_interet,
        duree_mois:              inv.duree_mois,
        type_remb:               inv.type_remb,
        freq_interets:           inv.freq_interets,
        statut:                  inv.statut,
        reference:               inv.reference,
        source:                  inv.source,
        notes:                   inv.notes,
      },
      plateforme: {
        nom: inv.plateforme_nom,
      },
      remboursements: (inv.remboursements || []).map(r => ({
        date_remb:          r.date_remb,
        capital:            r.capital,
        cashback:           r.cashback,
        interets_bruts:     r.interets_bruts,
        prelev_sociaux:     r.prelev_sociaux,
        prelev_forfaitaire: r.prelev_forfaitaire,
        interets_nets:      r.interets_nets,
        net_recu:           r.net_recu,
        statut:             r.statut,
        notes:              r.notes,
      })),
      projections: (inv.simul || []).map(s => ({
        numero_echeance: s.numero_echeance,
        date_prevue:     s.date_prevue,
        capital_prevu:   s.capital_prevu,
        interets_prevus: s.interets_prevus,
        total_prevu:     s.total_prevu,
      })),
      reinvestissements: (inv.reinvestissements || []).map(r => ({
        date_reinvestissement: r.date_reinvestissement,
        montant:               r.montant,
        source:                r.source,
        note:                  r.note,
      })),
      historique: (inv.historique || []).map(h => ({
        type_evenement: h.type_evenement,
        changements:    h.changements,
        notes:          h.notes,
        created_at:     h.created_at,
      })),
    };
    const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement('a');
    a.href = url; a.download = name; a.click();
    URL.revokeObjectURL(url);
  };

  // Rendement réel annualisé (XIRR) — calculé uniquement si l'opération est remboursée
  let rendementReelBrut = null;
  let rendementReel = null;
  if (inv.statut === 'rembourse' && remb.length > 0 && inv.date_souscription) {
    // Flux sortants supplémentaires (réinvestissements = nouveaux décaissements)
    const reinvOutflows = reinvs.map(r => ({ amount: -r.montant, date: r.date_reinvestissement }));
    // Brut : capital + cashback + intérêts bruts (avant fiscalité)
    const cashflowsBrut = [
      { amount: -inv.montant_investi, date: inv.date_souscription },
      ...reinvOutflows,
      ...remb.map(r => ({ amount: (r.capital || 0) + (r.cashback || 0) + (r.interets_bruts || 0), date: r.date_remb })),
    ];
    rendementReelBrut = xirr(cashflowsBrut);
    // Net : net_recu (après prélèvements sociaux + impôt sur le revenu)
    const cashflows = [
      { amount: -inv.montant_investi, date: inv.date_souscription },
      ...reinvOutflows,
      ...remb.map(r => ({ amount: r.net_recu, date: r.date_remb })),
    ];
    rendementReel = xirr(cashflows);
  }

  return (
    <>
      {/* ── Barre de titre ────────────────────────────────────────── */}
      <div className="topbar" style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <h2 style={{ flex: 1, margin: 0, display: 'flex', alignItems: 'center', gap: 14 }}>
          {inv.plateforme_logo ? (
            <img
              src={`${(import.meta.env.VITE_API_URL || '/api').replace(/\/api$/, '')}/api/logos/${inv.plateforme_logo}`}
              alt={inv.plateforme_nom}
              title={inv.plateforme_nom}
              className="logo-plateforme"
              style={{ height: 38, width: 'auto', maxWidth: 140, objectFit: 'contain', flexShrink: 0 }}
            />
          ) : (
            <span style={{ color: 'var(--text-muted, #6b7280)', fontWeight: 500, fontSize: '0.85em', whiteSpace: 'nowrap' }}>
              {inv.plateforme_nom}
            </span>
          )}
          <span style={{ color: 'var(--text-muted, #6b7280)', fontWeight: 300, fontSize: '0.85em' }}>·</span>
          <span>{inv.nom_projet}</span>
        </h2>
        <button onClick={() => navigate('/investissements')} style={{ whiteSpace: 'nowrap', flexShrink: 0 }}>
          ← Investissements
        </button>
      </div>

      {/* ── KPIs ──────────────────────────────────────────────────── */}
      <div className="kpi-grid" style={{ marginBottom: 16 }}>
        {(() => {
          const meta = STATUT_META[inv.statut] || { label: inv.statut, color: '#6b7280', bg: 'rgba(107,114,128,0.12)' };
          return (
            <div className="kpi" style={{ borderLeft: `4px solid ${meta.color}`, background: meta.bg }}>
              <div className="label">Statut</div>
              <div className="value" style={{ color: meta.color, fontSize: 20, fontWeight: 700, letterSpacing: '0.01em' }}>
                {meta.label}
              </div>
            </div>
          );
        })()}
        <div className="kpi">
          <div className="label">{hasReinvests ? 'Capital total investi' : 'Montant investi'}</div>
          <div className="value">{fmtEUR(capitalTotal)}</div>
          {hasReinvests && (
            <div className="text-muted" style={{ fontSize: 11, marginTop: 2 }}>
              Initial {fmtEUR(inv.montant_investi)} + {fmtEUR(inv.reinvestissements_total)} réinvesti
            </div>
          )}
        </div>
        <div className="kpi">
          <div className="label">Capital remboursé</div>
          <div className="value success">{fmtEUR(capitalRembourse)}</div>
        </div>
        <div className="kpi">
          <div className="label">Capital restant dû</div>
          <div className="value" style={{ color: capRestant === 0 ? 'var(--success)' : undefined }}>
            {fmtEUR(capRestant)}
          </div>
        </div>
        <div className="kpi">
          <div className="label">Intérêts perçus — {netMode ? 'Net' : 'Brut'}</div>
          <div className="value success">{fmtEUR(netMode ? interetsPercusNet : interetsPercus)}</div>
        </div>
        {(() => {
          const rendement = netMode ? rendementReel : rendementReelBrut;
          const titleTip  = rendement !== null
            ? (netMode ? 'XIRR sur flux nets réels (après fiscalité)' : 'XIRR sur flux bruts (avant fiscalité)')
            : inv.statut !== 'rembourse'
              ? 'Disponible uniquement sur les investissements remboursés'
              : 'Données insuffisantes';
          return (
            <div className="kpi" title={titleTip}>
              <div className="label">Rendement annualisé — {netMode ? 'Net' : 'Brut'}</div>
              <div className="value" style={{ color: rendement !== null ? (rendement >= 0 ? 'var(--success)' : 'var(--danger)') : 'var(--text-muted)' }}>
                {rendement !== null
                  ? `${(rendement * 100).toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} %`
                  : inv.statut === 'rembourse' ? '—' : 'En cours…'}
              </div>
            </div>
          );
        })()}
      </div>

      {/* ── Fiche projet ──────────────────────────────────────────── */}
      <div className="card" style={{ marginBottom: 16 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 }}>
          <h3 style={{ margin: 0 }}>Informations du projet{inv?.fiscalite_override === 'exonere' && <span style={{ marginLeft: 10, fontSize: 11, fontWeight: 600, background: 'var(--success, #22c55e)', color: '#fff', borderRadius: 4, padding: '2px 7px', verticalAlign: 'middle' }}>Exonéré flat tax</span>}</h3>
          <button
            onClick={e => { e.stopPropagation(); const r = e.currentTarget.getBoundingClientRect(); setCardMenu({ x: r.right, y: r.bottom }); }}
            style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 30, height: 30, padding: 0, background: 'none', border: '1px solid var(--border)', borderRadius: 6, cursor: 'pointer', color: 'var(--text-muted)', flexShrink: 0 }}
            title="Actions"
          >
            <svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true">
              <circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/>
            </svg>
          </button>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '14px 32px' }}>
          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Plateforme</div>
            <div style={{ fontWeight: 600 }}>{inv.plateforme_nom}</div>
          </div>
          {(inv.categories_inv || []).length > 0 && (
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Catégories d'investissement</div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
                {inv.categories_inv.map(c => (
                  <span key={c.id} className="chip-cat">{c.nom}</span>
                ))}
              </div>
            </div>
          )}
          {(inv.secteurs_inv || []).length > 0 && (
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Secteurs d'investissement</div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
                {inv.secteurs_inv.map(s => (
                  <span key={s.id} className="chip-sect">{s.nom}</span>
                ))}
              </div>
            </div>
          )}
          {inv.emetteur && (
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Émetteur</div>
              <div>{inv.emetteur}</div>
            </div>
          )}
          {inv.reference && (
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Référence</div>
              <div>{inv.reference}</div>
            </div>
          )}
          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Type de prêt</div>
            <div>{typeLabel(inv.type_remb, inv.freq_interets)}</div>
          </div>
          {inv.methode_remboursement && (
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Méthode de remboursement</div>
              <div>{inv.methode_remboursement === 'portefeuille' ? 'Porte-monnaie de la plateforme' : `Compte courant de l'investisseur${inv.compte_nom ? ` — ${inv.compte_nom}` : inv.nom_compte_courant ? ` (${inv.nom_compte_courant})` : ''}`}</div>
            </div>
          )}
          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Taux annuel</div>
            <div>{inv.taux_interet != null ? fmtPct(inv.taux_interet) : '—'}</div>
          </div>
          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Durée</div>
            <div>{inv.duree_mois ? `${inv.duree_mois} mois` : '—'}</div>
          </div>
          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Date souscription</div>
            <div>{fmtDate(inv.date_souscription)}</div>
          </div>
          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Date 1ère échéance</div>
            <div>{fmtDate(inv.date_premiere_echeance) || '—'}</div>
          </div>
          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Date cible contractuelle</div>
            <div>{fmtDate(inv.date_cible) || '—'}</div>
          </div>
          {inv.notes && (
            <div style={{ gridColumn: '1 / -1' }}>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Notes</div>
              <div style={{ whiteSpace: 'pre-wrap' }}>{inv.notes}</div>
            </div>
          )}
          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>Pays d'exposition</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <FlagIcon code={inv.pays_exposition || 'FR'} size={18} />
              <span>{COUNTRIES.find(c => c.code === (inv.pays_exposition || 'FR'))?.name || (inv.pays_exposition || 'FR')}</span>
            </div>
          </div>
        </div>
      </div>

      {/* ── Remboursements enregistrés ────────────────────────────── */}
      <div className="card" style={{ marginBottom: 16 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
          <h3 style={{ margin: 0 }}>
            Remboursements enregistrés
            <span className="text-muted" style={{ fontWeight: 400, fontSize: 13, marginLeft: 8 }}>
              ({remb.length})
            </span>
          </h3>
          <button
            onClick={e => { e.stopPropagation(); const r = e.currentTarget.getBoundingClientRect(); setRembMenu({ x: r.right, y: r.bottom }); }}
            style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 30, height: 30, padding: 0, background: 'none', border: '1px solid var(--border)', borderRadius: 6, cursor: 'pointer', color: 'var(--text-muted)', flexShrink: 0 }}
            title="Actions"
          >
            <svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true">
              <circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/>
            </svg>
          </button>
        </div>
        {(() => {
          const _plat = plats.find(p => p.id === inv?.plateforme_id);
          const _isIndicatif = inv?.fiscalite_override === 'exonere' || _plat?.fiscalite !== 'flat_tax';
          return (
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, marginBottom: 10 }}>
              <span style={{ color: 'var(--text-muted)' }}>
                {_isIndicatif
                  ? "L'imposition n'est pas déduite du montant versé (fiscalité appliquée à titre indicatif)."
                  : "L'imposition est déduite du montant versé (capital + cashback + intérêts nets)."}
              </span>
            </div>
          );
        })()}
        {remb.length === 0 ? (
          <p className="text-muted">Aucun remboursement enregistré.</p>
        ) : (
          <table className="remb-table">
            <thead>
              {(() => {
                const tblPlat = plats.find(p => p.id === inv?.plateforme_id);
                const showLocalCols = tblPlat?.fiscalite === 'avec_fiscalite_locale' && tblPlat?.taux_fiscalite_locale;
                const tblIsIndicatif = inv?.fiscalite_override === 'exonere' || tblPlat?.fiscalite !== 'flat_tax';
                return (
              <tr>
                <th>Date</th>
                <th className="num">Capital</th>
                {showLocalCols && <th className="num">Int. bruts Plateforme</th>}
                {showLocalCols && <th className="num">Taxe Locale</th>}
                <th className="num">Intérêts ({netMode ? 'Net' : 'Brut'})</th>
                <th className="num">Cashback</th>
                <th className="num">Imposition{tblIsIndicatif ? ' — indicatif' : ''}</th>
                <th className="num">Montant versé</th>
                <th style={{ width: 36 }}></th>
              </tr>
                );
              })()}
            </thead>
            <tbody>
              {remb.map(r => (
                <tr key={r.id} style={{ cursor: 'pointer' }} onClick={() => openEditRemb(r)}>
                  <td>{fmtDate(r.date_remb)}</td>
                  <td className="num">{fmtEUR(r.capital)}</td>
                  {plats.find(p => p.id === inv?.plateforme_id)?.fiscalite === 'avec_fiscalite_locale' && plats.find(p => p.id === inv?.plateforme_id)?.taux_fiscalite_locale && (
                    <td className="num">{r.interets_bruts_avant_local ? fmtEUR(r.interets_bruts_avant_local) : '—'}</td>
                  )}
                  {plats.find(p => p.id === inv?.plateforme_id)?.fiscalite === 'avec_fiscalite_locale' && plats.find(p => p.id === inv?.plateforme_id)?.taux_fiscalite_locale && (
                    <td className="num">{r.taxe_locale ? fmtEUR(r.taxe_locale) : '—'}</td>
                  )}
                  <td className="num">{fmtEUR(netMode ? r.interets_nets : r.interets_bruts)}</td>
                  <td className="num">{fmtEUR(r.cashback)}</td>
                  <td className="num cell-tooltip"
                    data-tooltip={`Prélèvements sociaux : ${fmtEUR(r.prelev_sociaux)}\nImpôt sur le revenu : ${fmtEUR(r.prelev_forfaitaire)}`}>
                    {fmtEUR((r.prelev_sociaux || 0) + (r.prelev_forfaitaire || 0))}
                  </td>
                  <td className="num"><strong>{fmtEUR(r.net_recu)}</strong></td>
                  <td style={{ width: 36, textAlign: 'center' }}>
                    <button
                      style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px 6px', borderRadius: 4, color: 'var(--text-muted)', lineHeight: 1, fontSize: 16 }}
                      onClick={e => { e.stopPropagation(); openRowMenu(e, { ...r, _table: 'remb' }); }}>⋮</button>
                  </td>
                </tr>
              ))}
            </tbody>
            {remb.length > 1 && (() => {
              const tot = remb.reduce((acc, r) => ({
                capital:            acc.capital            + (r.capital            || 0),
                interets_bruts_avant_local: acc.interets_bruts_avant_local + (r.interets_bruts_avant_local || 0),
                taxe_locale:        acc.taxe_locale        + (r.taxe_locale        || 0),
                interets_bruts:     acc.interets_bruts     + (r.interets_bruts     || 0),
                prelev_sociaux:     acc.prelev_sociaux     + (r.prelev_sociaux     || 0),
                prelev_forfaitaire: acc.prelev_forfaitaire + (r.prelev_forfaitaire || 0),
                cashback:           acc.cashback           + (r.cashback           || 0),
                interets_nets:      acc.interets_nets      + (r.interets_nets      || 0),
                net_recu:           acc.net_recu           + (r.net_recu           || 0),
              }), { capital: 0, interets_bruts_avant_local: 0, taxe_locale: 0, interets_bruts: 0, prelev_sociaux: 0, prelev_forfaitaire: 0, cashback: 0, interets_nets: 0, net_recu: 0 });
              return (
                <tfoot>
                  <tr style={{ borderTop: '2px solid var(--border, rgba(255,255,255,.12))', fontWeight: 700 }}>
                    <td className="text-muted" style={{ fontSize: 11 }}>Total</td>
                    <td className="num">{fmtEUR(tot.capital)}</td>
                    {plats.find(p => p.id === inv?.plateforme_id)?.fiscalite === 'avec_fiscalite_locale' && plats.find(p => p.id === inv?.plateforme_id)?.taux_fiscalite_locale && (
                      <td className="num">{fmtEUR(tot.interets_bruts_avant_local)}</td>
                    )}
                    {plats.find(p => p.id === inv?.plateforme_id)?.fiscalite === 'avec_fiscalite_locale' && plats.find(p => p.id === inv?.plateforme_id)?.taux_fiscalite_locale && (
                      <td className="num">{fmtEUR(tot.taxe_locale)}</td>
                    )}
                    <td className="num">{fmtEUR(netMode ? tot.interets_nets : tot.interets_bruts)}</td>
                    <td className="num">{fmtEUR(tot.cashback)}</td>
                    <td className="num cell-tooltip"
                      data-tooltip={`Prélèvements sociaux : ${fmtEUR(tot.prelev_sociaux)}\nImpôt sur le revenu : ${fmtEUR(tot.prelev_forfaitaire)}`}>
                      {fmtEUR(tot.prelev_sociaux + tot.prelev_forfaitaire)}
                    </td>
                    <td className="num">{fmtEUR(tot.net_recu)}</td>
                    <td></td>
                  </tr>
                </tfoot>
              );
            })()}
          </table>
        )}
      </div>

      {/* ── Réinvestissements ────────────────────────────────────── */}
      {hasReinvests && (
        <div className="card" style={{ marginBottom: 16 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
            <h3 style={{ margin: 0 }}>
              Réinvestissements complémentaires
              <span className="text-muted" style={{ fontWeight: 400, fontSize: 13, marginLeft: 8 }}>
                ({reinvs.length})
              </span>
            </h3>
            <button
              onClick={e => { e.stopPropagation(); const r = e.currentTarget.getBoundingClientRect(); setReinvMenu({ x: r.right, y: r.bottom }); }}
              style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 30, height: 30, padding: 0, background: 'none', border: '1px solid var(--border)', borderRadius: 6, cursor: 'pointer', color: 'var(--text-muted)', flexShrink: 0 }}
              title="Actions"
            >
              <svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true">
                <circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/>
              </svg>
            </button>
          </div>
          <table>
            <thead>
              <tr>
                <th>Date</th>
                <th className="num">Montant</th>
                <th>Note</th>
                <th className="num">Capital cumulé</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {(() => {
                let cumul = inv.montant_investi;
                return reinvs.map(r => {
                  cumul += r.montant;
                  return (
                    <tr key={r.id}>
                      <td>{fmtDate(r.date_reinvestissement)}</td>
                      <td className="num" style={{ color: 'var(--primary)' }}>
                        +{fmtEUR(r.montant)}
                      </td>
                      <td style={{ color: 'var(--text-muted)', fontSize: 12 }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                          {r.source === 'auto' && (
                            <span style={{ fontSize: 10, fontWeight: 600, padding: '1px 6px', borderRadius: 10, background: 'color-mix(in srgb, var(--primary) 12%, transparent)', color: 'var(--primary)', border: '1px solid color-mix(in srgb, var(--primary) 25%, transparent)', whiteSpace: 'nowrap' }}>auto</span>
                          )}
                          <span>{r.note || (r.source === 'auto' ? '' : '—')}</span>
                        </div>
                      </td>
                      <td className="num" style={{ fontWeight: 600 }}>{fmtEUR(cumul)}</td>
                      <td style={{ width: 36, textAlign: 'center' }}>
                        <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, _table: 'reinv' })}>⋮</button>
                      </td>
                    </tr>
                  );
                });
              })()}
            </tbody>
            {reinvs.length === 0 && autoReinvActive && (
              <tbody>
                <tr>
                  <td colSpan={5} style={{ textAlign: 'center', padding: '16px 0', color: 'var(--text-muted)', fontSize: 13, fontStyle: 'italic' }}>
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                      <span style={{ fontSize: 10, fontWeight: 600, padding: '2px 8px', borderRadius: 10, background: 'color-mix(in srgb, var(--primary) 12%, transparent)', color: 'var(--primary)', border: '1px solid color-mix(in srgb, var(--primary) 25%, transparent)' }}>auto</span>
                      Réinvestissement automatique des intérêts activé — alimenté après chaque remboursement
                    </span>
                  </td>
                </tr>
              </tbody>
            )}
            {reinvs.length > 0 && (
              <tfoot>
                <tr style={{ borderTop: '2px solid var(--border, rgba(255,255,255,.12))', fontWeight: 700 }}>
                  <td className="text-muted" style={{ fontSize: 11 }}>Total réinvesti</td>
                  <td className="num" style={{ color: 'var(--primary)' }}>
                    +{fmtEUR(inv.reinvestissements_total)}
                  </td>
                  <td></td>
                  <td className="num">{fmtEUR(capitalTotal)}</td>
                  <td></td>
                </tr>
              </tfoot>
            )}
          </table>
        </div>
      )}

      {/* ── Projections de remboursements ─────────────────────────── */}
      <div className="card" style={{ marginBottom: 16 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
          <h3 style={{ margin: 0 }}>
            Projections de remboursements
            <span className="text-muted" style={{ fontWeight: 400, fontSize: 13, marginLeft: 8 }}>
              ({simul.length} échéance{simul.length > 1 ? 's' : ''})
            </span>
          </h3>
          <button
            onClick={e => { e.stopPropagation(); const r = e.currentTarget.getBoundingClientRect(); setSimulMenu({ x: r.right, y: r.bottom }); }}
            style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 30, height: 30, padding: 0, background: 'none', border: '1px solid var(--border)', borderRadius: 6, cursor: 'pointer', color: 'var(--text-muted)', flexShrink: 0 }}
            title="Actions"
          >
            <svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true">
              <circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/>
            </svg>
          </button>
        </div>
        {simul.length > 0 && (
          <p className="text-muted" style={{ fontSize: 12, marginTop: 4, marginBottom: 10 }}>
            Cliquez sur une ligne pour saisir le remboursement correspondant.
          </p>
        )}
        {simul.length === 0 ? (
          <p className="text-muted">Aucune projection disponible (taux et durée requis).</p>
        ) : (
          <table>
            <thead>
              <tr>
                <th className="num">N°</th>
                <th>Date prévue</th>
                <th className="num">Capital prévu</th>
                <th className="num">Intérêts ({netMode ? 'Net estimé' : 'Brut'})</th>
                <th className="num">Total prévu</th>
                <th>Statut</th>
              </tr>
            </thead>
            <tbody>
              {simul.map(s => {
                const matched   = matchRemb(s.date_prevue);
                const isPaid    = !!matched;
                const rates     = getRatesForYear(s.date_prevue);
                const reduction = rates ? (rates.prelev_sociaux + rates.impot_revenu) / 100 : 0;
                const interets  = netMode
                  ? (s.interets_prevus || 0) * (1 - reduction)
                  : (s.interets_prevus || 0);
                return (
                  <tr
                    key={s.id}
                    onClick={() => isPaid ? openEditRemb(matched) : openRembFromSimul(s)}
                    style={{ cursor: 'pointer' }}
                    title={isPaid ? 'Cliquer pour modifier ce remboursement' : 'Cliquer pour saisir ce remboursement'}
                    onMouseEnter={e => e.currentTarget.style.opacity = '0.75'}
                    onMouseLeave={e => e.currentTarget.style.opacity = '1'}
                  >
                    <td className="num">{s.numero_echeance}</td>
                    <td>{fmtDate(s.date_prevue)}</td>
                    <td className="num">{fmtEUR(s.capital_prevu)}</td>
                    <td className="num">{fmtEUR(interets)}</td>
                    <td className="num"><strong>{fmtEUR(s.total_prevu)}</strong></td>
                    <td>
                      {isPaid
                        ? <span style={{ color: 'var(--success, #22c55e)', fontSize: 12, fontWeight: 600 }}>
                            ✓ Payé le {fmtDate(matched.date_remb)}
                          </span>
                        : <span style={{ color: 'var(--text-muted)', fontSize: 12 }}>
                            En attente
                          </span>
                      }
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
      </div>

      {/* ── Historique des modifications ──────────────────────────── */}
      {historique.length > 0 && (
        <div className="card" style={{ marginBottom: 16 }}>
          <h3 style={{ marginTop: 0, marginBottom: 16 }}>Historique du prêt</h3>
          <div style={{ position: 'relative', paddingLeft: 28 }}>
            {/* Ligne verticale de la timeline */}
            <div style={{
              position: 'absolute', left: 9, top: 0, bottom: 0,
              width: 2, background: 'var(--border, rgba(255,255,255,.1))',
            }} />

            {[...historique].reverse().map((evt, idx) => {
              const isRestruct = evt.type_evenement === 'restructuration';
              const isCreation = evt.type_evenement === 'creation';
              const dotColor   = isRestruct ? '#f59e0b' : isCreation ? '#22c55e' : 'var(--text-muted)';
              const cardBg     = isRestruct ? 'rgba(245,158,11,0.07)' : isCreation ? 'rgba(34,197,94,0.05)' : 'transparent';
              const cardBorder = isRestruct ? '1px solid rgba(245,158,11,0.3)' : isCreation ? '1px solid rgba(34,197,94,0.2)' : '1px solid var(--border, rgba(255,255,255,.08))';

              const fmtVal = (champ, val) => {
                if (val === null || val === undefined) return '—';
                const typeLabels = { in_fine: 'In fine', amortissable: 'Amortissable', differe: 'Différé' };
                const statutLabels = { en_cours: 'En cours', rembourse: 'Remboursé', en_retard: 'En retard', procedure: 'Procédure', cloture: 'Clôturé' };
                const freqLabels = { mensuel: 'Mensuel', trimestriel: 'Trimestriel', in_fine: 'In fine (unique)' };
                if (champ === 'type_remb')    return typeLabels[val] ?? val;
                if (champ === 'statut')       return statutLabels[val] ?? val;
                if (champ === 'freq_interets') return freqLabels[val] ?? val;
                if (champ === 'taux_interet') return `${val} %`;
                if (champ === 'duree_mois')   return `${val} mois`;
                if (champ === 'montant_investi') return `${Number(val).toLocaleString('fr-FR', { minimumFractionDigits: 2 })} €`;
                if (/^date_/.test(champ))     return fmtDate(val);
                return String(val);
              };

              // Correction fuseau horaire : SQLite datetime('now') stocke en UTC sans 'Z'
              // → on force le parsing UTC pour que toLocaleTimeString convertisse en heure locale
              const createdAtLocal = evt.created_at
                ? new Date(evt.created_at.replace(' ', 'T') + 'Z')
                    .toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })
                : null;

              const isConfirmingDelete = confirmingHistDelete === evt.id;

              return (
                <div key={evt.id} style={{ position: 'relative', marginBottom: idx < historique.length - 1 ? 16 : 0 }}>
                  {/* Point sur la timeline */}
                  <div style={{
                    position: 'absolute', left: -24, top: 14,
                    width: 10, height: 10, borderRadius: '50%',
                    background: dotColor,
                    border: `2px solid var(--bg-card, #1e293b)`,
                    zIndex: 1,
                  }} />

                  <div style={{
                    background: cardBg,
                    border: isConfirmingDelete ? '1px solid rgba(239,68,68,0.5)' : cardBorder,
                    borderRadius: 8,
                    padding: '10px 14px',
                    transition: 'border-color 0.15s',
                  }}>
                    {/* En-tête événement */}
                    <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: isCreation ? 0 : 8 }}>
                      <span style={{ fontWeight: 700, fontSize: 13, color: dotColor }}>
                        {isRestruct ? '⟳ Restructuration' : isCreation ? '✦ Création' : '✎ Modification'}
                      </span>
                      <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>
                        {fmtDate(evt.date_evenement)}
                        {createdAtLocal && <> · {createdAtLocal}</>}
                      </span>
                      {/* Bouton supprimer — sur toutes les entrées sauf la création */}
                      {!isCreation && !isConfirmingDelete && (
                        <button
                          title="Supprimer cette entrée d'historique"
                          onClick={() => setConfirmingHistDelete(evt.id)}
                          style={{
                            marginLeft: 'auto', background: 'transparent', border: 'none',
                            cursor: 'pointer', padding: '2px 6px', borderRadius: 4,
                            color: 'var(--text-muted)', fontSize: 14,
                            lineHeight: 1,
                          }}
                          onMouseEnter={e => e.currentTarget.style.color = '#ef4444'}
                          onMouseLeave={e => e.currentTarget.style.color = 'var(--text-muted)'}
                        >
                          🗑
                        </button>
                      )}
                    </div>

                    {/* Confirmation de suppression inline */}
                    {isConfirmingDelete && (
                      <div style={{
                        display: 'flex', alignItems: 'center', gap: 12,
                        padding: '8px 12px', marginBottom: 10,
                        background: 'rgba(239,68,68,0.1)',
                        border: '1px solid rgba(239,68,68,0.3)',
                        borderRadius: 6,
                      }}>
                        <span style={{ fontSize: 16 }}>⚠️</span>
                        <span style={{ fontSize: 12, flex: 1, color: 'var(--text)', lineHeight: 1.4 }}>
                          Supprimer cet événement de l'historique ?<br />
                          <span style={{ color: 'var(--text-muted)', fontSize: 11 }}>
                            Cette action est irréversible. La simulation du prêt ne sera pas modifiée.
                          </span>
                        </span>
                        <button
                          onClick={() => setConfirmingHistDelete(null)}
                          style={{ fontSize: 12, padding: '4px 10px' }}
                        >
                          Annuler
                        </button>
                        <button
                          className="danger"
                          onClick={() => deleteHist(evt.id)}
                          style={{ fontSize: 12, padding: '4px 10px' }}
                        >
                          Supprimer
                        </button>
                      </div>
                    )}

                    {/* Détail des champs modifiés */}
                    {!isCreation && evt.changements.map((c, ci) => (
                      <div key={ci} style={{
                        display: 'flex', alignItems: 'baseline', gap: 8, flexWrap: 'wrap',
                        fontSize: 12, padding: '3px 0',
                        borderTop: ci > 0 ? '1px solid var(--border, rgba(255,255,255,.06))' : 'none',
                      }}>
                        <span style={{ color: 'var(--text-muted)', minWidth: 160 }}>{c.label}</span>
                        <span style={{
                          background: 'rgba(239,68,68,0.12)', color: '#f87171',
                          borderRadius: 4, padding: '1px 6px',
                          textDecoration: 'line-through', opacity: 0.8,
                        }}>
                          {fmtVal(c.champ, c.ancienne_valeur)}
                        </span>
                        <span style={{ color: 'var(--text-muted)', fontSize: 10 }}>→</span>
                        <span style={{
                          background: 'rgba(34,197,94,0.12)', color: '#4ade80',
                          borderRadius: 4, padding: '1px 6px',
                        }}>
                          {fmtVal(c.champ, c.nouvelle_valeur)}
                        </span>
                      </div>
                    ))}

                    {/* Badge restructuration bien visible */}
                    {isRestruct && !isConfirmingDelete && (
                      <div style={{
                        marginTop: 8, padding: '4px 10px',
                        background: 'rgba(245,158,11,0.15)', borderRadius: 4,
                        fontSize: 11, color: '#fbbf24', fontWeight: 600,
                      }}>
                        Ce prêt a été restructuré — la projection de remboursement a été mise à jour.
                      </div>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* ── Modal édition ─────────────────────────────────────────── */}
      <Modal
        open={modalOpen}
        title="Modifier l'investissement"
        onClose={close}
        width={720}
        footer={
          <div style={{ display: 'flex', alignItems: 'center', width: '100%', gap: 8 }}>
            {/* Zone suppression — à gauche */}
            <div style={{ flex: 1 }}>
              {!confirmingInvDelete ? (
                <button className="danger" onClick={() => setConfirmingInvDelete(true)}>
                  Supprimer
                </button>
              ) : (
                <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <span style={{ fontSize: 13, color: 'var(--danger, #ef4444)', whiteSpace: 'nowrap' }}>
                    Supprimer définitivement ?
                  </span>
                  <button onClick={() => setConfirmingInvDelete(false)}>Non</button>
                  <button className="danger" onClick={() => { onDelete(); }}>Oui, supprimer</button>
                </span>
              )}
            </div>
            {/* Actions standard — à droite */}
            <button onClick={close}>Annuler</button>
            <button className="primary" onClick={submit}>Enregistrer</button>
          </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;
                const platObj = plats.find(p => String(p.id) === String(platId));
                const platMethode = platObj?.methode_remboursement;
                const newMethode = platMethode === 'choix_investisseur' ? '' : (platMethode || '');
                setForm(f => ({ ...f, plateforme_id: platId, methode_remboursement: newMethode, compte_id: '' }));
              }}>
                <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>
            {(() => {
              const platMethode = plats.find(p => String(p.id) === String(form.plateforme_id))?.methode_remboursement;
              if (!platMethode) return null;
              if (platMethode === 'choix_investisseur') return (
                <div>
                  <label>Méthode de remboursement</label>
                  <select value={form.methode_remboursement} onChange={e => {
                    const methode = e.target.value;
                    const defaultCompte = methode === 'compte_courant'
                      ? (comptesInvestisseur.find(c => c.type === 'compte_courant') ?? comptesInvestisseur[0])
                      : null;
                    setForm({ ...form, methode_remboursement: methode, nom_compte_courant: defaultCompte?.nom || '', compte_id: String(defaultCompte?.id || '') });
                  }}>
                    <option value="">— Non renseignée —</option>
                    <option value="portefeuille">Porte-monnaie de la plateforme</option>
                    <option value="compte_courant">Compte courant de l'investisseur</option>
                  </select>
                </div>
              );
              return (
                <div>
                  <label>Méthode de remboursement</label>
                  <input readOnly value={platMethode === 'compte_courant' ? "Compte courant de l'investisseur" : 'Porte-monnaie de la plateforme'}
                    style={{ background: 'var(--surface-2)', color: 'var(--text-muted)', cursor: 'default' }} />
                </div>
              );
            })()}
            {form.methode_remboursement === 'compte_courant' && (
              <div>
                <label>Compte de réception *</label>
                {comptesInvestisseur.length > 0 ? (
                  <select
                    required
                    value={form.compte_id}
                    onChange={e => {
                      const c = comptesInvestisseur.find(c => String(c.id) === e.target.value);
                      setForm({ ...form, compte_id: e.target.value, nom_compte_courant: c?.nom || '' });
                    }}
                  >
                    <option value="">— Choisir un compte —</option>
                    {comptesInvestisseur.map(c => (
                      <option key={c.id} value={c.id}>{c.nom}{c.banque ? ` — ${c.banque}` : ''}</option>
                    ))}
                  </select>
                ) : (
                  <p className="text-muted" style={{ fontSize: 'var(--fs-sm)', margin: '4px 0' }}>
                    Aucun compte défini pour ce détenteur. <a href="/settings?section=comptes" target="_blank" rel="noreferrer">Créer un compte →</a>
                  </p>
                )}
              </div>
            )}
            <div>
              <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: 'span 2' }}>
              <label>Nom du projet *</label>
              <input required value={form.nom_projet} onChange={e => setForm({ ...form, nom_projet: e.target.value })} />
            </div>
            <div>
              <label>Type de prêt</label>
              <select value={form.type_remb} onChange={e => {
                const t = e.target.value;
                const next = {
                  ...form,
                  type_remb: t,
                  freq_interets: t === 'differe' ? 'in_fine' : (form.freq_interets === 'in_fine' ? 'mensuel' : form.freq_interets),
                };
                if (t === 'differe') {
                  if (form.date_premiere_echeance) next.date_cible = form.date_premiere_echeance;
                  else if (form.date_souscription && form.duree_mois) {
                    const dpe = addMonthsFE(form.date_souscription, 1);
                    const cible = addMonthsFE(dpe, Number(form.duree_mois) - 1);
                    next.date_premiere_echeance = cible;
                    next.date_cible = cible;
                  }
                } else {
                  if (form.date_souscription) {
                    next.date_premiere_echeance = addMonthsFE(form.date_souscription, 1);
                    if (next.date_premiere_echeance && form.duree_mois)
                      next.date_cible = addMonthsFE(next.date_premiere_echeance, Number(form.duree_mois) - 1);
                  }
                }
                setForm(next);
              }}>
                <option value="in_fine">Prêt in fine</option>
                <option value="amortissable">Prêt amortissable</option>
                <option value="differe">Prêt différé</option>
              </select>
            </div>
            <div>
              <label>Fréquence intérêts</label>
              <select
                value={form.type_remb === 'differe' ? 'in_fine' : form.freq_interets}
                disabled={form.type_remb === 'differe'}
                onChange={e => setForm({ ...form, freq_interets: e.target.value })}
              >
                {form.type_remb === 'differe' ? (
                  <option value="in_fine">In fine (unique)</option>
                ) : (
                  <>
                    <option value="mensuel">Mensuel</option>
                    <option value="trimestriel">Trimestriel</option>
                  </>
                )}
              </select>
            </div>
            <div>
              <label>Durée (mois)</label>
              <input type="number" min="0" value={form.duree_mois} onChange={e => {
                const duree = e.target.value;
                const next = { ...form, duree_mois: duree };
                if (duree) {
                  if (form.type_remb === 'differe' && form.date_souscription) {
                    const tempDpe = addMonthsFE(form.date_souscription, 1);
                    const cible = addMonthsFE(tempDpe, Number(duree) - 1);
                    next.date_premiere_echeance = cible;
                    next.date_cible = cible;
                  } else if (form.date_premiere_echeance) {
                    next.date_cible = addMonthsFE(form.date_premiere_echeance, Number(duree) - 1);
                  }
                }
                setForm(next);
              }} />
            </div>
            <div>
              <label>Taux annuel (%)</label>
              <input type="number" step="0.01" value={form.taux_interet} onChange={e => setForm({ ...form, taux_interet: e.target.value })} />
            </div>
            <div>
              <label>Montant investi (€) *</label>
              <input type="number" step="0.01" min="0" required value={form.montant_investi} onChange={e => setForm({ ...form, montant_investi: e.target.value })} />
            </div>
            <div>
              <label>Date souscription *</label>
              <input type="date" required value={form.date_souscription} onChange={e => {
                const souscription = e.target.value;
                setForm(prev => {
                  const next = { ...prev, date_souscription: souscription };
                  if (!souscription || souscription.length !== 10) return next;
                  if (prev.type_remb === 'differe') {
                    if (prev.duree_mois) {
                      const tempDpe = addMonthsFE(souscription, 1);
                      const cible = addMonthsFE(tempDpe, Number(prev.duree_mois) - 1);
                      next.date_premiere_echeance = cible;
                      next.date_cible = cible;
                    }
                  } else {
                    // Pour in_fine / amortissable : recalcul systématique de premiere_echeance
                    // (cohérent avec le comportement de "differe" et évite les faux négatifs
                    // du test isAutoCalc quand premiere_echeance ne vaut pas exactement souscription+1m)
                    next.date_premiere_echeance = addMonthsFE(souscription, 1);
                    if (next.date_premiere_echeance && prev.duree_mois)
                      next.date_cible = addMonthsFE(next.date_premiere_echeance, Number(prev.duree_mois) - 1);
                  }
                  return next;
                });
                // Mémorise la dernière souscription complète HORS setForm (évite les doubles appels Strict Mode)
                if (souscription && souscription.length === 10) {
                  lastValidSouscriptionRef.current = souscription;
                }
              }} />
            </div>
            <div>
              <label>Date 1ère échéance</label>
              <input
                type="date"
                value={form.date_premiere_echeance}
                onChange={e => {
                  const dpe = e.target.value;
                  const next = { ...form, date_premiere_echeance: dpe };
                  if (dayOfMonth(dpe) <= 27) next.echeance_fin_de_mois = false;
                  if (form.type_remb === 'differe') {
                    next.date_cible = dpe;
                  } else if (dpe && form.duree_mois) {
                    next.date_cible = next.echeance_fin_de_mois
                      ? addMonthsFinDeMoisFE(dpe, Number(form.duree_mois) - 1)
                      : addMonthsFE(dpe, Number(form.duree_mois) - 1);
                  }
                  setForm(next);
                }}
              />
              {/* ── Case "Dernier jour du mois" — visible si jour > 27 ── */}
              {(form.type_remb === 'in_fine' || form.type_remb === 'differe') &&
               dayOfMonth(form.date_premiere_echeance) > 27 && (
                <label style={{
                  display: 'inline-flex', alignItems: 'center', gap: 8,
                  marginTop: 6, cursor: 'pointer', userSelect: 'none',
                  fontSize: 'var(--fs-sm)', color: 'var(--text)',
                  width: 'fit-content',
                }}>
                  <input
                    type="checkbox"
                    style={{ width: 'auto', flexShrink: 0 }}
                    checked={form.echeance_fin_de_mois}
                    onChange={e => {
                      const checked = e.target.checked;
                      setForm(prev => {
                        const next = { ...prev, echeance_fin_de_mois: checked };
                        if (checked && prev.date_premiere_echeance) {
                          const newDpe = lastDayOfMonthFE(prev.date_premiere_echeance);
                          next.date_premiere_echeance = newDpe;
                          if (prev.type_remb === 'differe') {
                            next.date_cible = newDpe;
                          } else if (newDpe && prev.duree_mois) {
                            next.date_cible = addMonthsFinDeMoisFE(newDpe, Number(prev.duree_mois) - 1);
                          }
                        }
                        return next;
                      });
                    }}
                  />
                  Dernier jour du mois
                </label>
              )}
            </div>
            <div>
              <label>Date cible contractuelle{form.type_remb === 'differe' ? ' — calculée' : ''}</label>
              <input
                type="date"
                value={form.date_cible}
                readOnly={form.type_remb === 'differe'}
                style={form.type_remb === 'differe' ? { opacity: 0.6, cursor: 'default' } : undefined}
                onChange={e => {
                  if (form.type_remb === 'differe') return;
                  setForm({ ...form, date_cible: e.target.value });
                }}
              />
            </div>
            {/* Champ restructuration : visible uniquement si des remboursements existent
                ET que le type de prêt a été modifié (ou qu'une date était déjà définie) */}
            {remb.length > 0 && (form.type_remb !== inv.type_remb || !!inv.date_debut_simul) && (
              <div style={{ gridColumn: '1 / -1' }}>
                <div style={{
                  borderLeft: '3px solid var(--warning, #f59e0b)',
                  background: 'rgba(245,158,11,0.08)',
                  borderRadius: '0 6px 6px 0',
                  padding: '10px 14px',
                  marginBottom: 2,
                }}>
                  <label style={{ color: 'var(--warning, #f59e0b)', fontWeight: 600, marginBottom: 4 }}>
                    ⟳ Restructuration — Début de la nouvelle simulation
                  </label>
                  <input
                    type="date"
                    value={form.date_debut_simul}
                    min={form.date_premiere_echeance || undefined}
                    max={form.date_cible || undefined}
                    onChange={e => setForm({ ...form, date_debut_simul: e.target.value })}
                  />
                  <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 6, lineHeight: 1.5 }}>
                    La projection démarrera à cette date avec le nouveau type de prêt. La durée restante
                    sera calculée automatiquement à partir de la date de 1ère échéance d'origine.
                    {form.date_debut_simul && form.date_premiere_echeance && form.duree_mois && (() => {
                      const [y1, m1] = form.date_premiere_echeance.split('-').map(Number);
                      const [y2, m2] = form.date_debut_simul.split('-').map(Number);
                      const elapsed  = (y2 - y1) * 12 + (m2 - m1);
                      const restant  = Math.max(1, Number(form.duree_mois) - elapsed);
                      return elapsed > 0
                        ? <strong style={{ display: 'block', marginTop: 4 }}>
                            → {elapsed} mois écoulés · {restant} mois simulés
                          </strong>
                        : null;
                    })()}
                  </div>
                  {form.date_debut_simul && (
                    <button
                      type="button"
                      style={{ marginTop: 8, fontSize: 11, padding: '2px 10px' }}
                      onClick={() => setForm({ ...form, date_debut_simul: '' })}
                    >
                      Effacer la restructuration
                    </button>
                  )}
                </div>
              </div>
            )}
            <div>
              <label>Statut</label>
              <select value={form.statut} onChange={e => setForm({ ...form, statut: e.target.value })}>
                <option value="en_cours">En cours</option>
                <option value="rembourse">Remboursé</option>
                <option value="en_retard">En retard</option>
                <option value="procedure">Procédure</option>
                <option value="cloture">Clôturé</option>
              </select>
            </div>
            <div>
              <label>Référence (ID plateforme)</label>
              <input value={form.reference} onChange={e => setForm({ ...form, reference: e.target.value })} />
            </div>
            <div>
              <label>Pays d'exposition</label>
              <CountrySelect
                value={form.pays_exposition || 'FR'}
                onChange={code => setForm(f => ({ ...f, pays_exposition: code }))}
              />
            </div>
            <div>
              <label>Émetteur</label>
              <input value={form.emetteur} onChange={e => setForm({ ...form, emetteur: e.target.value })} />
            </div>
            <div style={{ gridColumn: '1 / -1' }}>
              <label>Notes</label>
              <textarea rows={2} value={form.notes} onChange={e => setForm({ ...form, notes: e.target.value })} />
            </div>
          </div>
        </form>
      </Modal>

      {/* ── Modal saisie remboursement (depuis projection) ────────── */}
      {(() => {
        const rates       = getRatesForYear(rembForm.date_remb);
        const fmtRate     = n => n != null ? n.toFixed(1).replace('.', ',') + ' %' : '…';
        const labelPS     = `Prélèvements sociaux${rates ? ` (${fmtRate(rates.prelev_sociaux)})` : ''} (€)`;
        const currentPlat    = plats.find(p => p.id === inv?.plateforme_id);
        const isChoixOuvert  = currentPlat?.methode_remboursement === 'choix_investisseur';
        const hasLocalTax    = currentPlat?.fiscalite === 'avec_fiscalite_locale' && currentPlat?.taux_fiscalite_locale;
        const labelIR     = `Impôt sur le revenu${rates  ? ` (${fmtRate(rates.impot_revenu)})`   : ''} (€)`;
        const interetsNets     = computeInteretsNets(rembForm);
        const netRecu          = computeNet(rembForm);
        const isExonere        = inv?.fiscalite_override === 'exonere';
        // Indicatif = plateforme étrangère (non flat_tax) OU investissement exonéré PEA-PME.
        // Dans ce cas, les prélèvements sont affichés en lecture seule et le montant versé
        // ne les déduit pas (l'investisseur reçoit le brut intégral).
        const isIndicatif      = isExonere || currentPlat?.fiscalite !== 'flat_tax';
        const montantRembourse = round2((Number(rembForm.capital)||0) + (Number(rembForm.cashback)||0) + (Number(rembForm.interets_bruts)||0));
        return (
          <Modal
            open={rembModalOpen}
            title={editingRembId ? 'Modifier le remboursement' : 'Saisir le remboursement'}
            onClose={closeRembModal}
            width={900}
            footer={
              <div style={{ display: 'flex', width: '100%', justifyContent: 'space-between', alignItems: 'center' }}>
                {editingRembId && !confirmingRembDelete && (
                  <button className="danger" onClick={() => setConfirmingRembDelete(true)}>
                    Supprimer
                  </button>
                )}
                {confirmingRembDelete && (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                    <span style={{ fontSize: 13, color: 'var(--danger, #ef4444)' }}>
                      Supprimer ce remboursement ?
                    </span>
                    <button onClick={() => setConfirmingRembDelete(false)}>Non</button>
                    <button className="danger" onClick={deleteRemb}>Oui, supprimer</button>
                  </div>
                )}
                {!confirmingRembDelete && (
                  <div style={{ display: 'flex', gap: 8, marginLeft: 'auto' }}>
                    <button onClick={closeRembModal}>Annuler</button>
                    <button className="primary" onClick={submitRemb}>Enregistrer</button>
                  </div>
                )}
              </div>
            }
          >
            <form onSubmit={submitRemb}>
              {rembErr && <div className="error">{rembErr}</div>}
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '12px 16px' }}>
                <div style={{ gridColumn: 'span 3', borderTop: '1px solid var(--border)', paddingTop: 4, marginTop: 4 }}>
                  <span style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--text-muted)' }}>Remboursement</span>
                </div>
                {hasLocalTax && (
                  <div style={{ gridColumn: 'span 2' }}>
                    <label>Intérêts bruts avant retenue locale ({currentPlat.taux_fiscalite_locale} %) (€)</label>
                    <input type="number" step="0.01" value={rembForm.interets_bruts_avant_local ?? 0}
                      onChange={e => setRembField('interets_bruts_avant_local', e.target.value)} />
                  </div>
                )}
                {hasLocalTax && (
                  <div>
                    <label>Retenue à la source locale (€)</label>
                    <input type="number" step="0.01" value={rembForm.taxe_locale ?? 0}
                      onChange={e => setRembField('taxe_locale', e.target.value)} />
                  </div>
                )}
                <div>
                  <label>Capital (€)</label>
                  <input type="number" step="0.01" value={rembForm.capital ?? 0}
                    onChange={e => setRembField('capital', e.target.value)} />
                </div>
                <div>
                  <label>
                    {hasLocalTax ? 'Intérêts bruts après retenue locale (€)' : 'Intérêts bruts (€)'}
                  </label>
                  <input type="number" step="0.01" value={rembForm.interets_bruts ?? 0}
                    readOnly={!!hasLocalTax}
                    style={hasLocalTax ? { background: 'var(--surface-2)', color: 'var(--text-muted)', cursor: 'not-allowed' } : undefined}
                    onChange={e => !hasLocalTax && setRembField('interets_bruts', e.target.value)} />
                </div>
                <div>
                  <label>Cashback (€)</label>
                  <input type="number" step="0.01" value={rembForm.cashback ?? 0}
                    onChange={e => setRembField('cashback', e.target.value)} />
                </div>
                <div style={{ gridColumn: 'span 3', borderTop: '1px solid var(--border)', paddingTop: 4, marginTop: 4 }}>
                  <span style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--text-muted)' }}>Imposition{isIndicatif ? ' — indicatif' : ''}</span>
                </div>
                <div>
                  <label>{labelPS}</label>
                  <input type="number" step="0.01" value={rembForm.prelev_sociaux ?? 0}
                    readOnly={isIndicatif}
                    style={isIndicatif ? { background: 'var(--surface-2)', color: 'var(--text-muted)', cursor: 'not-allowed' } : undefined}
                    onChange={e => !isIndicatif && setRembField('prelev_sociaux', e.target.value)} />
                </div>
                <div>
                  <label>{labelIR}</label>
                  <input type="number" step="0.01" value={rembForm.prelev_forfaitaire ?? 0}
                    readOnly={isIndicatif}
                    style={isIndicatif ? { background: 'var(--surface-2)', color: 'var(--text-muted)', cursor: 'not-allowed' } : undefined}
                    onChange={e => !isIndicatif && setRembField('prelev_forfaitaire', e.target.value)} />
                </div>
                <div>
                  <label>Total prélèvements (€)</label>
                  <input type="number" step="0.01"
                    value={round2((Number(rembForm.prelev_sociaux) || 0) + (Number(rembForm.prelev_forfaitaire) || 0))}
                    readOnly
                    style={{ background: 'var(--surface-2)', color: 'var(--text-muted)', cursor: 'not-allowed' }} />
                </div>
                <div style={{ gridColumn: 'span 3', display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>
                  <span style={{ color: 'var(--text-muted)' }}>Montant des intérêts après imposition</span>
                  <span style={{ fontWeight: 600, color: 'var(--text)' }}>{fmtEUR(interetsNets)}</span>
                </div>
                <div style={{ gridColumn: 'span 3', borderTop: '1px solid var(--border)', paddingTop: 4, marginTop: 4 }}>
                  <span style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--text-muted)' }}>Versement</span>
                </div>
                <div>
                  <label>Date *</label>
                  <input type="date" required value={rembForm.date_remb || ''}
                    onChange={e => setRembField('date_remb', e.target.value)} />
                </div>
                <div>
                  <label>Méthode de remboursement</label>
                  <select value={rembForm.methode_remboursement || 'portefeuille'}
                    onChange={e => {
                      const m = e.target.value;
                      const def = m === 'compte_courant' ? (comptesRembInvestisseur.find(c => c.type === 'compte_courant') ?? comptesRembInvestisseur[0]) : null;
                      setRembForm(f => ({ ...f, methode_remboursement: m, compte_id: def ? String(def.id) : '' }));
                    }}>
                    <option value="portefeuille">Porte-monnaie de la plateforme</option>
                    <option value="compte_courant">Compte courant de l'investisseur</option>
                  </select>
                </div>
                {rembForm.methode_remboursement === 'compte_courant' && (
                  <div>
                    <label>Compte de réception</label>
                    {comptesRembInvestisseur.length > 0 ? (
                      <select value={rembForm.compte_id || ''}
                        onChange={e => setRembForm(f => ({ ...f, compte_id: e.target.value }))}>
                        <option value="">— Choisir un compte —</option>
                        {comptesRembInvestisseur.map(c => (
                          <option key={c.id} value={c.id}>{c.nom}{c.banque ? ` — ${c.banque}` : ''}</option>
                        ))}
                      </select>
                    ) : (
                      <p className="text-muted" style={{ fontSize: 'var(--fs-sm)', margin: '4px 0' }}>
                        Aucun compte défini. <a href="/settings?section=comptes" target="_blank" rel="noreferrer">Créer un compte →</a>
                      </p>
                    )}
                  </div>
                )}
                <div>
                  <label>Montant Versé (€)</label>
                  <input type="number" step="0.01" value={montantRembourse} readOnly
                    style={{ background: 'var(--bg-input-readonly, rgba(255,255,255,.05))', cursor: 'default' }} />
                </div>
                <div style={{ gridColumn: 'span 3', display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>
                  <span style={{ color: 'var(--text-muted)' }}>
                    {isIndicatif
                      ? "L'imposition n'est pas déduite du montant versé (fiscalité appliquée à titre indicatif)."
                      : "L'imposition est déduite du montant versé (capital + cashback + intérêts nets)."}
                  </span>
                </div>
              </div>
            </form>
          </Modal>
        );
      })()}

      {/* ── Modal réinvestissement ────────────────────────────────── */}
      <Modal
        open={reinvModalOpen}
        title="Ajouter un réinvestissement"
        onClose={closeReinvModal}
        width={480}
        footer={
          reinvTab === 'manuel' ? (
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
              <button onClick={closeReinvModal}>Annuler</button>
              <button className="primary" onClick={submitReinv}>Enregistrer</button>
            </div>
          ) : (
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
              <button onClick={closeReinvModal}>Annuler</button>
              {!autoReinvActive ? (
                <button className="primary" onClick={async () => {
                  await api.put(`/investissements/${id}/auto-reinvest`, { active: true });
                  await load();
                  closeReinvModal();
                }}>Activer</button>
              ) : (
                <button className="danger" onClick={async () => {
                  await api.put(`/investissements/${id}/auto-reinvest`, { active: false });
                  await load();
                  closeReinvModal();
                }}>Désactiver</button>
              )}
            </div>
          )
        }
      >
        {/* Sélecteur Manuel / Automatique */}
        <div style={{ display: 'flex', gap: 2, background: 'var(--surface-2)', borderRadius: 8, padding: 3, marginBottom: 20 }}>
          {['manuel', 'auto'].map(tab => (
            <button key={tab}
              onClick={() => setReinvTab(tab)}
              style={{
                flex: 1, padding: '6px 0', borderRadius: 6, border: 'none', cursor: 'pointer', fontSize: 13, fontWeight: 500, transition: 'all .15s',
                background: reinvTab === tab ? 'var(--surface)' : 'transparent',
                color: reinvTab === tab ? 'var(--text)' : 'var(--text-muted)',
                boxShadow: reinvTab === tab ? '0 1px 4px rgba(0,0,0,.1)' : 'none',
              }}>
              {tab === 'manuel' ? 'Manuel' : 'Automatique'}
            </button>
          ))}
        </div>

        {reinvTab === 'manuel' ? (
          <form onSubmit={submitReinv}>
            <p className="text-muted" style={{ marginTop: 0, marginBottom: 16, fontSize: 13 }}>
              Un réinvestissement ajoute du capital à ce prêt à une date donnée. La projection
              des intérêts sera recalculée à partir de cette date.
            </p>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 12 }}>
              <div>
                <label style={{ display: 'block', marginBottom: 4, fontSize: 12, color: 'var(--text-muted)' }}>
                  Montant réinvesti (€) *
                </label>
                <input
                  type="number" step="0.01" min="0.01" required
                  value={reinvForm.montant}
                  onChange={e => setReinvForm(f => ({ ...f, montant: e.target.value }))}
                  placeholder="ex. 500"
                  style={{ width: '100%' }}
                />
              </div>
              <div>
                <label style={{ display: 'block', marginBottom: 4, fontSize: 12, color: 'var(--text-muted)' }}>
                  Date du réinvestissement *
                </label>
                <input
                  type="date" required
                  value={reinvForm.date_reinvestissement}
                  onChange={e => setReinvForm(f => ({ ...f, date_reinvestissement: e.target.value }))}
                  style={{ width: '100%' }}
                />
              </div>
            </div>
            <div style={{ marginBottom: 12 }}>
              <label style={{ display: 'block', marginBottom: 4, fontSize: 12, color: 'var(--text-muted)' }}>
                Note (optionnel)
              </label>
              <input
                type="text"
                value={reinvForm.note}
                onChange={e => setReinvForm(f => ({ ...f, note: e.target.value }))}
                placeholder="ex. Abondement contrat, tranche 2…"
                style={{ width: '100%' }}
              />
            </div>
            {reinvErr && <div className="error">{reinvErr}</div>}
          </form>
        ) : (
          <div style={{ padding: '8px 0' }}>
            {autoReinvActive ? (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '10px 14px', borderRadius: 8, background: 'color-mix(in srgb, var(--success) 10%, transparent)', border: '1px solid color-mix(in srgb, var(--success) 25%, transparent)' }}>
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--success)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                  <span style={{ fontSize: 13, color: 'var(--success)', fontWeight: 600 }}>Réinvestissement automatique activé</span>
                </div>
                <p style={{ margin: 0, fontSize: 13, color: 'var(--text-muted)', lineHeight: 1.6 }}>
                  Après chaque remboursement, les intérêts {inv?.plateforme_fiscalite === 'flat_tax' ? 'nets (prélevés à la source)' : 'bruts'} non nuls sont automatiquement réinvestis sur ce prêt.
                </p>
              </div>
            ) : (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
                <p style={{ margin: 0, fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.6 }}>
                  Après chaque remboursement, les intérêts {inv?.plateforme_fiscalite === 'flat_tax' ? 'nets (prélevés à la source)' : 'bruts'} non nuls seront automatiquement réinvestis et intégrés au capital de ce prêt.
                </p>
              </div>
            )}
          </div>
        )}
      </Modal>

      {/* ── Modale modification date 1ère échéance ────────────────── */}
      <Modal
        open={editDpeModal}
        title="Modifier la date de la 1ère échéance"
        onClose={() => setEditDpeModal(false)}
        width={420}
        footer={
          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
            <button onClick={() => setEditDpeModal(false)}>Annuler</button>
            <button className="primary" onClick={saveEditDpe} disabled={editDpeSaving}>
              {editDpeSaving ? 'Enregistrement…' : 'Enregistrer'}
            </button>
          </div>
        }
      >
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          {editDpeErr && <div className="error">{editDpeErr}</div>}
          <div>
            <label style={{ display: 'block', marginBottom: 4 }}>Date de la 1ère échéance</label>
            <input
              type="date"
              value={editDpeValue}
              onChange={e => setEditDpeValue(e.target.value)}
              style={{ width: '100%' }}
            />
          </div>
          <p style={{ margin: 0, fontSize: 13, color: 'var(--text-muted)', lineHeight: 1.6 }}>
            L'échéancier sera recalculé à partir de cette date. Les remboursements déjà enregistrés ne sont pas modifiés.
          </p>
        </div>
      </Modal>

      {/* ── Modale traitement en masse des remboursements ─────────── */}
      <Modal
        open={bulkRembModal}
        title="Traitement en masse des remboursements"
        onClose={() => { if (!bulkRembProcessing) setBulkRembModal(false); }}
        width={560}
        footer={
          bulkRembDone ? (
            <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
              <button className="primary" onClick={() => setBulkRembModal(false)}>Fermer</button>
            </div>
          ) : (
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
              <button onClick={() => setBulkRembModal(false)} disabled={bulkRembProcessing}>Annuler</button>
              <button className="primary" onClick={runBulkRemb} disabled={bulkRembProcessing || bulkRembItems.length === 0}>
                {bulkRembProcessing ? `Traitement… (${bulkRembProgress}/${bulkRembItems.length})` : `Enregistrer ${bulkRembItems.length} remboursement${bulkRembItems.length > 1 ? 's' : ''}`}
              </button>
            </div>
          )
        }
      >
        {bulkRembDone ? (
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0' }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--success)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
            <span style={{ fontSize: 14, color: 'var(--success)', fontWeight: 600 }}>
              {bulkRembItems.length} remboursement{bulkRembItems.length > 1 ? 's' : ''} enregistré{bulkRembItems.length > 1 ? 's' : ''} avec succès.
            </span>
          </div>
        ) : bulkRembItems.length === 0 ? (
          <p style={{ margin: 0, fontSize: 13, color: 'var(--text-muted)' }}>
            Aucune échéance passée non enregistrée trouvée.
          </p>
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            <p style={{ margin: 0, fontSize: 13, color: 'var(--text-muted)', lineHeight: 1.6 }}>
              {bulkRembItems.length} échéance{bulkRembItems.length > 1 ? 's' : ''} passée{bulkRembItems.length > 1 ? 's' : ''} sans remboursement enregistré. Chaque entrée sera créée avec les montants projetés.
            </p>
            <div style={{ maxHeight: 260, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 6 }}>
              <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
                <thead>
                  <tr style={{ background: 'var(--surface-2)' }}>
                    <th style={{ padding: '6px 10px', textAlign: 'left', fontWeight: 600 }}>Date</th>
                    <th style={{ padding: '6px 10px', textAlign: 'right', fontWeight: 600 }}>Capital</th>
                    <th style={{ padding: '6px 10px', textAlign: 'right', fontWeight: 600 }}>Intérêts bruts</th>
                    <th style={{ padding: '6px 10px', textAlign: 'right', fontWeight: 600 }}>Total</th>
                  </tr>
                </thead>
                <tbody>
                  {bulkRembItems.map((item, idx) => (
                    <tr key={idx} style={{ borderTop: '1px solid var(--border)' }}>
                      <td style={{ padding: '6px 10px' }}>{fmtDate(item.payload.date_remb)}</td>
                      <td style={{ padding: '6px 10px', textAlign: 'right' }}>{fmtEUR(item.payload.capital)}</td>
                      <td style={{ padding: '6px 10px', textAlign: 'right' }}>{fmtEUR(item.payload.interets_bruts)}</td>
                      <td style={{ padding: '6px 10px', textAlign: 'right', fontWeight: 600 }}>{fmtEUR(item.payload.capital + item.payload.interets_bruts)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        )}
      </Modal>

      {simulMenu && (
        <>
          <div style={{ position: 'fixed', inset: 0, zIndex: 299 }} onClick={() => setSimulMenu(null)} />
          <div style={{
            position: 'fixed', left: simulMenu.x, top: simulMenu.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: 200,
          }}>
            <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={() => { setSimulMenu(null); setEditDpeValue(inv.date_premiere_echeance || ''); setEditDpeErr(null); setEditDpeModal(true); }}>
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>
              </svg>
              Modifier la date de la 1ère échéance
            </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(--text)', textAlign: 'left' }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
              onMouseLeave={e => e.currentTarget.style.background = 'none'}
              onClick={() => { setSimulMenu(null); openBulkRembModal(); }}>
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/>
                <circle cx="3" cy="6" r="1" fill="currentColor"/><circle cx="3" cy="12" r="1" fill="currentColor"/><circle cx="3" cy="18" r="1" fill="currentColor"/>
              </svg>
              Traitement en masse des remboursements
            </button>
            {inv.taux_interet && inv.duree_mois ? (
              <button
                style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '8px 14px', background: 'none', border: 'none', cursor: recalculating ? 'not-allowed' : 'pointer', fontSize: 'var(--fs-sm)', color: recalculating ? 'var(--text-muted)' : 'var(--text)', textAlign: 'left', opacity: recalculating ? 0.6 : 1 }}
                onMouseEnter={e => { if (!recalculating) e.currentTarget.style.background = 'var(--surface-2)'; }}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}
                disabled={recalculating}
                onClick={() => { setSimulMenu(null); recalculateSchedule(); }}>
                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
                  style={{ animation: recalculating ? 'spin 1s linear infinite' : 'none' }}>
                  <path d="M23 4v6h-6"/><path d="M1 20v-6h6"/>
                  <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10"/>
                  <path d="M20.49 15a9 9 0 0 1-14.85 3.36L1 14"/>
                </svg>
                {recalculating ? 'Régénération…' : "Régénérer l'échéancier"}
              </button>
            ) : (
              <div style={{ padding: '8px 14px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)', fontStyle: 'italic' }}>
                Taux et durée requis pour régénérer
              </div>
            )}
          </div>
        </>
      )}
      {reinvMenu && (
        <>
          <div style={{ position: 'fixed', inset: 0, zIndex: 299 }} onClick={() => setReinvMenu(null)} />
          <div style={{
            position: 'fixed', left: reinvMenu.x, top: reinvMenu.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: 220,
          }}>
            <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={() => { setReinvMenu(null); openReinvModal(); }}>
              <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="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
              Ajouter un réinvestissement
            </button>
            {reinvs.length > 0 && (
              <>
                <div style={{ height: 1, background: 'var(--border)', margin: '4px 0' }} />
                <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={() => {
                    setReinvMenu(null);
                    setRowDeleteConfirm({
                      message: `Supprimer les ${reinvs.length} réinvestissement${reinvs.length > 1 ? 's' : ''} de ce projet ? Cette action est irréversible.`,
                      onConfirm: async () => {
                        await Promise.all(reinvs.map(r => api.del(`/reinvestissements/${r.id}`)));
                        setRowDeleteConfirm(null);
                        await load();
                      },
                    });
                  }}>
                  <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 tous les réinvestissements
                </button>
              </>
            )}
          </div>
        </>
      )}
      {rembMenu && (
        <>
          <div style={{ position: 'fixed', inset: 0, zIndex: 299 }} onClick={() => setRembMenu(null)} />
          <div style={{
            position: 'fixed', left: rembMenu.x, top: rembMenu.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: 200,
          }}>
            <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={() => { setRembMenu(null); openNewRemb(); }}>
              <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="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
              Ajouter un remboursement
            </button>
            {remb.length > 0 && (
              <>
                <div style={{ height: 1, background: 'var(--border)', margin: '4px 0' }} />
                <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={() => {
                    setRembMenu(null);
                    setRowDeleteConfirm({
                      message: `Supprimer les ${remb.length} remboursement${remb.length > 1 ? 's' : ''} de ce projet ? Cette action est irréversible.`,
                      onConfirm: async () => {
                        await api.del(`/remboursements?investissement_id=${id}`);
                        setRowDeleteConfirm(null);
                        await load();
                      },
                    });
                  }}>
                  <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 tous les remboursements
                </button>
              </>
            )}
          </div>
        </>
      )}
      {fiscaliteOverrideConfirm && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 400, background: 'rgba(0,0,0,0.4)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ background: 'var(--surface)', borderRadius: 10, padding: '28px 32px', maxWidth: 440, width: '90%', boxShadow: '0 8px 32px rgba(0,0,0,0.2)' }}>
            <h3 style={{ margin: '0 0 12px' }}>
              {inv?.fiscalite_override === 'exonere' ? 'Rétablir la flat tax' : 'Exonérer de la flat tax'}
            </h3>
            <p style={{ margin: '0 0 20px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)', lineHeight: 1.6 }}>
              {inv?.fiscalite_override === 'exonere'
                ? 'Les prochains remboursements appliqueront à nouveau les prélèvements flat tax selon les taux PFU. Les remboursements existants ne sont pas modifiés.'
                : 'Les prochains remboursements seront traités sans prélèvements automatiques (comme une plateforme étrangère hors flat tax). Les remboursements existants ne sont pas modifiés.'}
            </p>
            <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
              <button onClick={() => setFiscaliteOverrideConfirm(false)}>Annuler</button>
              <button className="primary" onClick={async () => {
                setFiscaliteOverrideConfirm(false);
                const newOverride = inv?.fiscalite_override === 'exonere' ? null : 'exonere';
                await api.put(`/investissements/${id}/fiscalite-override`, { override: newOverride });
                await load();
              }}>Confirmer</button>
            </div>
          </div>
        </div>
      )}
      {cardMenu && (
        <>
          <div style={{ position: 'fixed', inset: 0, zIndex: 299 }} onClick={() => setCardMenu(null)} />
          <div style={{
            position: 'fixed', left: cardMenu.x, top: cardMenu.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: 160,
          }}>
            {[
              { label: 'Modifier',     icon: <><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"/></>, action: () => { setCardMenu(null); openEdit(); } },
              { label: 'Réinvestir',   icon: <><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></>, action: () => { setCardMenu(null); openReinvModal(); } },
              { label: 'Exporter',     icon: <><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"/></>, action: () => { setCardMenu(null); exportDossier(); } },
            ].map(({ label, icon, action }) => (
              <button key={label}
                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={() => action()}>
                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">{icon}</svg>
                {label}
              </button>
            ))}
            <div style={{ height: 1, background: 'var(--border)', margin: '4px 0' }} />
            {autoReinvActive && (
              <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-muted)', textAlign: 'left' }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}
                onClick={async () => {
                  setCardMenu(null);
                  await api.put(`/investissements/${id}/auto-reinvest`, { active: false });
                  await load();
                }}>
                <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="8" y1="12" x2="16" y2="12"/></svg>
                Désactiver le réinvestissement auto
              </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={() => {
                setCardMenu(null);
                setRowDeleteConfirm({
                  message: `Supprimer l'investissement "${inv?.nom_projet}" ? Cette action est irréversible.`,
                  onConfirm: async () => {
                    await api.del(`/investissements/${id}`);
                    setRowDeleteConfirm(null);
                    navigate('/investissements');
                  },
                });
              }}>
              <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>
        </>
      )}

      <ConfirmModal
        open={!!rowDeleteConfirm}
        message={rowDeleteConfirm?.message}
        onConfirm={rowDeleteConfirm?.onConfirm}
        onCancel={() => setRowDeleteConfirm(null)}
      />
    </>
  );
}
