import Database from 'better-sqlite3';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { generateSimul } from '../utils/schedule.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const DB_PATH = process.env.DB_PATH || path.resolve(__dirname, '../../data/crowdlending.db');

// Ensure data directory exists
fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });

const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');

// ── CLEANUP PRIORITAIRE : tables __repair_* orphelines ───────────────────
// Si un repair précédent a créé des tables __repair_X mais n'a pas pu
// finaliser le RENAME (interruption, crash), on le finit ici, avant tout.
{
  const repairTables = db.prepare(
    "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '__repair_%'"
  ).all().map(r => r.name);

  if (repairTables.length > 0) {
    db.exec('PRAGMA foreign_keys = OFF');

    // SQLite valide toutes les vues lors d'un RENAME TABLE.
    // Les vues cassées (qui référencent _investissements_old ou des tables
    // provisoirement absentes) doivent être supprimées avant les renommages.
    db.exec('DROP VIEW IF EXISTS v_interets_annuels');
    db.exec('DROP VIEW IF EXISTS v_synthese_inv');

    for (const repairName of repairTables) {
      const originalName = repairName.slice('__repair_'.length);
      const originalExists = db.prepare(
        "SELECT COUNT(*) AS n FROM sqlite_master WHERE type='table' AND name=?"
      ).get(originalName).n > 0;
      if (originalExists) {
        // Les deux coexistent : l'original est vide, le repair a les données.
        const idxs = db.prepare(
          "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name=? AND sql IS NOT NULL"
        ).all(originalName);
        for (const idx of idxs) db.exec(`DROP INDEX IF EXISTS "${idx.name}"`);
        db.exec(`DROP TABLE "${originalName}"`);
      }
      db.exec(`ALTER TABLE "${repairName}" RENAME TO "${originalName}"`);
    }

    // Recréer les index (idempotent IF NOT EXISTS)
    const knownIndexes = [
      'CREATE INDEX IF NOT EXISTS idx_remb_inv    ON remboursements(investissement_id)',
      'CREATE INDEX IF NOT EXISTS idx_remb_date   ON remboursements(date_remb)',
      'CREATE INDEX IF NOT EXISTS idx_remb_statut ON remboursements(statut)',
      'CREATE INDEX IF NOT EXISTS idx_simul_inv   ON simul_remboursements(investissement_id)',
      'CREATE INDEX IF NOT EXISTS idx_simul_date  ON simul_remboursements(date_prevue)',
      'CREATE INDEX IF NOT EXISTS idx_inv_inv     ON investissements(investisseur_id)',
      'CREATE INDEX IF NOT EXISTS idx_inv_plat    ON investissements(plateforme_id)',
      'CREATE INDEX IF NOT EXISTS idx_inv_statut  ON investissements(statut)',
      'CREATE INDEX IF NOT EXISTS idx_inv_date    ON investissements(date_souscription)',
    ];
    for (const sql of knownIndexes) db.exec(sql);

    // Recréer les vues avec les références correctes
    db.exec(`CREATE VIEW IF NOT EXISTS v_interets_annuels AS
      SELECT
        i.investisseur_id,
        substr(r.date_remb,1,4)                AS annee,
        SUM(r.interets_bruts)                  AS interets_bruts,
        SUM(r.prelev_sociaux)                  AS prelev_sociaux,
        SUM(r.prelev_forfaitaire)              AS prelev_forfaitaire,
        SUM(r.net_recu)                        AS net_recu
      FROM remboursements r
      JOIN investissements i ON i.id = r.investissement_id
      WHERE r.statut IN ('paye','partiel')
      GROUP BY i.investisseur_id, substr(r.date_remb,1,4)`);

    db.exec(`CREATE VIEW IF NOT EXISTS v_synthese_inv AS
      SELECT
        i.investisseur_id,
        COUNT(*)                                                             AS nb_projets,
        SUM(i.montant_investi)                                               AS total_investi,
        SUM(CASE WHEN i.statut='en_cours'  THEN i.montant_investi ELSE 0 END) AS encours,
        SUM(CASE WHEN i.statut='rembourse' THEN i.montant_investi ELSE 0 END) AS rembourse,
        SUM(CASE WHEN i.statut IN ('en_retard','procedure') THEN i.montant_investi ELSE 0 END) AS en_defaut
      FROM investissements i
      GROUP BY i.investisseur_id`);

    db.exec('PRAGMA foreign_keys = ON');
  }
}

// Apply schema on first run (idempotent thanks to IF NOT EXISTS)
const schemaSql = fs.readFileSync(path.join(__dirname, 'schema.sql'), 'utf8');
db.exec(schemaSql);

// ── RÉPARATION D'URGENCE (doit s'exécuter EN PREMIER) ────────────────────
// SQLite 3.26+ a auto-mis-à-jour les FK des tables enfants lors d'un RENAME
// TABLE de investissements → _investissements_old. Après DROP TABLE orpheline,
// ces FK sont invalides. writable_schema est bloqué par better-sqlite3, donc
// on recrée chaque table cassée : DDL corrigé en JS → temp table → copy → rename.
{
  const hasOrphanTable = db.prepare(
    "SELECT COUNT(*) AS n FROM sqlite_master WHERE name='_investissements_old'"
  ).get().n > 0;

  const brokenTables = db.prepare(
    "SELECT name FROM sqlite_master WHERE type='table' AND sql LIKE '%_investissements_old%'"
  ).all().map(r => r.name);

  if (hasOrphanTable || brokenTables.length > 0) {
    db.exec('PRAGMA foreign_keys = OFF');

    if (hasOrphanTable) {
      db.exec('DROP TABLE IF EXISTS _investissements_old');
    }

    for (const tableName of brokenTables) {
      const row = db.prepare(
        "SELECT sql FROM sqlite_master WHERE type='table' AND name=?"
      ).get(tableName);
      if (!row) continue;

      // Corriger la référence FK cassée en JS (writable_schema indisponible)
      const tempName = `__repair_${tableName}`;
      const fixedDdl = row.sql
        .replace(/_investissements_old/g, 'investissements')
        .replace(new RegExp(`CREATE TABLE (?:"${tableName}"|${tableName})\\b`), `CREATE TABLE "${tempName}"`);

      // Colonnes réelles de la table cassée (pour le INSERT SELECT)
      const cols = db.prepare(`PRAGMA table_info("${tableName}")`)
        .all().map(c => `"${c.name}"`).join(', ');

      db.exec(`DROP TABLE IF EXISTS "${tempName}"`);
      db.exec(fixedDdl);
      db.exec(`INSERT INTO "${tempName}" (${cols}) SELECT ${cols} FROM "${tableName}"`);

      // Supprimer les index utilisateurs (ils seront recréés ci-dessous)
      const idxs = db.prepare(
        "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name=? AND sql IS NOT NULL"
      ).all(tableName);
      for (const idx of idxs) db.exec(`DROP INDEX IF EXISTS "${idx.name}"`);

      db.exec(`DROP TABLE "${tableName}"`);
      db.exec(`ALTER TABLE "${tempName}" RENAME TO "${tableName}"`);
    }

    // Recréer les index supprimés avec leurs tables
    if (brokenTables.includes('remboursements')) {
      db.exec('CREATE INDEX IF NOT EXISTS idx_remb_inv    ON remboursements(investissement_id)');
      db.exec('CREATE INDEX IF NOT EXISTS idx_remb_date   ON remboursements(date_remb)');
      db.exec('CREATE INDEX IF NOT EXISTS idx_remb_statut ON remboursements(statut)');
    }
    if (brokenTables.includes('simul_remboursements')) {
      db.exec('CREATE INDEX IF NOT EXISTS idx_simul_inv  ON simul_remboursements(investissement_id)');
      db.exec('CREATE INDEX IF NOT EXISTS idx_simul_date ON simul_remboursements(date_prevue)');
    }

    db.exec('PRAGMA foreign_keys = ON');
  }

  // Corriger aussi les VUES dont le corps SQL référence _investissements_old
  // (SQLite 3.26+ auto-met-à-jour les JOIN dans les vues lors d'un RENAME TABLE)
  const brokenViews = db.prepare(
    "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql LIKE '%_investissements_old%'"
  ).all();
  for (const view of brokenViews) {
    const fixedSql = view.sql.replace(/_investissements_old/g, 'investissements');
    db.exec(`DROP VIEW IF EXISTS "${view.name}"`);
    db.exec(fixedSql);
  }
}

// ── Migrations incrémentales (colonnes ajoutées après le schéma initial) ──
const invCols = db.prepare("PRAGMA table_info(investisseurs)").all().map(c => c.name);

if (!invCols.includes('prenom')) {
  db.exec('ALTER TABLE investisseurs ADD COLUMN prenom TEXT');
}
if (!invCols.includes('type')) {
  db.exec("ALTER TABLE investisseurs ADD COLUMN type TEXT NOT NULL DEFAULT 'famille'");
  // Backfill : PM / SCI / SCPI → entreprise, tout le reste → famille
  db.exec("UPDATE investisseurs SET type = 'entreprise' WHERE type_fiscal IN ('PM','SCI','SCPI')");
}

// ── Migration : suppression de la colonne autres_taxes (devenue inutile) ──
const rembCols = db.prepare('PRAGMA table_info(remboursements)').all().map(c => c.name);
if (rembCols.includes('autres_taxes')) {
  // SQLite exige que la vue qui référence la colonne soit supprimée avant DROP COLUMN
  db.exec('DROP VIEW IF EXISTS v_interets_annuels');
  db.exec('ALTER TABLE remboursements DROP COLUMN autres_taxes');
  // Recrée la vue sans autres_taxes (version à jour du schéma)
  db.exec(`
    CREATE VIEW IF NOT EXISTS v_interets_annuels AS
    SELECT
      i.investisseur_id,
      substr(r.date_remb,1,4)                AS annee,
      SUM(r.interets_bruts)                  AS interets_bruts,
      SUM(r.prelev_sociaux)                  AS prelev_sociaux,
      SUM(r.prelev_forfaitaire)              AS prelev_forfaitaire,
      SUM(r.net_recu)                        AS net_recu
    FROM remboursements r
    JOIN investissements i ON i.id = r.investissement_id
    WHERE r.statut IN ('paye','partiel')
    GROUP BY i.investisseur_id, substr(r.date_remb,1,4)
  `);
}

// ── Migration : renommage date_debut → date_premiere_echeance, date_echeance → date_cible ──
{
  const cols = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (cols.includes('date_debut')) {
    db.exec('ALTER TABLE investissements RENAME COLUMN date_debut TO date_premiere_echeance');
  }
  if (cols.includes('date_echeance')) {
    db.exec('ALTER TABLE investissements RENAME COLUMN date_echeance TO date_cible');
  }
  // Backfill date_premiere_echeance = souscription + 1 mois si vide
  db.exec(`UPDATE investissements
    SET date_premiere_echeance = date(date_souscription, '+1 month')
    WHERE date_premiere_echeance IS NULL AND date_souscription IS NOT NULL`);
  // Backfill date_cible = date_premiere_echeance + (duree_mois - 1) mois si vide
  // Formule cohérente avec la simulation : échéance 1 = date_premiere_echeance, dernière = +duree-1 mois
  db.exec(`UPDATE investissements
    SET date_cible = date(date_premiere_echeance, '+' || (duree_mois - 1) || ' months')
    WHERE date_cible IS NULL AND date_premiere_echeance IS NOT NULL AND duree_mois IS NOT NULL`);
  // Correction des date_cible déjà calculées avec l'ancienne formule (+duree_mois au lieu de +duree_mois-1)
  db.exec(`UPDATE investissements
    SET date_cible = date(date_premiere_echeance, '+' || (duree_mois - 1) || ' months')
    WHERE date_premiere_echeance IS NOT NULL AND duree_mois IS NOT NULL`);
}

// ── Migration : type_remb 'mensuel' → 'amortissable' + ajout freq_interets ──
const invCols2 = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
db.exec("UPDATE investissements SET type_remb = 'amortissable' WHERE type_remb = 'mensuel'");
if (!invCols2.includes('freq_interets')) {
  db.exec("ALTER TABLE investissements ADD COLUMN freq_interets TEXT NOT NULL DEFAULT 'mensuel'");
  // Les prêts différés ont forcément une fréquence in_fine
  db.exec("UPDATE investissements SET freq_interets = 'in_fine' WHERE type_remb = 'differe'");
}

// ── Migration : ajout cashback + interets_nets ────────────────────────
const rembCols2 = db.prepare('PRAGMA table_info(remboursements)').all().map(c => c.name);
if (!rembCols2.includes('cashback')) {
  db.exec('ALTER TABLE remboursements ADD COLUMN cashback REAL NOT NULL DEFAULT 0');
}
if (!rembCols2.includes('interets_nets')) {
  db.exec('ALTER TABLE remboursements ADD COLUMN interets_nets REAL NOT NULL DEFAULT 0');
  // Backfill depuis les colonnes existantes
  db.exec('UPDATE remboursements SET interets_nets = ROUND(interets_bruts - prelev_sociaux - prelev_forfaitaire, 2)');
  // Recalcule net_recu avec la nouvelle formule (cashback=0 sur les anciens, donc inchangé)
  db.exec('UPDATE remboursements SET net_recu = ROUND(capital + cashback + interets_nets, 2)');
}

// ── Migration : prêts différés — date_premiere_echeance doit égaler date_cible ──
// (versement unique à l'échéance : les deux dates sont identiques)
{
  const needsFix = db.prepare(`
    SELECT COUNT(*) AS n FROM investissements
    WHERE type_remb = 'differe'
      AND date_cible IS NOT NULL
      AND (date_premiere_echeance IS NULL OR date_premiere_echeance != date_cible)
  `).get().n;

  if (needsFix > 0) {
    db.exec(`
      UPDATE investissements
      SET date_premiere_echeance = date_cible
      WHERE type_remb = 'differe' AND date_cible IS NOT NULL
    `);
    // Régénère les simulations avec la nouvelle logique (date = startDate directement)
    const differeInvs = db.prepare(`
      SELECT id, montant_investi, taux_interet, duree_mois, type_remb, freq_interets,
             date_premiere_echeance, date_souscription
      FROM investissements
      WHERE type_remb = 'differe'
    `).all();
    for (const inv of differeInvs) {
      generateSimul(db, inv);
    }
  }
}

// ── Migration : ajout date_debut_simul (restructuration de prêt) ─────
{
  const invColsDs = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!invColsDs.includes('date_debut_simul')) {
    db.exec('ALTER TABLE investissements ADD COLUMN date_debut_simul TEXT');
  }
}

// ── Migration : statut 'defaut' → 'en_retard' ────────────────────────
// writable_schema est bloqué par better-sqlite3 → on recrée la table
// investissements avec le bon CHECK constraint (même stratégie que le bloc
// d'urgence). PRAGMA legacy_alter_table = ON empêche SQLite 3.26+ de
// réécrire les FK des tables enfants lors du RENAME final.
{
  const schemaInv = db.prepare(
    "SELECT sql FROM sqlite_master WHERE type='table' AND name='investissements'"
  ).get()?.sql ?? '';

  if (schemaInv.includes("'defaut'")) {
    const tempName = '__repair_investissements';

    const fixedDdl = schemaInv
      .replace(/CREATE TABLE investissements\b/, `CREATE TABLE "${tempName}"`)
      .replace(/'defaut'/g, "'en_retard'");

    const colDefs = db.prepare('PRAGMA table_info(investissements)').all();
    const colNames = colDefs.map(c => `"${c.name}"`).join(', ');
    // Convertit les éventuels statuts 'defaut' → 'en_retard' lors du SELECT
    const selectList = colDefs.map(c =>
      c.name === 'statut'
        ? `CASE WHEN statut = 'defaut' THEN 'en_retard' ELSE statut END AS "statut"`
        : `"${c.name}"`
    ).join(', ');

    const idxs = db.prepare(
      "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='investissements' AND sql IS NOT NULL"
    ).all();

    db.exec('PRAGMA foreign_keys = OFF');
    db.exec(`DROP TABLE IF EXISTS "${tempName}"`);
    db.exec(fixedDdl);
    db.exec(`INSERT INTO "${tempName}" (${colNames}) SELECT ${selectList} FROM investissements`);
    for (const idx of idxs) db.exec(`DROP INDEX IF EXISTS "${idx.name}"`);
    db.exec('DROP TABLE investissements');
    // legacy_alter_table = ON : empêche la réécriture automatique des FK enfants
    db.exec('PRAGMA legacy_alter_table = ON');
    db.exec(`ALTER TABLE "${tempName}" RENAME TO investissements`);
    db.exec('PRAGMA legacy_alter_table = OFF');
    db.exec('PRAGMA foreign_keys = ON');

    db.exec('CREATE INDEX IF NOT EXISTS idx_inv_inv    ON investissements(investisseur_id)');
    db.exec('CREATE INDEX IF NOT EXISTS idx_inv_plat   ON investissements(plateforme_id)');
    db.exec('CREATE INDEX IF NOT EXISTS idx_inv_statut ON investissements(statut)');
    db.exec('CREATE INDEX IF NOT EXISTS idx_inv_date   ON investissements(date_souscription)');
  }

  // Corriger la vue v_synthese_inv si elle référence encore 'defaut'
  const viewSql = db.prepare(
    "SELECT sql FROM sqlite_master WHERE type='view' AND name='v_synthese_inv'"
  ).get()?.sql ?? '';

  if (viewSql.includes("'defaut'")) {
    db.exec('DROP VIEW IF EXISTS v_synthese_inv');
    db.exec(`CREATE VIEW v_synthese_inv AS
      SELECT
        i.investisseur_id,
        COUNT(*)                                                             AS nb_projets,
        SUM(i.montant_investi)                                               AS total_investi,
        SUM(CASE WHEN i.statut='en_cours'  THEN i.montant_investi ELSE 0 END) AS encours,
        SUM(CASE WHEN i.statut='rembourse' THEN i.montant_investi ELSE 0 END) AS rembourse,
        SUM(CASE WHEN i.statut IN ('en_retard','procedure') THEN i.montant_investi ELSE 0 END) AS en_defaut
      FROM investissements i
      GROUP BY i.investisseur_id`);
  }

  // Migrer les données (idempotent : no-op si déjà fait ou aucun statut 'defaut')
  db.exec("UPDATE investissements SET statut = 'en_retard' WHERE statut = 'defaut'");
}

// ── Migration : table historique des modifications d'investissement ──
db.exec(`
  CREATE TABLE IF NOT EXISTS investissement_historique (
    id                 INTEGER PRIMARY KEY AUTOINCREMENT,
    investissement_id  INTEGER NOT NULL REFERENCES investissements(id) ON DELETE CASCADE,
    date_evenement     TEXT    NOT NULL DEFAULT (date('now')),
    type_evenement     TEXT    NOT NULL DEFAULT 'modification',
    changements        TEXT    NOT NULL DEFAULT '[]',
    notes              TEXT,
    created_at         TEXT    NOT NULL DEFAULT (datetime('now'))
  )
`);

// ── Migration : rôle utilisateur ─────────────────────────────────────────
{
  const userCols = db.prepare('PRAGMA table_info(users)').all().map(c => c.name);
  if (!userCols.includes('role')) {
    db.exec("ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT 'user'");
    // Le premier utilisateur enregistré (id le plus petit) devient administrateur
    db.exec("UPDATE users SET role = 'admin' WHERE id = (SELECT MIN(id) FROM users)");
  }
}

// ── Migration : table de logs des jobs automatiques ──────────────────────
db.exec(`
  CREATE TABLE IF NOT EXISTS job_logs (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    job_name    TEXT NOT NULL,
    run_at      TEXT NOT NULL DEFAULT (datetime('now')),
    status      TEXT NOT NULL DEFAULT 'ok',
    nb_changes  INTEGER NOT NULL DEFAULT 0,
    details     TEXT,
    error_msg   TEXT
  )
`);
db.exec('CREATE INDEX IF NOT EXISTS idx_job_logs_run_at ON job_logs(run_at DESC)');

// ── Notation : critères par plateforme ───────────────────────────────
db.exec(`
  CREATE TABLE IF NOT EXISTS notation_criteres (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    plateforme_id INTEGER NOT NULL REFERENCES plateformes(id) ON DELETE CASCADE,
    nom          TEXT NOT NULL,
    type         TEXT NOT NULL DEFAULT 'etoiles',
    valeurs      TEXT,
    min_val      REAL,
    max_val      REAL,
    description  TEXT,
    ordre        INTEGER NOT NULL DEFAULT 0,
    created_at   TEXT NOT NULL DEFAULT (datetime('now'))
  )
`);
db.exec('CREATE INDEX IF NOT EXISTS idx_notation_plat ON notation_criteres(plateforme_id, ordre)');

// ── Types de garanties ───────────────────────────────────────────────
db.exec(`
  CREATE TABLE IF NOT EXISTS garantie_types (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id     INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    libelle     TEXT    NOT NULL,
    description TEXT,
    ordre       INTEGER NOT NULL DEFAULT 0,
    created_at  TEXT    NOT NULL DEFAULT (datetime('now'))
  )
`);
db.exec('CREATE INDEX IF NOT EXISTS idx_garantie_types_user ON garantie_types(user_id, ordre)');

// ── Migration : colonne is_principal sur investisseurs ───────────────────
{
  const invColsPrincipal = db.prepare('PRAGMA table_info(investisseurs)').all().map(c => c.name);
  if (!invColsPrincipal.includes('is_principal')) {
    db.exec('ALTER TABLE investisseurs ADD COLUMN is_principal INTEGER NOT NULL DEFAULT 0');
    // Backfill : le plus ancien investisseur de type 'famille' par user devient principal
    db.exec(`
      UPDATE investisseurs SET is_principal = 1
      WHERE id IN (
        SELECT MIN(id) FROM investisseurs WHERE type = 'famille' GROUP BY user_id
      )
    `);
  }
}

// ── Migration : domiciliation / fiscalité sur plateformes ───────────────
{
  const platCols = db.prepare('PRAGMA table_info(plateformes)').all().map(c => c.name);
  if (!platCols.includes('domiciliation')) {
    db.exec("ALTER TABLE plateformes ADD COLUMN domiciliation TEXT NOT NULL DEFAULT 'france'");
  }
  if (!platCols.includes('fiscalite')) {
    db.exec("ALTER TABLE plateformes ADD COLUMN fiscalite TEXT NOT NULL DEFAULT 'flat_tax'");
  }
  if (!platCols.includes('taux_fiscalite_locale')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN taux_fiscalite_locale REAL');
  }
  if (!platCols.includes('type_produit_fiscal')) {
    db.exec("ALTER TABLE plateformes ADD COLUMN type_produit_fiscal TEXT NOT NULL DEFAULT '2TT'");
  }
  // ── Table plateforme_tax_details ──────────────────────────────────────────
  db.exec(`
    CREATE TABLE IF NOT EXISTS plateforme_tax_details (
      id            INTEGER PRIMARY KEY,
      plateforme_id INTEGER NOT NULL REFERENCES plateformes(id) ON DELETE CASCADE,
      annee         INTEGER NOT NULL,
      raison_sociale TEXT,
      siret_n        TEXT,
      siret_n1       TEXT,
      created_at     TEXT NOT NULL DEFAULT (datetime('now')),
      UNIQUE(plateforme_id, annee)
    )
  `);

  if (!platCols.includes('methode_remboursement')) {
    db.exec("ALTER TABLE plateformes ADD COLUMN methode_remboursement TEXT NOT NULL DEFAULT 'portefeuille'");
  }
}

// ── Migration : methode_remboursement sur remboursements ─────────────────────
{
  const rembCols = db.prepare('PRAGMA table_info(remboursements)').all().map(c => c.name);
  if (!rembCols.includes('methode_remboursement')) {
    db.exec("ALTER TABLE remboursements ADD COLUMN methode_remboursement TEXT NOT NULL DEFAULT 'portefeuille'");
  }
}

// ── Migration : remboursement_id sur depots_retraits (retrait auto) ───────────
{
  const drCols = db.prepare('PRAGMA table_info(depots_retraits)').all().map(c => c.name);
  if (!drCols.includes('remboursement_id')) {
    db.exec('ALTER TABLE depots_retraits ADD COLUMN remboursement_id INTEGER');
  }
}

// ── Migration : détail des taux 2778-SD sur taux_pfu ─────────────────────────
// Décompose prelev_sociaux en ses 3 composantes pour le calcul de la déclaration
// mensuelle 2778-SD (taux identiques au PFU global mais ventilés par case).
// Taux 2021-2025 : CSG 9,2 % | CRDS 0,5 % | Solidarité 7,5 %  → total 17,2 %
// Taux 2026+     : CSG 10,6 %| CRDS 0,5 % | Solidarité 7,5 %  → total 18,6 %
{
  const pfuCols = db.prepare('PRAGMA table_info(taux_pfu)').all().map(c => c.name);
  if (!pfuCols.includes('csg')) {
    db.exec('ALTER TABLE taux_pfu ADD COLUMN csg REAL NOT NULL DEFAULT 9.2');
    // Mise à jour des années 2026+ où la CSG passe à 10,6 %
    db.exec('UPDATE taux_pfu SET csg = 10.6 WHERE annee >= 2026');
  }
  if (!pfuCols.includes('crds')) {
    db.exec('ALTER TABLE taux_pfu ADD COLUMN crds REAL NOT NULL DEFAULT 0.5');
  }
  if (!pfuCols.includes('solidarite')) {
    db.exec('ALTER TABLE taux_pfu ADD COLUMN solidarite REAL NOT NULL DEFAULT 7.5');
  }
}

// ── Seed taux_pfu si vide ─────────────────────────────────────────────
const pfuCount = db.prepare('SELECT COUNT(*) AS n FROM taux_pfu').get().n;
if (pfuCount === 0) {
  const insertPfu = db.prepare(
    'INSERT OR IGNORE INTO taux_pfu (annee, pfu_total, impot_revenu, prelev_sociaux, csg, crds, solidarite) VALUES (?, ?, ?, ?, ?, ?, ?)'
  );
  const seedPfu = db.transaction(() => {
    const data = [
      [2018, 30.0, 12.8, 17.2, 9.2, 0.5, 7.5],
      [2019, 30.0, 12.8, 17.2, 9.2, 0.5, 7.5],
      [2020, 30.0, 12.8, 17.2, 9.2, 0.5, 7.5],
      [2021, 30.0, 12.8, 17.2, 9.2, 0.5, 7.5],
      [2022, 30.0, 12.8, 17.2, 9.2, 0.5, 7.5],
      [2023, 30.0, 12.8, 17.2, 9.2, 0.5, 7.5],
      [2024, 30.0, 12.8, 17.2, 9.2, 0.5, 7.5],
      [2025, 30.0, 12.8, 17.2, 9.2, 0.5, 7.5],
      [2026, 31.4, 12.8, 18.6, 10.6, 0.5, 7.5],
    ];
    for (const row of data) insertPfu.run(...row);
  });
  seedPfu();
}

// ── Migration : categorie_id sur investissements ──────────────────────────
{
  const invColsCat = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!invColsCat.includes('categorie_id')) {
    db.exec('ALTER TABLE investissements ADD COLUMN categorie_id INTEGER REFERENCES categories_plateforme(id) ON DELETE SET NULL');
  }
}

// ── Migration : remboursements — investissement_id nullable + type + bonus ──
// Recrée la table pour lever la contrainte NOT NULL sur investissement_id
// et ajouter les colonnes bonus_plateforme_id, bonus_investisseur_id, type.
{
  const rembColsBonus = db.prepare('PRAGMA table_info(remboursements)').all().map(c => c.name);
  if (!rembColsBonus.includes('bonus_plateforme_id')) {
    const tempName = '__repair_remboursements';

    db.exec('PRAGMA foreign_keys = OFF');
    db.exec('DROP VIEW IF EXISTS v_interets_annuels');
    db.exec(`DROP TABLE IF EXISTS "${tempName}"`);

    db.exec(`CREATE TABLE "${tempName}" (
      id                   INTEGER PRIMARY KEY,
      investissement_id    INTEGER REFERENCES investissements(id) ON DELETE CASCADE,
      bonus_plateforme_id  INTEGER REFERENCES plateformes(id) ON DELETE SET NULL,
      bonus_investisseur_id INTEGER REFERENCES investisseurs(id) ON DELETE SET NULL,
      type                 TEXT NOT NULL DEFAULT 'normal',
      date_remb            TEXT NOT NULL,
      capital              REAL NOT NULL DEFAULT 0,
      interets_bruts       REAL NOT NULL DEFAULT 0,
      prelev_sociaux       REAL NOT NULL DEFAULT 0,
      prelev_forfaitaire   REAL NOT NULL DEFAULT 0,
      cashback             REAL NOT NULL DEFAULT 0,
      interets_nets        REAL NOT NULL DEFAULT 0,
      net_recu             REAL NOT NULL DEFAULT 0,
      statut               TEXT NOT NULL DEFAULT 'paye'
                           CHECK(statut IN ('paye','retard','partiel','impaye')),
      source               TEXT NOT NULL DEFAULT 'manuel',
      notes                TEXT,
      created_at           TEXT NOT NULL DEFAULT (datetime('now')),
      updated_at           TEXT NOT NULL DEFAULT (datetime('now'))
    )`);

    // Copier toutes les colonnes existantes (investissement_id était NOT NULL)
    const existingCols = rembColsBonus.map(c => `"${c}"`).join(', ');
    db.exec(`INSERT INTO "${tempName}" (${existingCols}) SELECT ${existingCols} FROM remboursements`);

    const idxs = db.prepare(
      "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='remboursements' AND sql IS NOT NULL"
    ).all();
    for (const idx of idxs) db.exec(`DROP INDEX IF EXISTS "${idx.name}"`);

    db.exec('DROP TABLE remboursements');
    db.exec('PRAGMA legacy_alter_table = ON');
    db.exec(`ALTER TABLE "${tempName}" RENAME TO remboursements`);
    db.exec('PRAGMA legacy_alter_table = OFF');

    db.exec('CREATE INDEX IF NOT EXISTS idx_remb_inv    ON remboursements(investissement_id)');
    db.exec('CREATE INDEX IF NOT EXISTS idx_remb_date   ON remboursements(date_remb)');
    db.exec('CREATE INDEX IF NOT EXISTS idx_remb_statut ON remboursements(statut)');

    // Recréer la vue (exclut les bonus qui n'ont pas d'investissement_id)
    db.exec(`CREATE VIEW IF NOT EXISTS v_interets_annuels AS
      SELECT
        i.investisseur_id,
        substr(r.date_remb,1,4)   AS annee,
        SUM(r.interets_bruts)     AS interets_bruts,
        SUM(r.prelev_sociaux)     AS prelev_sociaux,
        SUM(r.prelev_forfaitaire) AS prelev_forfaitaire,
        SUM(r.net_recu)           AS net_recu
      FROM remboursements r
      JOIN investissements i ON i.id = r.investissement_id
      WHERE r.statut IN ('paye','partiel')
        AND r.type = 'normal'
      GROUP BY i.investisseur_id, substr(r.date_remb,1,4)`);

    db.exec('PRAGMA foreign_keys = ON');
  }
}

// ── Migration : categorie_id sur investissements ──────────────────────────
{
  const invColsCat = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!invColsCat.includes('categorie_id')) {
    db.exec('ALTER TABLE investissements ADD COLUMN categorie_id INTEGER REFERENCES categories_plateforme(id) ON DELETE SET NULL');
  }
}

// ── Migration : remboursements — investissement_id nullable + type + bonus ──
// Recrée la table pour lever la contrainte NOT NULL sur investissement_id
// et ajouter les colonnes bonus_plateforme_id, bonus_investisseur_id, type.
{
  const rembColsBonus = db.prepare('PRAGMA table_info(remboursements)').all().map(c => c.name);
  if (!rembColsBonus.includes('bonus_plateforme_id')) {
    const tempName = '__repair_remboursements';

    db.exec('PRAGMA foreign_keys = OFF');
    db.exec('DROP VIEW IF EXISTS v_interets_annuels');
    db.exec(`DROP TABLE IF EXISTS "${tempName}"`);

    db.exec(`CREATE TABLE "${tempName}" (
      id                    INTEGER PRIMARY KEY,
      investissement_id     INTEGER REFERENCES investissements(id) ON DELETE CASCADE,
      bonus_plateforme_id   INTEGER REFERENCES plateformes(id) ON DELETE SET NULL,
      bonus_investisseur_id INTEGER REFERENCES investisseurs(id) ON DELETE SET NULL,
      type                  TEXT NOT NULL DEFAULT 'normal',
      date_remb             TEXT NOT NULL,
      capital               REAL NOT NULL DEFAULT 0,
      interets_bruts        REAL NOT NULL DEFAULT 0,
      prelev_sociaux        REAL NOT NULL DEFAULT 0,
      prelev_forfaitaire    REAL NOT NULL DEFAULT 0,
      cashback              REAL NOT NULL DEFAULT 0,
      interets_nets         REAL NOT NULL DEFAULT 0,
      net_recu              REAL NOT NULL DEFAULT 0,
      statut                TEXT NOT NULL DEFAULT 'paye'
                            CHECK(statut IN ('paye','retard','partiel','impaye')),
      source                TEXT NOT NULL DEFAULT 'manuel',
      notes                 TEXT,
      created_at            TEXT NOT NULL DEFAULT (datetime('now')),
      updated_at            TEXT NOT NULL DEFAULT (datetime('now'))
    )`);

    // Copier toutes les colonnes existantes (investissement_id était NOT NULL)
    const existingCols = rembColsBonus.map(c => `"${c}"`).join(', ');
    db.exec(`INSERT INTO "${tempName}" (${existingCols}) SELECT ${existingCols} FROM remboursements`);

    const idxs = db.prepare(
      "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='remboursements' AND sql IS NOT NULL"
    ).all();
    for (const idx of idxs) db.exec(`DROP INDEX IF EXISTS "${idx.name}"`);

    db.exec('DROP TABLE remboursements');
    db.exec('PRAGMA legacy_alter_table = ON');
    db.exec(`ALTER TABLE "${tempName}" RENAME TO remboursements`);
    db.exec('PRAGMA legacy_alter_table = OFF');

    db.exec('CREATE INDEX IF NOT EXISTS idx_remb_inv    ON remboursements(investissement_id)');
    db.exec('CREATE INDEX IF NOT EXISTS idx_remb_date   ON remboursements(date_remb)');
    db.exec('CREATE INDEX IF NOT EXISTS idx_remb_statut ON remboursements(statut)');

    // Recréer la vue (exclut les bonus qui n'ont pas d'investissement_id)
    db.exec(`CREATE VIEW IF NOT EXISTS v_interets_annuels AS
      SELECT
        i.investisseur_id,
        substr(r.date_remb,1,4)   AS annee,
        SUM(r.interets_bruts)     AS interets_bruts,
        SUM(r.prelev_sociaux)     AS prelev_sociaux,
        SUM(r.prelev_forfaitaire) AS prelev_forfaitaire,
        SUM(r.net_recu)           AS net_recu
      FROM remboursements r
      JOIN investissements i ON i.id = r.investissement_id
      WHERE r.statut IN ('paye','partiel')
        AND r.type = 'normal'
      GROUP BY i.investisseur_id, substr(r.date_remb,1,4)`);

    db.exec('PRAGMA foreign_keys = ON');
  }
}

// ── Migration : logo_filename sur plateformes ─────────────────────────────
{
  const platColsLogo = db.prepare('PRAGMA table_info(plateformes)').all().map(c => c.name);
  if (!platColsLogo.includes('logo_filename')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN logo_filename TEXT');
  }
}

// ── Migration : icone_filename sur plateformes ───────────────────────────
{
  const platColsIcone = db.prepare('PRAGMA table_info(plateformes)').all().map(c => c.name);
  if (!platColsIcone.includes('icone_filename')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN icone_filename TEXT');
  }
}

// ── Migration : investisseur_id + date_ouverture sur plateformes ──────────
{
  const platCols2 = db.prepare('PRAGMA table_info(plateformes)').all().map(c => c.name);
  if (!platCols2.includes('investisseur_id')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN investisseur_id INTEGER REFERENCES investisseurs(id) ON DELETE SET NULL');
    // Backfill : associer au compte is_principal de l'utilisateur propriétaire
    db.exec(`
      UPDATE plateformes SET investisseur_id = (
        SELECT inv.id FROM investisseurs inv
        WHERE inv.user_id = plateformes.user_id AND inv.is_principal = 1
        LIMIT 1
      )
      WHERE investisseur_id IS NULL
    `);
  }
  if (!platCols2.includes('date_ouverture')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN date_ouverture TEXT');
  }
}

// ── Migration : valeurs par défaut sur plateformes ────────────────────────
{
  const platColsDef = db.prepare('PRAGMA table_info(plateformes)').all().map(c => c.name);
  if (!platColsDef.includes('type_pret_defaut')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN type_pret_defaut TEXT');
  }
  if (!platColsDef.includes('duree_defaut')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN duree_defaut INTEGER');
  }
  if (!platColsDef.includes('taux_defaut')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN taux_defaut REAL');
  }
  if (!platColsDef.includes('freq_interets_defaut')) {
    db.exec("ALTER TABLE plateformes ADD COLUMN freq_interets_defaut TEXT");
  }
}



// ── Migration : table comptes ─────────────────────────────────────────────
{
  const comptesExists = db.prepare(
    "SELECT COUNT(*) AS n FROM sqlite_master WHERE type='table' AND name='comptes'"
  ).get().n > 0;
  if (!comptesExists) {
    db.exec(`
      CREATE TABLE comptes (
        id              INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id         INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
        investisseur_id INTEGER REFERENCES investisseurs(id) ON DELETE SET NULL,
        nom             TEXT NOT NULL,
        type            TEXT NOT NULL DEFAULT 'compte_courant',
        banque          TEXT,
        created_at      TEXT NOT NULL DEFAULT (datetime('now')),
        updated_at      TEXT
      )
    `);
    db.exec('CREATE INDEX IF NOT EXISTS idx_comptes_user ON comptes(user_id)');
    db.exec('CREATE INDEX IF NOT EXISTS idx_comptes_inv  ON comptes(investisseur_id)');
  }
}

// ── Migration : exoneration_fiscale sur comptes ─────────────────────────────
{
  const cols = db.prepare('PRAGMA table_info(comptes)').all().map(c => c.name);
  if (!cols.includes('exoneration_fiscale')) {
    db.exec("ALTER TABLE comptes ADD COLUMN exoneration_fiscale TEXT NOT NULL DEFAULT 'aucune'");
  }
}

// ── Migration : UNIQUE(user_id, nom) → UNIQUE(user_id, nom, investisseur_id) ─
// Permet d'avoir la même plateforme (même nom) détenue par deux investisseurs
// différents. SQLite ne supporte pas DROP CONSTRAINT → recréation de la table.
{
  const platSql = db.prepare(
    "SELECT sql FROM sqlite_master WHERE type='table' AND name='plateformes'"
  ).get()?.sql ?? '';

  // Détecte l'ancienne contrainte UNIQUE(user_id, nom) sans investisseur_id
  const needsMigration = /UNIQUE\s*\(\s*user_id\s*,\s*nom\s*\)/.test(platSql)
    && !/UNIQUE\s*\(\s*user_id\s*,\s*nom\s*,\s*investisseur_id\s*\)/.test(platSql);

  if (needsMigration) {
    const tempName = '__repair_plateformes';
    const platCols = db.prepare('PRAGMA table_info(plateformes)').all();
    const colNames = platCols.map(c => `"${c.name}"`).join(', ');

    const idxs = db.prepare(
      "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='plateformes' AND sql IS NOT NULL"
    ).all();

    db.exec('PRAGMA foreign_keys = OFF');
    db.exec(`DROP TABLE IF EXISTS "${tempName}"`);
    db.exec(`CREATE TABLE "${tempName}" (
      id                    INTEGER PRIMARY KEY,
      user_id               INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
      nom                   TEXT NOT NULL,
      url                   TEXT,
      notes                 TEXT,
      created_at            TEXT NOT NULL DEFAULT (datetime('now')),
      domiciliation         TEXT NOT NULL DEFAULT 'france',
      fiscalite             TEXT NOT NULL DEFAULT 'flat_tax',
      taux_fiscalite_locale REAL,
      methode_remboursement TEXT NOT NULL DEFAULT 'portefeuille',
      investisseur_id       INTEGER REFERENCES investisseurs(id) ON DELETE SET NULL,
      date_ouverture        TEXT,
      logo_filename         TEXT,
      UNIQUE(user_id, nom, investisseur_id)
    )`);
    db.exec(`INSERT INTO "${tempName}" (${colNames}) SELECT ${colNames} FROM plateformes`);
    for (const idx of idxs) db.exec(`DROP INDEX IF EXISTS "${idx.name}"`);
    db.exec('DROP TABLE plateformes');
    db.exec('PRAGMA legacy_alter_table = ON');
    db.exec(`ALTER TABLE "${tempName}" RENAME TO plateformes`);
    db.exec('PRAGMA legacy_alter_table = OFF');
    db.exec('PRAGMA foreign_keys = ON');
    db.exec('CREATE INDEX IF NOT EXISTS idx_plateformes_user ON plateformes(user_id)');
  }
}

// ── Migration : echeance_fin_de_mois sur investissements ─────────────────
// Indicateur "dernier jour du mois" pour les prêts in fine / différés
// dont la date de première échéance tombe après le 27 du mois.
{
  const invColsEfm = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!invColsEfm.includes('echeance_fin_de_mois')) {
    db.exec('ALTER TABLE investissements ADD COLUMN echeance_fin_de_mois INTEGER NOT NULL DEFAULT 0');
  }
}

// ── Migration : table reinvestissements ──────────────────────────────────
db.exec(`
  CREATE TABLE IF NOT EXISTS reinvestissements (
    id                    INTEGER PRIMARY KEY AUTOINCREMENT,
    investissement_id     INTEGER NOT NULL REFERENCES investissements(id) ON DELETE CASCADE,
    montant               REAL    NOT NULL,
    date_reinvestissement TEXT    NOT NULL,
    note                  TEXT,
    created_at            TEXT    NOT NULL DEFAULT (datetime('now'))
  )
`);
db.exec('CREATE INDEX IF NOT EXISTS idx_reinv_inv ON reinvestissements(investissement_id, date_reinvestissement)');

// ── Migration : table corrections_solde ──────────────────────────────────
// Corrections manuelles de solde porte-monnaie pour plateformes flat_tax.
// Permet de réconcilier les micro-écarts de calcul de taxe (ex. 0,01€)
// en déclarant le solde constaté après une opération dépôt/retrait.
db.exec(`
  CREATE TABLE IF NOT EXISTS corrections_solde (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    investisseur_id INTEGER NOT NULL REFERENCES investisseurs(id) ON DELETE CASCADE,
    plateforme_id   INTEGER NOT NULL REFERENCES plateformes(id) ON DELETE CASCADE,
    date            TEXT    NOT NULL,
    montant         REAL    NOT NULL,
    notes           TEXT,
    created_at      TEXT    NOT NULL DEFAULT (datetime('now'))
  )
`);
db.exec('CREATE INDEX IF NOT EXISTS idx_corrections_investisseur ON corrections_solde(investisseur_id, date)');
db.exec('CREATE INDEX IF NOT EXISTS idx_corrections_plateforme  ON corrections_solde(plateforme_id, date)');

// ── Migration : fiscalité locale sur remboursements ──────────────────────
// Stocke le montant brut avant retenue et le montant de la taxe locale
// (pour les plateformes avec fiscalite = 'avec_fiscalite_locale').
{
  const rembColsLocal = db.prepare('PRAGMA table_info(remboursements)').all().map(c => c.name);
  if (!rembColsLocal.includes('interets_bruts_avant_local')) {
    db.exec('ALTER TABLE remboursements ADD COLUMN interets_bruts_avant_local REAL NOT NULL DEFAULT 0');
  }
  if (!rembColsLocal.includes('taxe_locale')) {
    db.exec('ALTER TABLE remboursements ADD COLUMN taxe_locale REAL NOT NULL DEFAULT 0');
  }
}

// ── Migration : réinvestissement automatique ─────────────────────────────
// auto_reinvest sur investissements : active le réinvestissement automatique des intérêts.
// source sur reinvestissements      : 'manuel' (défaut) ou 'auto'.
{
  const invCols   = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  const reinvCols = db.prepare('PRAGMA table_info(reinvestissements)').all().map(c => c.name);
  if (!invCols.includes('auto_reinvest')) {
    db.exec('ALTER TABLE investissements ADD COLUMN auto_reinvest INTEGER NOT NULL DEFAULT 0');
  }
  if (!reinvCols.includes('source')) {
    db.exec("ALTER TABLE reinvestissements ADD COLUMN source TEXT NOT NULL DEFAULT 'manuel'");
  }
}

// ── Migration : fiscalite_override sur investissements ──────────────────────
// Permet d'exonérer un investissement de la flat tax, indépendamment du paramétrage
// de la plateforme (ex : investissement logé en PEA-PME).
// Valeurs : null (suit la plateforme) | 'exonere' (traité comme sans_fiscalite_locale).
{
  const invCols = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!invCols.includes('fiscalite_override')) {
    db.exec('ALTER TABLE investissements ADD COLUMN fiscalite_override TEXT');
  }
}

// ── Migration : nom_compte_courant sur investissements ──────────────────────
// Nom du compte bancaire de l'investisseur quand methode_remboursement = 'compte_courant'.
{
  const invCols = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!invCols.includes('nom_compte_courant')) {
    db.exec('ALTER TABLE investissements ADD COLUMN nom_compte_courant TEXT');
  }
}

// ── Migration : methode_remboursement sur investissements ────────────────────
// Permet à l'investisseur de préciser sa préférence de remboursement quand la
// plateforme est configurée 'choix_investisseur'.
// Valeurs : 'portefeuille' | 'compte_courant' | null (non renseigné).
{
  const invCols = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!invCols.includes('methode_remboursement')) {
    db.exec('ALTER TABLE investissements ADD COLUMN methode_remboursement TEXT');
  }
}

// ── Migration : pays_exposition sur investissements ──────────────────────────
// Permet d'enregistrer le pays d'exposition du prêt (code ISO 3166-1 alpha-2).
// Défaut : 'FR' (France).
{
  const invCols = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!invCols.includes('pays_exposition')) {
    db.exec("ALTER TABLE investissements ADD COLUMN pays_exposition TEXT NOT NULL DEFAULT 'FR'");
  }
}

export default db;

// ── Table user_preferences ───────────────────────────────────────────────────
// Stockage générique des préférences UI par utilisateur.
// Clé/valeur (TEXT) avec upsert. Extensible à toutes les prefs futures.
db.exec(`
  CREATE TABLE IF NOT EXISTS user_preferences (
    user_id    INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    key        TEXT    NOT NULL,
    value      TEXT    NOT NULL,
    updated_at TEXT    NOT NULL DEFAULT (datetime('now')),
    PRIMARY KEY (user_id, key)
  )
`);

// ── Tables app_icons + app_icons_history ─────────────────────────────────────
// Bibliothèque d'icônes de l'application, gérée depuis /admin.
// `app_icons`         : association nom (slug) ↔ fichier actif
// `app_icons_history` : historique des fichiers remplacés
db.exec(`
  CREATE TABLE IF NOT EXISTS app_icons (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    name        TEXT NOT NULL UNIQUE,
    filename    TEXT NOT NULL,
    description TEXT,
    created_at  TEXT NOT NULL DEFAULT (datetime('now')),
    updated_at  TEXT NOT NULL DEFAULT (datetime('now'))
  )
`);

db.exec(`
  CREATE TABLE IF NOT EXISTS app_icons_history (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    icon_id     INTEGER NOT NULL REFERENCES app_icons(id) ON DELETE CASCADE,
    filename    TEXT NOT NULL,
    replaced_at TEXT NOT NULL DEFAULT (datetime('now'))
  )
`);

// ── Seed initial depuis design/Icones/svg ────────────────────────────────────
{
  const count = db.prepare('SELECT COUNT(*) AS n FROM app_icons').get().n;
  if (count === 0) {
    const seeds = [
      { name: 'balance',         filename: 'icon_balance_seed.svg',                    description: 'Balance / équilibre' },
      { name: 'capital',         filename: 'icon_capital_1779539824176.svg',            description: 'Capital investi' },
      { name: 'cashback',        filename: 'icon_cashback_seed.svg',                   description: 'Cashback / bonus' },
      { name: 'compte-courant',  filename: 'icon_compte-courant_1780313604075.svg',    description: 'Compte courant' },
      { name: 'dashboard',       filename: 'icon_dashboard_1780232184451.svg',         description: 'Dashboard financier' },
      { name: 'depot',           filename: 'icon_depot_1779539637657.svg',             description: 'Dépôt de fonds' },
      { name: 'depots-retraits', filename: 'icon_depots-retraits_1780231579297.svg',   description: 'Dépôts / Retraits' },
      { name: 'interets',        filename: 'icon_interets_seed.svg',                   description: 'Intérêts perçus' },
      { name: 'investissement',  filename: 'icon_investissement_seed.svg',             description: 'Investissement' },
      { name: 'plateforme',      filename: 'icon_plateforme_seed.svg',                 description: 'Plateforme de prêt' },
      { name: 'plateforme-fr',   filename: 'icon_plateforme-fr_1780332257672.png',     description: 'Plateforme fraçaise' },
      { name: 'plateforme-ww',   filename: 'icon_plateforme-ww_1780332295418.png',     description: 'Plateforme étrangère' },
      { name: 'porte-monnaie',   filename: 'icon_porte-monnaie_1779539652739.svg',     description: 'Porte-monnaie' },
      { name: 'remboursement',   filename: 'icon_remboursement_seed.svg',              description: 'Remboursement' },
      { name: 'retrait',         filename: 'icon_retrait_seed.svg',                    description: 'Retrait de fonds' },
      { name: 'tax',             filename: 'icon_tax_1780230337883.svg',               description: 'Fiscalité / Tax' },
    ];
    const ins = db.prepare(
      'INSERT OR IGNORE INTO app_icons (name, filename, description) VALUES (?,?,?)'
    );
    for (const s of seeds) ins.run(s.name, s.filename, s.description);
  }
}

// ── Migration : compte_id sur investissements ────────────────────────────────
// Lie un investissement au compte bancaire du détenteur (FK → comptes.id).
{
  const cols = db.prepare('PRAGMA table_info(investissements)').all().map(c => c.name);
  if (!cols.includes('compte_id')) {
    db.exec('ALTER TABLE investissements ADD COLUMN compte_id INTEGER REFERENCES comptes(id) ON DELETE SET NULL');
  }
}

// ── Migration : compte_id sur remboursements ─────────────────────────────────
// Lie un remboursement au compte bancaire récepteur (FK → comptes.id).
{
  const cols = db.prepare('PRAGMA table_info(remboursements)').all().map(c => c.name);
  if (!cols.includes('compte_id')) {
    db.exec('ALTER TABLE remboursements ADD COLUMN compte_id INTEGER REFERENCES comptes(id) ON DELETE SET NULL');
  }
}



// ── Table taux_credit_impot — Référentiel 2047 crédit d'impôt par pays ───────
// Stocke les taux de crédit d'impôt applicables sur dividendes et intérêts
// selon les conventions fiscales, pour la déclaration 2047.
// Alimenté depuis les pages 5-6 de la notice DGFiP 2047 (édition 2024).
{
  const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map(r => r.name);
  if (!tables.includes('taux_credit_impot')) {
    db.exec(`
      CREATE TABLE taux_credit_impot (
        id                    INTEGER PRIMARY KEY AUTOINCREMENT,
        nom_pays              TEXT NOT NULL,
        code_pays             TEXT,
        div_taux              REAL,
        div_taux_alt          REAL,
        div_taux_alt_label    TEXT,
        div_exclusif_residence INTEGER NOT NULL DEFAULT 0,
        int_taux              REAL,
        int_taux_alt          REAL,
        int_taux_alt_label    TEXT,
        int_exclusif_residence INTEGER NOT NULL DEFAULT 0,
        notice                TEXT,
        statut_convention     TEXT NOT NULL DEFAULT 'active',
        date_suspension       TEXT,
        ref_boi               TEXT,
        created_at            TEXT NOT NULL DEFAULT (datetime('now')),
        updated_at            TEXT NOT NULL DEFAULT (datetime('now'))
      )
    `);

    // Seed depuis notice DGFiP 2047 (pages 5-6)
    // Colonnes : nom_pays, code_pays, div_taux, div_taux_alt, div_taux_alt_label,
    //            div_excl_res, int_taux, int_taux_alt, int_taux_alt_label, int_excl_res,
    //            statut_convention, date_suspension, ref_boi, notice
    const seed = [
      ["Afrique du Sud","ZA",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Albanie","AL",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Algérie","DZ",17.6,null,null,0,13.6,null,null,0,"active",null,null,null],
      ["Allemagne","DE",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Andorre","AD",17.6,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Arabie Saoudite","SA",null,null,null,1,null,null,null,1,"active",null,null,null],
      ["Argentine","AR",17.6,null,null,0,25.0,null,null,0,"active",null,null,null],
      ["Arménie","AM",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Australie","AU",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Autriche","AT",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Azerbaïdjan","AZ",11.1,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Bahreïn","BH",null,null,null,1,null,null,null,1,"active",null,null,null],
      ["Bangladesh","BD",25.0,null,null,0,25.0,null,null,0,"active",null,null,"Dividendes et intérêts : le crédit d'impôt est égal à l'impôt prélevé au Bangladesh dans les limites prévues par la convention, augmenté de 10 % du montant brut du revenu, sans que le total puisse excéder 20 % du montant brut de ce revenu. En l'absence de retenue à la source au Bangladesh, le taux du crédit d'impôt est de 10 %."],
      ["Belgique","BE",17.6,null,null,0,17.6,null,null,0,"active",null,null,null],
      ["Bénin","BJ",25.0,null,null,0,null,null,null,0,"active",null,null,"Intérêts : le crédit d'impôt est égal à l'impôt prélevé au Bénin. Dividendes : la déduction correspondant à l'impôt effectivement payé au Bénin est égale à 25 % du montant brut des revenus."],
      ["Biélorussie","BY",17.6,null,null,0,11.1,null,null,0,"suspendue","2024-06-01",null,"La convention fiscale applicable entre la France et la Biélorussie est suspendue à compter du 1er juin 2024. Convention ex-URSS."],
      ["Bolivie","BO",17.6,null,null,0,17.6,null,null,0,"active",null,null,null],
      ["Bosnie-Herzégovine","BA",17.6,null,null,0,null,null,null,1,"active",null,null,"Convention fiscale conclue entre la France et la République socialiste fédérative de Yougoslavie."],
      ["Botswana","BW",13.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Brésil","BR",20.0,null,null,0,20.0,null,null,0,"active",null,null,"Dividendes et intérêts : crédit d'impôt forfaitaire égal à 20 % du montant brut de ceux-ci lorsque les revenus ont été effectivement imposés au Brésil."],
      ["Bulgarie","BG",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Burkina Faso","BF",null,null,null,0,null,null,null,0,"caduque","2023-11-08","BOI-INT-CVB-BFA","La convention fiscale conclue entre la France et le Burkina Faso a cessé de produire ses effets à compter du 8 novembre 2023."],
      ["Cameroun","CM",17.6,null,null,0,17.6,null,null,0,"active",null,null,null],
      ["Canada","CA",17.6,null,null,0,11.1,null,null,0,"active",null,null,"Québec compris."],
      ["Centrafricaine (Rép.)","CF",33.3,null,null,0,13.6,null,null,0,"active",null,null,null],
      ["Chili","CL",null,null,null,0,10.0,null,null,0,"active",null,null,"Dividendes : crédit d'impôt égal à la plus petite des sommes suivantes : montant de l'impôt additionnel payé au Chili après déduction de l'impôt de première catégorie, ou 15 % du montant brut des dividendes. Intérêts : crédit d'impôt de 10 % en application de la clause de la nation la plus favorisée."],
      ["Chine","CN",11.1,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Chypre","CY",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Colombie","CO",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Congo","CG",25.0,null,null,0,null,null,null,1,"active",null,null,null],
      ["Corée du Sud","KR",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Côte d'Ivoire","CI",17.6,22.0,"Société exonérée ou à taux réduit IS",0,17.6,null,null,0,"active",null,null,"Dividendes : crédit d'impôt plafonné à 18 % du montant brut lorsqu'ils sont payés par une société exonérée de l'IS ou acquittant cet impôt à taux réduit, et à 15 % dans les autres cas."],
      ["Croatie","HR",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Danemark","DK",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Égypte","EG",null,null,null,1,17.6,null,null,0,"active",null,null,null],
      ["Émirats arabes unis","AE",null,null,null,1,null,null,null,1,"active",null,null,null],
      ["Équateur","EC",17.6,null,null,0,17.6,11.1,"Conditions particulières conv.",0,"active",null,null,null],
      ["Espagne","ES",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Estonie","EE",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["États-Unis","US",17.6,null,null,0,17.6,null,null,0,"active",null,null,"Dividendes : crédit d'impôt égal à l'impôt américain, dans la limite de 15 % du montant brut. Certains dividendes de résidents de France possédant la citoyenneté américaine ouvrent droit à un crédit d'impôt égal au montant de l'impôt français (art. 24 §1-b-i de la convention du 31 août 1994). Intérêts : crédit d'impôt égal à l'impôt américain, dans la limite de 15 % du montant brut. Les intérêts exonérés aux États-Unis n'ouvrent droit à aucun crédit d'impôt en France."],
      ["Éthiopie","ET",5.3,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Finlande","FI",null,null,null,1,11.1,null,null,0,"active",null,null,null],
      ["Gabon","GA",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Géorgie","GE",11.1,null,null,0,null,null,null,1,"active",null,null,null],
      ["Ghana","GH",17.6,null,null,0,14.3,null,null,0,"active",null,null,null],
      ["Grèce","GR",17.6,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Guinée","GN",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Hong-Kong","HK",11.1,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Hongrie","HU",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Inde","IN",11.1,null,null,0,10.0,null,null,0,"active",null,null,"Intérêts : crédit d'impôt de 10 % en application de la clause de la nation la plus favorisée. Les intérêts de source indienne n'ayant pas supporté l'impôt en Inde ouvrent droit à un crédit forfaitaire correspondant à l'impôt qui aurait été perçu, plafonné à 10 %."],
      ["Indonésie","ID",17.6,null,null,0,17.6,null,null,0,"active",null,null,"Dividendes et intérêts : crédit d'impôt égal à 10 % du montant brut lorsque l'impôt indonésien n'est pas perçu ou lorsqu'il est perçu à un taux inférieur à 10 % du fait de mesures spéciales incitatives."],
      ["Iran","IR",25.0,null,null,0,17.6,null,null,0,"active",null,null,null],
      ["Irlande","IE",null,null,null,1,null,null,null,1,"active",null,null,null],
      ["Islande","IS",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Israël","IL",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Italie","IT",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Jamaïque","JM",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Japon","JP",11.1,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Jordanie","JO",17.6,null,null,0,17.6,null,null,0,"active",null,null,null],
      ["Kazakhstan","KZ",11.1,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Kenya","KE",11.1,null,null,0,13.6,null,null,0,"active",null,null,null],
      ["Kirghizistan","KG",17.6,null,null,0,11.1,null,null,0,"active",null,null,"Convention fiscale conclue entre la France et l'ex-URSS."],
      ["Kosovo","XK",17.6,null,null,0,null,null,null,1,"active",null,null,"Convention fiscale conclue entre la France et la République socialiste fédérative de Yougoslavie."],
      ["Koweït","KW",null,null,null,1,null,null,null,1,"active",null,null,null],
      ["Lettonie","LV",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Liban","LB",null,null,null,1,null,null,null,1,"active",null,null,null],
      ["Libye","LY",11.1,null,null,0,null,null,null,1,"active",null,null,null],
      ["Lituanie","LT",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Luxembourg","LU",17.6,null,null,0,null,null,null,1,"active",null,null,"Dividendes : le crédit d'impôt ne peut excéder 25 % du montant brut des dividendes."],
      ["Macédoine","MK",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Madagascar","MG",33.3,null,null,0,17.6,null,null,0,"active",null,null,null],
      ["Malaisie","MY",null,null,null,1,17.6,null,null,0,"active",null,null,"Intérêts : le crédit d'impôt est égal à l'impôt prélevé en Malaisie."],
      ["Mali","ML",null,null,null,0,null,null,null,0,"caduque","2024-03-05","BOI-INT-CVB-MLI","La convention fiscale conclue entre la France et le Mali a cessé de produire ses effets à compter du 5 mars 2024."],
      ["Malte","MT",17.6,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Maroc","MA",25.0,null,null,0,17.6,11.1,"Emprunts organismes de développement",0,"active",null,null,"Dividendes ayant supporté l'impôt au Maroc : crédit d'impôt égal à 25 % du montant brut. Intérêts : crédit forfaitaire de 10 % lorsqu'ils proviennent d'emprunts émis par certains organismes spécialisés pour le développement économique du Maroc (plafonné à l'impôt français). Dans les autres cas, CI égal à l'impôt effectivement supporté."],
      ["Maurice","MU",33.3,null,null,0,null,null,null,0,"active",null,null,"Dividendes : le crédit d'impôt ne peut excéder 25 % du montant brut des dividendes."],
      ["Mauritanie","MR",33.3,null,null,0,16.0,12.0,"Obligations négociables",0,"active",null,null,"Dividendes : crédit d'impôt égal à 25 % du montant brut lorsque effectivement imposés en Mauritanie. Intérêts : crédit d'impôt de 16 % du montant brut (prêts, dépôts, bons de caisse non négociables) ; 12 % pour les obligations négociables."],
      ["Mexique","MX",17.6,null,null,0,11.1,5.3,"Conditions particulières conv.",0,"active",null,null,null],
      ["Moldavie","MD",11.1,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Mongolie","MN",17.6,null,null,0,11.1,null,null,0,"active",null,null,"Dividendes et intérêts : l'art. 23 §I d) iii prévoit une modalité particulière de calcul du crédit d'impôt lorsque le revenu a bénéficié d'une réduction ou d'une suppression de l'impôt à la source qui n'est plus applicable."],
      ["Monténégro","ME",17.6,null,null,0,null,null,null,1,"active",null,null,"Convention fiscale conclue entre la France et la République socialiste fédérative de Yougoslavie."],
      ["Namibie","NA",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Niger","NE",null,null,null,0,null,null,null,0,"suspendue","2024-06-05","BOI-INT-CVB-NER","La convention fiscale conclue entre la France et le Niger est suspendue à compter du 5 juin 2024. Intérêts : CI égal à l'impôt payé au Niger ; intérêts de prêts/dépôts non négociables ouvrent droit à CI de 16 % du montant brut."],
      ["Nigéria","NG",17.6,null,null,0,14.3,null,null,0,"active",null,null,"Dividendes et intérêts : crédit d'impôt de 15 % (div.) ou 12,5 % (int.) lorsqu'ils sont exonérés ou soumis à taux réduit en vertu de la législation nigériane sur le développement économique."],
      ["Norvège","NO",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Nouvelle-Calédonie","NC",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Nouvelle-Zélande","NZ",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Oman","OM",null,null,null,1,null,null,null,1,"active",null,null,null],
      ["Ouzbékistan","UZ",8.7,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Pakistan","PK",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Panama","PA",17.6,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Pays-Bas","NL",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Philippines","PH",17.6,null,null,0,17.6,null,null,0,"active",null,null,null],
      ["Pologne","PL",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Polynésie française","PF",null,null,null,1,null,null,null,1,"active",null,null,"Les dividendes et intérêts de source polynésienne n'ouvrent pas droit à crédit d'impôt."],
      ["Portugal","PT",17.6,null,null,0,10.0,12.0,"Autres emprunts (hors obligations négociables)",0,"active",null,null,"Intérêts ayant donné lieu à imposition au Portugal : 10 % du montant brut pour les obligations et titres d'emprunts négociables ; 12 % du montant brut pour tous autres emprunts."],
      ["Qatar","QA",null,null,null,1,null,null,null,1,"active",null,null,null],
      ["République tchèque","CZ",11.1,null,null,0,null,null,null,1,"active",null,null,null],
      ["Roumanie","RO",11.1,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Royaume-Uni","GB",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Russie","RU",null,null,null,0,null,null,null,0,"suspendue","2023-08-08","BOI-INT-CVB-RUS","La convention fiscale conclue entre la France et la Russie est partiellement suspendue à compter du 8 août 2023."],
      ["Saint-Barthélemy","BL",null,null,null,0,null,null,null,0,"active",null,null,"En l'absence de convention fiscale, l'art. L.O. 6214-4 du CGCT prévoit l'octroi d'un crédit d'impôt égal à l'impôt payé à Saint-Barthélemy. Cette collectivité n'imposant que les plus-values immobilières, aucun crédit d'impôt n'est accordé pour les autres catégories de revenu."],
      ["Saint-Martin","MF",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Saint-Pierre-et-Miquelon","PM",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Sénégal","SN",17.6,null,null,0,17.6,null,null,0,"active",null,null,null],
      ["Serbie","RS",17.6,null,null,0,null,null,null,1,"active",null,null,"Convention fiscale conclue entre la France et la République socialiste fédérative de Yougoslavie."],
      ["Singapour","SG",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Slovaquie","SK",11.1,null,null,0,null,null,null,1,"active",null,null,null],
      ["Slovénie","SI",17.6,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Sri Lanka","LK",25.0,null,null,0,15.0,null,null,0,"active",null,null,"Dividendes : crédit d'impôt égal à 25 % du montant brut. Intérêts : crédit d'impôt égal à 15 % du montant brut des intérêts de source sri-lankaise ayant supporté un impôt inférieur."],
      ["Suède","SE",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Suisse","CH",17.6,null,null,0,null,null,null,1,"active",null,null,null],
      ["Syrie","SY",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Taïwan","TW",11.1,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Thaïlande","TH",25.0,null,null,0,null,null,null,1,"active",null,null,"Dividendes : le crédit d'impôt ne peut excéder 25 % du montant brut des dividendes."],
      ["Togo","TG",33.3,null,null,0,13.6,null,null,0,"active",null,null,null],
      ["Trinité-et-Tobago","TT",17.6,null,null,0,11.1,null,null,0,"active",null,null,"Les intérêts et dividendes visés à l'art. 24-2-c ouvrent droit à un CI correspondant à l'impôt qui aurait été perçu par Trinité-et-Tobago en l'absence des dispositions particulières, plafonné au montant de la retenue à la source prévue aux articles 10 et 11 de la convention."],
      ["Tunisie","TN",null,null,null,0,13.6,null,null,0,"active",null,null,"Dividendes : CI calculé par la formule (100 – (25 + t)) / 2, où t est le taux de la retenue à la source tunisienne. Aucun CI n'est accordé si la Tunisie n'impose pas ces revenus."],
      ["Turkménistan","TM",17.6,null,null,0,11.1,null,null,0,"active",null,null,"Convention fiscale conclue entre la France et l'ex-URSS."],
      ["Turquie","TR",25.0,null,null,0,17.6,null,null,0,"active",null,null,"Dividendes et intérêts : lorsque ces revenus bénéficient d'une exonération ou d'une réduction d'impôt en vertu de mesures sur le développement économique, le CI est égal à l'impôt qui aurait dû être payé en l'absence de ces mesures (max 20 % du montant brut des div., max 15 % pour les int.)."],
      ["Ukraine","UA",17.6,null,null,0,11.1,null,null,0,"active",null,null,null],
      ["Venezuela","VE",5.3,null,null,0,5.3,null,null,0,"active",null,null,null],
      ["Viêt Nam","VN",11.1,null,null,0,null,null,null,1,"active",null,null,null],
      ["Zimbabwe","ZW",25.0,null,null,0,11.1,null,null,0,"active",null,null,null]
    ];

    const ins = db.prepare(
      'INSERT INTO taux_credit_impot' +
      ' (nom_pays, code_pays,' +
      '  div_taux, div_taux_alt, div_taux_alt_label, div_exclusif_residence,' +
      '  int_taux, int_taux_alt, int_taux_alt_label, int_exclusif_residence,' +
      '  statut_convention, date_suspension, ref_boi, notice)' +
      ' VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
    );
    const txn = db.transaction((rows) => { for (const r of rows) ins.run(...r); });
    txn(seed);
  }
}

// ── Migration : référentiel commun de plateformes (admin) ────────────────────
// plateformes_referentiel : données partagées gérées par l'administrateur.
// referentiel_categories  : junction référentiel ↔ catégories.
// referentiel_notation    : critères de notation attachés au référentiel.
// plateformes.referentiel_id    : lien vers l'entrée référentiel (nullable).
// plateformes.overridden_fields : JSON array des champs modifiés par l'user.
{
  db.exec(`
    CREATE TABLE IF NOT EXISTS plateformes_referentiel (
      id                    INTEGER PRIMARY KEY AUTOINCREMENT,
      nom                   TEXT NOT NULL UNIQUE,
      url                   TEXT,
      domiciliation         TEXT NOT NULL DEFAULT 'france',
      fiscalite             TEXT NOT NULL DEFAULT 'flat_tax',
      taux_fiscalite_locale REAL,
      type_produit_fiscal   TEXT NOT NULL DEFAULT '2TT',
      logo_filename         TEXT,
      description           TEXT,
      created_at            TEXT NOT NULL DEFAULT (datetime('now')),
      updated_at            TEXT NOT NULL DEFAULT (datetime('now'))
    )
  `);
  db.exec('CREATE INDEX IF NOT EXISTS idx_ref_nom ON plateformes_referentiel(nom)');

  // Si la table existe avec l'ancien schéma FK (categorie_id), on la recrée
  {
    const refCatSql = db.prepare(
      "SELECT sql FROM sqlite_master WHERE type='table' AND name='referentiel_categories'"
    ).get()?.sql ?? '';
    if (refCatSql && refCatSql.includes('categorie_id')) {
      db.exec('DROP TABLE referentiel_categories');
    }
  }
  db.exec(`
    CREATE TABLE IF NOT EXISTS referentiel_categories (
      referentiel_id INTEGER NOT NULL REFERENCES plateformes_referentiel(id) ON DELETE CASCADE,
      categorie_nom  TEXT    NOT NULL,
      PRIMARY KEY (referentiel_id, categorie_nom)
    )
  `);

  db.exec(`
    CREATE TABLE IF NOT EXISTS referentiel_notation (
      id             INTEGER PRIMARY KEY AUTOINCREMENT,
      referentiel_id INTEGER NOT NULL REFERENCES plateformes_referentiel(id) ON DELETE CASCADE,
      nom            TEXT    NOT NULL,
      type           TEXT    NOT NULL DEFAULT 'etoiles',
      valeurs        TEXT,
      min_val        REAL,
      max_val        REAL,
      description    TEXT,
      ordre          INTEGER NOT NULL DEFAULT 0,
      created_at     TEXT    NOT NULL DEFAULT (datetime('now'))
    )
  `);
  db.exec('CREATE INDEX IF NOT EXISTS idx_ref_notation ON referentiel_notation(referentiel_id, ordre)');

  // Colonnes d'héritage sur plateformes
  const platColsRef = db.prepare('PRAGMA table_info(plateformes)').all().map(c => c.name);
  if (!platColsRef.includes('referentiel_id')) {
    db.exec('ALTER TABLE plateformes ADD COLUMN referentiel_id INTEGER REFERENCES plateformes_referentiel(id) ON DELETE SET NULL');
  }
  if (!platColsRef.includes('overridden_fields')) {
    db.exec("ALTER TABLE plateformes ADD COLUMN overridden_fields TEXT NOT NULL DEFAULT '[]'");
  }
}

// ── Migration : icone_filename sur plateformes_referentiel ────────────────
{
  const refCols = db.prepare('PRAGMA table_info(plateformes_referentiel)').all().map(c => c.name);
  if (!refCols.includes('icone_filename')) {
    db.exec('ALTER TABLE plateformes_referentiel ADD COLUMN icone_filename TEXT');
  }
}


// ── Migration : champs remboursement sur plateformes_referentiel ─────────────
{
  const refCols = db.prepare('PRAGMA table_info(plateformes_referentiel)').all().map(c => c.name);
  if (!refCols.includes('methode_remboursement'))
    db.exec("ALTER TABLE plateformes_referentiel ADD COLUMN methode_remboursement TEXT NOT NULL DEFAULT 'portefeuille'");
  if (!refCols.includes('type_pret_defaut'))
    db.exec('ALTER TABLE plateformes_referentiel ADD COLUMN type_pret_defaut TEXT');
  if (!refCols.includes('freq_interets_defaut'))
    db.exec('ALTER TABLE plateformes_referentiel ADD COLUMN freq_interets_defaut TEXT');

  // Remontée : récupère les valeurs depuis les plateformes liées (première valeur non nulle)
  db.exec(`
    UPDATE plateformes_referentiel
    SET
      methode_remboursement = COALESCE(
        methode_remboursement,
        (SELECT p.methode_remboursement FROM plateformes p
         WHERE p.referentiel_id = plateformes_referentiel.id
           AND p.methode_remboursement IS NOT NULL LIMIT 1)
      ),
      type_pret_defaut = COALESCE(
        type_pret_defaut,
        (SELECT p.type_pret_defaut FROM plateformes p
         WHERE p.referentiel_id = plateformes_referentiel.id
           AND p.type_pret_defaut IS NOT NULL LIMIT 1)
      ),
      freq_interets_defaut = COALESCE(
        freq_interets_defaut,
        (SELECT p.freq_interets_defaut FROM plateformes p
         WHERE p.referentiel_id = plateformes_referentiel.id
           AND p.freq_interets_defaut IS NOT NULL LIMIT 1)
      )
  `);
}

// ── Migration : champs profil sur plateformes_referentiel ────────────────
{
  const refCols = db.prepare('PRAGMA table_info(plateformes_referentiel)').all().map(c => c.name);
  const addCol = (col, def) => {
    if (!refCols.includes(col)) db.exec(`ALTER TABLE plateformes_referentiel ADD COLUMN ${col} ${def}`);
  };

  // Identité
  addCol('annee_creation',      'INTEGER');
  addCol('type_investissement', 'TEXT');   // 'p2p' | 'dette' | 'equity' | 'mixte'
  addCol('secteur',             'TEXT');   // 'immobilier' | 'pme' | 'startups' | 'energie' | ...
  addCol('investisseurs_types', 'TEXT');   // 'particulier' | 'entreprise' | 'les_deux'

  // Régulation
  addCol('regulateur',          'TEXT');
  addCol('numero_licence',      'TEXT');
  addCol('is_regule',           'INTEGER NOT NULL DEFAULT 0');

  // Localisation
  addCol('pays_inscription',    'TEXT');
  addCol('pays_siege',          'TEXT');
  addCol('pays_operation',      'TEXT');   // JSON array ex: '["France","Allemagne"]'

  // Stats / conditions
  addCol('investissement_minimum', 'REAL');
  addCol('rendement_annonce',      'REAL');
  addCol('nb_investisseurs',       'INTEGER');
  addCol('volume_total_finance',   'REAL');
  addCol('duree_moyenne_pret',     'REAL');  // en mois

  // Features booléennes
  addCol('garantie_rachat',         'INTEGER NOT NULL DEFAULT 0');
  addCol('statistiques_publiques',  'INTEGER NOT NULL DEFAULT 0');
  addCol('bonus_inscription',       'INTEGER NOT NULL DEFAULT 0');
  addCol('marche_secondaire',       'INTEGER NOT NULL DEFAULT 0');
  addCol('investissement_auto',     'INTEGER NOT NULL DEFAULT 0');

  // Liens complémentaires
  addCol('url_trustpilot', 'TEXT');
  addCol('url_linkedin',   'TEXT');
}

// ── Migration : categories_inv + secteurs_inv (listes admin globales) ──────
{
  // Labels lisibles pour la migration des données existantes
  const TYPES_INV_LABELS = {
    p2p:              'Prêt P2P',
    dette:            'Dette',
    equity:           'Capitaux propres',
    tokenise:         'Tokenisé',
    achat_louer:      'Achat à louer',
    mini_obligations: 'Mini-obligations',
    mixte:            'Mixte',
  };
  const SECTEURS_LABELS = {
    immobilier:    'Immobilier',
    pme:           'PME',
    startups:      'Startups',
    litige:        'Litige',
    energie:       'Énergie verte',
    sante_science: 'Santé & Science',
    logistique:    'Logistique',
    personnel:     'Prêts personnels',
    art:           'Art',
    autre:         'Autre',
  };

  // 1. Créer les tables globales
  db.exec(`
    CREATE TABLE IF NOT EXISTS categories_inv (
      id         INTEGER PRIMARY KEY AUTOINCREMENT,
      nom        TEXT    NOT NULL UNIQUE,
      created_at TEXT    NOT NULL DEFAULT (datetime('now'))
    )
  `);
  db.exec(`
    CREATE TABLE IF NOT EXISTS referentiel_categories_inv (
      referentiel_id INTEGER NOT NULL REFERENCES plateformes_referentiel(id) ON DELETE CASCADE,
      categorie_id   INTEGER NOT NULL REFERENCES categories_inv(id) ON DELETE CASCADE,
      PRIMARY KEY (referentiel_id, categorie_id)
    )
  `);
  db.exec(`
    CREATE TABLE IF NOT EXISTS secteurs_inv (
      id         INTEGER PRIMARY KEY AUTOINCREMENT,
      nom        TEXT    NOT NULL UNIQUE,
      created_at TEXT    NOT NULL DEFAULT (datetime('now'))
    )
  `);
  db.exec(`
    CREATE TABLE IF NOT EXISTS referentiel_secteurs_inv (
      referentiel_id INTEGER NOT NULL REFERENCES plateformes_referentiel(id) ON DELETE CASCADE,
      secteur_id     INTEGER NOT NULL REFERENCES secteurs_inv(id) ON DELETE CASCADE,
      PRIMARY KEY (referentiel_id, secteur_id)
    )
  `);

  // 2. Seeder categories_inv si vide
  const catCount = db.prepare('SELECT COUNT(*) AS n FROM categories_inv').get().n;
  if (catCount === 0) {
    const insC = db.prepare('INSERT OR IGNORE INTO categories_inv (nom) VALUES (?)');
    db.transaction(() => {
      // Valeurs par défaut depuis TYPES_INV
      for (const nom of Object.values(TYPES_INV_LABELS)) insC.run(nom);
      // Valeurs existantes depuis referentiel_categories (chaînes libres)
      const existing = db.prepare('SELECT DISTINCT categorie_nom FROM referentiel_categories').all();
      for (const { categorie_nom } of existing) insC.run(categorie_nom);
      // Valeurs existantes depuis type_investissement (slugs → labels)
      const typesExist = db.prepare('SELECT DISTINCT type_investissement FROM plateformes_referentiel WHERE type_investissement IS NOT NULL').all();
      for (const { type_investissement: t } of typesExist) {
        const label = TYPES_INV_LABELS[t] || t;
        insC.run(label);
      }
    })();
  }

  // 3. Seeder secteurs_inv si vide
  const sectCount = db.prepare('SELECT COUNT(*) AS n FROM secteurs_inv').get().n;
  if (sectCount === 0) {
    const insS = db.prepare('INSERT OR IGNORE INTO secteurs_inv (nom) VALUES (?)');
    db.transaction(() => {
      for (const nom of Object.values(SECTEURS_LABELS)) insS.run(nom);
      // Valeurs existantes depuis le champ secteur
      const sectsExist = db.prepare('SELECT DISTINCT secteur FROM plateformes_referentiel WHERE secteur IS NOT NULL').all();
      for (const { secteur: s } of sectsExist) {
        const label = SECTEURS_LABELS[s] || s;
        insS.run(label);
      }
    })();
  }

  // 4. Migrer les données existantes → referentiel_categories_inv
  const alreadyMigrated = db.prepare('SELECT COUNT(*) AS n FROM referentiel_categories_inv').get().n;
  if (alreadyMigrated === 0) {
    const getCatId = db.prepare('SELECT id FROM categories_inv WHERE nom = ?');
    const insLink  = db.prepare('INSERT OR IGNORE INTO referentiel_categories_inv (referentiel_id, categorie_id) VALUES (?,?)');
    const refs     = db.prepare('SELECT id, type_investissement FROM plateformes_referentiel').all();
    const oldCats  = db.prepare('SELECT referentiel_id, categorie_nom FROM referentiel_categories').all();

    db.transaction(() => {
      for (const ref of refs) {
        // Depuis type_investissement
        if (ref.type_investissement) {
          const label = TYPES_INV_LABELS[ref.type_investissement] || ref.type_investissement;
          const cat   = getCatId.get(label);
          if (cat) insLink.run(ref.id, cat.id);
        }
      }
      // Depuis referentiel_categories (chaînes libres)
      for (const { referentiel_id, categorie_nom } of oldCats) {
        const cat = getCatId.get(categorie_nom);
        if (cat) insLink.run(referentiel_id, cat.id);
      }
    })();
  }

  // 5. Migrer les données existantes → referentiel_secteurs_inv
  const alreadySectMigrated = db.prepare('SELECT COUNT(*) AS n FROM referentiel_secteurs_inv').get().n;
  if (alreadySectMigrated === 0) {
    const getSectId = db.prepare('SELECT id FROM secteurs_inv WHERE nom = ?');
    const insSectLink = db.prepare('INSERT OR IGNORE INTO referentiel_secteurs_inv (referentiel_id, secteur_id) VALUES (?,?)');
    const refs = db.prepare('SELECT id, secteur FROM plateformes_referentiel WHERE secteur IS NOT NULL').all();

    db.transaction(() => {
      for (const ref of refs) {
        const label = SECTEURS_LABELS[ref.secteur] || ref.secteur;
        const sect  = getSectId.get(label);
        if (sect) insSectLink.run(ref.id, sect.id);
      }
    })();
  }
}

// ── Migration : domiciliation texte → code ISO 3166-1 alpha-2 ────────────────
{
  const OLD_VALUES = ['france', 'zone_europeenne', 'hors_zone_europeenne'];

  // plateformes (par user)
  const hasOldPlat = db.prepare(
    `SELECT 1 FROM plateformes WHERE domiciliation IN ('france','zone_europeenne','hors_zone_europeenne') LIMIT 1`
  ).get();
  if (hasOldPlat) {
    db.exec(`UPDATE plateformes SET domiciliation = 'FR' WHERE domiciliation = 'france'`);
    // zone_europeenne / hors_zone_europeenne → 'EU' provisoire, à corriger via l'interface
    db.exec(`UPDATE plateformes SET domiciliation = 'EU' WHERE domiciliation = 'zone_europeenne'`);
    db.exec(`UPDATE plateformes SET domiciliation = 'EU' WHERE domiciliation = 'hors_zone_europeenne'`);
    console.log('[DB] Migration domiciliation → ISO : plateformes mises à jour.');
  }

  // plateformes_referentiel (admin)
  const hasOldRef = db.prepare(
    `SELECT 1 FROM plateformes_referentiel WHERE domiciliation IN ('france','zone_europeenne','hors_zone_europeenne') LIMIT 1`
  ).get();
  if (hasOldRef) {
    db.exec(`UPDATE plateformes_referentiel SET domiciliation = 'FR' WHERE domiciliation = 'france'`);
    db.exec(`UPDATE plateformes_referentiel SET domiciliation = 'EU' WHERE domiciliation = 'zone_europeenne'`);
    db.exec(`UPDATE plateformes_referentiel SET domiciliation = 'EU' WHERE domiciliation = 'hors_zone_europeenne'`);
    console.log('[DB] Migration domiciliation → ISO : référentiel mis à jour.');
  }
}

// ── Migration : pays_siege → domiciliation (ISO) sur plateformes_referentiel ──
{
  const NAME_TO_ISO = {
    'Afghanistan': 'AF', 'Afrique du Sud': 'ZA', 'Albanie': 'AL', 'Algérie': 'DZ',
    'Allemagne': 'DE', 'Andorre': 'AD', 'Angola': 'AO', 'Antigua-et-Barbuda': 'AG',
    'Arabie saoudite': 'SA', 'Argentine': 'AR', 'Arménie': 'AM', 'Australie': 'AU',
    'Autriche': 'AT', 'Azerbaïdjan': 'AZ', 'Bahamas': 'BS', 'Bahreïn': 'BH',
    'Bangladesh': 'BD', 'Barbade': 'BB', 'Biélorussie': 'BY', 'Belgique': 'BE',
    'Belize': 'BZ', 'Bénin': 'BJ', 'Bhoutan': 'BT', 'Bolivie': 'BO',
    'Bosnie-Herzégovine': 'BA', 'Botswana': 'BW', 'Brésil': 'BR', 'Brunéi': 'BN',
    'Bulgarie': 'BG', 'Burkina Faso': 'BF', 'Burundi': 'BI', 'Cap-Vert': 'CV',
    'Cambodge': 'KH', 'Cameroun': 'CM', 'Canada': 'CA', 'République centrafricaine': 'CF',
    'Chili': 'CL', 'Chine': 'CN', 'Chypre': 'CY', 'Colombie': 'CO',
    'Comores': 'KM', 'Congo': 'CG', 'Congo (RDC)': 'CD', 'Corée du Nord': 'KP',
    'Corée du Sud': 'KR', 'Costa Rica': 'CR', 'Croatie': 'HR', 'Cuba': 'CU',
    'Danemark': 'DK', 'Djibouti': 'DJ', 'République dominicaine': 'DO', 'Dominique': 'DM',
    'Égypte': 'EG', 'Salvador': 'SV', 'Émirats arabes unis': 'AE', 'Équateur': 'EC',
    'Érythrée': 'ER', 'Espagne': 'ES', 'Estonie': 'EE', 'Eswatini': 'SZ',
    'Éthiopie': 'ET', 'Fidji': 'FJ', 'Finlande': 'FI', 'France': 'FR',
    'Gabon': 'GA', 'Gambie': 'GM', 'Géorgie': 'GE', 'Ghana': 'GH',
    'Grèce': 'GR', 'Grenade': 'GD', 'Guatemala': 'GT', 'Guinée': 'GN',
    'Guinée-Bissau': 'GW', 'Guinée équatoriale': 'GQ', 'Guyana': 'GY', 'Haïti': 'HT',
    'Honduras': 'HN', 'Hongrie': 'HU', 'Inde': 'IN', 'Indonésie': 'ID',
    'Irak': 'IQ', 'Iran': 'IR', 'Irlande': 'IE', 'Islande': 'IS',
    'Israël': 'IL', 'Italie': 'IT', 'Jamaïque': 'JM', 'Japon': 'JP',
    'Jordanie': 'JO', 'Kazakhstan': 'KZ', 'Kenya': 'KE', 'Kirghizistan': 'KG',
    'Kiribati': 'KI', 'Koweït': 'KW', 'Laos': 'LA', 'Lesotho': 'LS',
    'Lettonie': 'LV', 'Liban': 'LB', 'Liberia': 'LR', 'Libye': 'LY',
    'Liechtenstein': 'LI', 'Lituanie': 'LT', 'Luxembourg': 'LU', 'Macédoine du Nord': 'MK',
    'Madagascar': 'MG', 'Malaisie': 'MY', 'Malawi': 'MW', 'Maldives': 'MV',
    'Mali': 'ML', 'Malte': 'MT', 'Maroc': 'MA', 'Îles Marshall': 'MH',
    'Maurice': 'MU', 'Mauritanie': 'MR', 'Mexique': 'MX', 'Micronésie': 'FM',
    'Moldavie': 'MD', 'Monaco': 'MC', 'Mongolie': 'MN', 'Monténégro': 'ME',
    'Mozambique': 'MZ', 'Myanmar': 'MM', 'Namibie': 'NA', 'Nauru': 'NR',
    'Népal': 'NP', 'Nicaragua': 'NI', 'Niger': 'NE', 'Nigeria': 'NG',
    'Norvège': 'NO', 'Nouvelle-Zélande': 'NZ', 'Oman': 'OM', 'Ouganda': 'UG',
    'Ouzbékistan': 'UZ', 'Pakistan': 'PK', 'Palaos': 'PW', 'Panama': 'PA',
    'Papouasie-Nouvelle-Guinée': 'PG', 'Paraguay': 'PY', 'Pays-Bas': 'NL', 'Pérou': 'PE',
    'Philippines': 'PH', 'Pologne': 'PL', 'Portugal': 'PT', 'Qatar': 'QA',
    'Roumanie': 'RO', 'Royaume-Uni': 'GB', 'Russie': 'RU', 'Rwanda': 'RW',
    'Saint-Kitts-et-Nevis': 'KN', 'Saint-Marin': 'SM', 'Saint-Vincent-et-les-Grenadines': 'VC',
    'Sainte-Lucie': 'LC', 'Îles Salomon': 'SB', 'Samoa': 'WS', 'Sao Tomé-et-Principe': 'ST',
    'Sénégal': 'SN', 'Serbie': 'RS', 'Seychelles': 'SC', 'Sierra Leone': 'SL',
    'Singapour': 'SG', 'Slovaquie': 'SK', 'Slovénie': 'SI', 'Somalie': 'SO',
    'Soudan': 'SD', 'Soudan du Sud': 'SS', 'Sri Lanka': 'LK', 'Suède': 'SE',
    'Suisse': 'CH', 'Suriname': 'SR', 'Syrie': 'SY', 'Tadjikistan': 'TJ',
    'Tanzanie': 'TZ', 'Tchad': 'TD', 'Tchéquie': 'CZ', 'Thaïlande': 'TH',
    'Timor oriental': 'TL', 'Togo': 'TG', 'Tonga': 'TO', 'Trinité-et-Tobago': 'TT',
    'Tunisie': 'TN', 'Turkménistan': 'TM', 'Turquie': 'TR', 'Tuvalu': 'TV',
    'Ukraine': 'UA', 'Uruguay': 'UY', 'Vanuatu': 'VU', 'Venezuela': 'VE',
    'Viêt Nam': 'VN', 'Yémen': 'YE', 'Zambie': 'ZM', 'Zimbabwe': 'ZW',
    'États-Unis': 'US',
  };

  const rows = db.prepare(
    `SELECT id, pays_siege, domiciliation FROM plateformes_referentiel WHERE pays_siege IS NOT NULL`
  ).all();

  const stmt = db.prepare(`UPDATE plateformes_referentiel SET domiciliation = ? WHERE id = ?`);
  let updated = 0, unmatched = [];

  for (const row of rows) {
    const iso = NAME_TO_ISO[row.pays_siege];
    if (iso && iso !== row.domiciliation) {
      stmt.run(iso, row.id);
      updated++;
    } else if (!iso) {
      unmatched.push(row.pays_siege);
    }
  }

  if (updated > 0)
    console.log(`[DB] Migration pays_siege → domiciliation : ${updated} ligne(s) mise(s) à jour.`);
  if (unmatched.length > 0)
    console.warn(`[DB] Migration pays_siege → domiciliation : valeurs non reconnues →`, [...new Set(unmatched)]);
}

// ── Migration : catégories/secteurs par user + associations plateformes/investissements ──
{
  // 1. Ajouter user_id sur categories_inv (NULL = global admin)
  const colsCatInv = db.prepare("PRAGMA table_info(categories_inv)").all().map(c => c.name);
  if (!colsCatInv.includes('user_id')) {
    db.exec('ALTER TABLE categories_inv ADD COLUMN user_id INTEGER REFERENCES users(id) ON DELETE CASCADE');
    console.log('[DB] categories_inv.user_id ajouté');
  }

  // 2. Ajouter user_id sur secteurs_inv (NULL = global admin)
  const colsSectInv = db.prepare("PRAGMA table_info(secteurs_inv)").all().map(c => c.name);
  if (!colsSectInv.includes('user_id')) {
    db.exec('ALTER TABLE secteurs_inv ADD COLUMN user_id INTEGER REFERENCES users(id) ON DELETE CASCADE');
    console.log('[DB] secteurs_inv.user_id ajouté');
  }

  // 3. Table associations plateforme ↔ catégories
  db.exec(`
    CREATE TABLE IF NOT EXISTS plateforme_categories_inv (
      plateforme_id  INTEGER NOT NULL REFERENCES plateformes(id) ON DELETE CASCADE,
      categorie_id   INTEGER NOT NULL REFERENCES categories_inv(id) ON DELETE CASCADE,
      PRIMARY KEY (plateforme_id, categorie_id)
    )
  `);

  // 4. Table associations plateforme ↔ secteurs
  db.exec(`
    CREATE TABLE IF NOT EXISTS plateforme_secteurs_inv (
      plateforme_id  INTEGER NOT NULL REFERENCES plateformes(id) ON DELETE CASCADE,
      secteur_id     INTEGER NOT NULL REFERENCES secteurs_inv(id) ON DELETE CASCADE,
      PRIMARY KEY (plateforme_id, secteur_id)
    )
  `);

  // 5. Table associations investissement ↔ catégories
  db.exec(`
    CREATE TABLE IF NOT EXISTS investissement_categories_inv (
      investissement_id INTEGER NOT NULL REFERENCES investissements(id) ON DELETE CASCADE,
      categorie_id      INTEGER NOT NULL REFERENCES categories_inv(id) ON DELETE CASCADE,
      PRIMARY KEY (investissement_id, categorie_id)
    )
  `);

  // 6. Table associations investissement ↔ secteurs
  db.exec(`
    CREATE TABLE IF NOT EXISTS investissement_secteurs_inv (
      investissement_id INTEGER NOT NULL REFERENCES investissements(id) ON DELETE CASCADE,
      secteur_id        INTEGER NOT NULL REFERENCES secteurs_inv(id) ON DELETE CASCADE,
      PRIMARY KEY (investissement_id, secteur_id)
    )
  `);

  console.log('[DB] Tables catégories/secteurs plateforme+investissement OK');
}

// ── Migration ponctuelle : correction date_cible aberrantes (>2100) ──────────
// Certains prêts différés importés ont une date_cible avec un siècle erroné.
// On recalcule date_souscription + duree_mois et on régénère la simulation.
{
  function fixAddMonths(isoDate, months) {
    const [y, m, d] = isoDate.split('-').map(Number);
    let nm = m + months;
    let ny = y;
    while (nm > 12) { nm -= 12; ny++; }
    const maxDay = new Date(Date.UTC(ny, nm, 0)).getUTCDate();
    const nd = Math.min(d, maxDay);
    return `${String(ny).padStart(4,'0')}-${String(nm).padStart(2,'0')}-${String(nd).padStart(2,'0')}`;
  }

  const toFix = db.prepare(`
    SELECT i.id, i.date_souscription, i.duree_mois,
      i.montant_investi, i.taux_interet, i.type_remb, i.freq_interets,
      i.date_premiere_echeance, i.date_debut_simul, i.echeance_fin_de_mois
    FROM investissements i
    WHERE i.statut IN ('en_cours','en_retard','procedure')
      AND i.type_remb = 'differe'
      AND i.date_cible > '2100-01-01'
      AND i.duree_mois IS NOT NULL
  `).all();

  if (toFix.length > 0) {
    const updateDate = db.prepare(`UPDATE investissements SET date_cible=?, updated_at=datetime('now') WHERE id=?`);
    const fixAll = db.transaction(() => {
      for (const inv of toFix) {
        const newDate = fixAddMonths(inv.date_souscription, inv.duree_mois);
        updateDate.run(newDate, inv.id);
        generateSimul(db, { ...inv, date_cible: newDate });
        console.log(`[DB] Fix date_cible id=${inv.id} → ${newDate}`);
      }
    });
    fixAll();
    console.log(`[DB] ${toFix.length} date_cible aberrantes corrigées.`);
  }
}
