import { Router } from 'express';
import { z } from 'zod';
import db from '../db/index.js';
import { HttpError } from '../middleware/errorHandler.js';
import { requireInvestisseur } from '../middleware/investisseurScope.js';
import { generateSimul } from '../utils/schedule.js';

const router = Router();

const TRACKED_FIELDS = [
  { key: 'type_remb',              label: 'Type de prêt'              },
  { key: 'taux_interet',           label: 'Taux annuel (%)'           },
  { key: 'duree_mois',             label: 'Durée (mois)'              },
  { key: 'montant_investi',        label: 'Montant investi (€)'   },
  { key: 'statut',                 label: 'Statut'                    },
  { key: 'freq_interets',          label: 'Fréquence des intérêts'    },
  { key: 'date_premiere_echeance', label: 'Date 1ère échéance'        },
  { key: 'date_cible',             label: 'Date cible'                },
  { key: 'date_debut_simul',       label: 'Date de restructuration'   },
  { key: 'plateforme_id',          label: 'Plateforme'                },
];

function recordHistory(investissementId, { type_evenement, changements, notes }) {
  if (!changements || changements.length === 0) return;
  db.prepare(`
    INSERT INTO investissement_historique (investissement_id, type_evenement, changements, notes)
    VALUES (?, ?, ?, ?)
  `).run(investissementId, type_evenement, JSON.stringify(changements), notes || null);
}

function detectChangements(ancien, nouveau) {
  const diffs = [];
  for (const { key, label } of TRACKED_FIELDS) {
    const av = ancien[key] ?? null;
    const nv = nouveau[key] ?? null;
    const avNorm = av === '' ? null : av;
    const nvNorm = nv === '' ? null : nv;
    if (String(avNorm) !== String(nvNorm)) {
      diffs.push({ champ: key, label, ancienne_valeur: avNorm, nouvelle_valeur: nvNorm });
    }
  }
  return diffs;
}

function detectTypeEvenement(changements) {
  const champsRestructuration = ['type_remb', 'date_debut_simul'];
  if (changements.some(c => champsRestructuration.includes(c.champ))) return 'restructuration';
  return 'modification';
}

const Schema = z.object({
  investisseur_id: z.number().int().positive().optional(),
  plateforme_id: z.number().int().positive(),
  nom_projet: z.string().min(1),
  emetteur: z.string().optional(),
  date_souscription: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  date_premiere_echeance: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().or(z.literal('')),
  date_cible: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().or(z.literal('')),
  date_debut_simul: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().or(z.literal('')),
  montant_investi: z.number().positive(),
  taux_interet: z.number().optional(),
  duree_mois: z.number().int().optional(),
  type_remb: z.enum(['in_fine','amortissable','differe']).optional().or(z.literal('')),
  freq_interets: z.enum(['mensuel','trimestriel','in_fine']).default('mensuel'),
  statut: z.enum(['en_cours','rembourse','en_retard','procedure','cloture']).default('en_cours'),
  reference: z.string().optional(),
  notes: z.string().optional(),
  categorie_id: z.number().int().positive().nullable().optional(),
  echeance_fin_de_mois: z.number().int().min(0).max(1).optional().default(0),
  methode_remboursement: z.enum(['portefeuille','compte_courant']).nullable().optional(),
  nom_compte_courant: z.string().nullable().optional(),
  compte_id: z.number().int().positive().nullable().optional(),
  pays_exposition: z.string().length(2).optional().default('FR'),
});

router.use(requireInvestisseur);

function resolveInvestisseurId(req, bodyInvestisseurId) {
  if (!bodyInvestisseurId) return req.investisseur.id;
  const row = db.prepare('SELECT id FROM investisseurs WHERE id = ? AND user_id = ?')
    .get(bodyInvestisseurId, req.user.id);
  if (!row) throw new HttpError(403, 'Investisseur non autorisé');
  return bodyInvestisseurId;
}

router.get('/', (req, res) => {
  const scopeAll = req.query.scope === 'all';
  const { statut, plateforme_id } = req.query;

  const conds = scopeAll
    ? ['i.investisseur_id IN (SELECT id FROM investisseurs WHERE user_id = ?)']
    : ['i.investisseur_id = ?'];
  const args = scopeAll ? [req.user.id] : [req.investisseur.id];

  if (statut)       { conds.push('i.statut = ?');        args.push(statut); }
  if (plateforme_id){ conds.push('i.plateforme_id = ?'); args.push(Number(plateforme_id)); }

  const rows = db.prepare(`
    SELECT i.*, p.nom AS plateforme_nom,
           inv.nom AS investisseur_nom,
           cp.nom AS categorie_nom,
           plat_inv.nom AS plateforme_detenteur_nom,
           c.id AS compte_id, c.nom AS compte_nom, c.type AS compte_type,
           (SELECT COALESCE(SUM(r.capital),0)        FROM remboursements r WHERE r.investissement_id = i.id) AS capital_rembourse,
           (SELECT COALESCE(SUM(r.interets_bruts),0) FROM remboursements r WHERE r.investissement_id = i.id) AS interets_percus,
           (SELECT COALESCE(SUM(r.interets_nets),0)  FROM remboursements r WHERE r.investissement_id = i.id) AS interets_nets_total,
           (SELECT COALESCE(SUM(r.net_recu),0)       FROM remboursements r WHERE r.investissement_id = i.id) AS net_recu_total,
           (SELECT COALESCE(SUM(rv.montant),0)       FROM reinvestissements rv WHERE rv.investissement_id = i.id) AS reinvestissements_total,
           i.montant_investi + (SELECT COALESCE(SUM(rv.montant),0) FROM reinvestissements rv WHERE rv.investissement_id = i.id) AS capital_total
    FROM investissements i
    JOIN plateformes p ON p.id = i.plateforme_id
    JOIN investisseurs inv ON inv.id = i.investisseur_id
    LEFT JOIN investisseurs plat_inv ON plat_inv.id = p.investisseur_id
    LEFT JOIN categories_plateforme cp ON cp.id = i.categorie_id
    LEFT JOIN comptes c ON c.id = i.compte_id
    WHERE ${conds.join(' AND ')}
    ORDER BY i.date_souscription DESC, i.id DESC
  `).all(...args);

  // Attacher les associations catégories/secteurs d'investissement
  if (rows.length > 0) {
    const ids = rows.map(r => r.id);
    const placeholders = ids.map(() => '?').join(',');
    const cats = db.prepare(`
      SELECT ic.investissement_id, c.id, c.nom,
             CASE WHEN c.user_id IS NULL THEN 1 ELSE 0 END AS is_global
      FROM investissement_categories_inv ic
      JOIN categories_inv c ON c.id = ic.categorie_id
      WHERE ic.investissement_id IN (${placeholders})
      ORDER BY is_global DESC, c.nom
    `).all(...ids);
    const sects = db.prepare(`
      SELECT is2.investissement_id, s.id, s.nom,
             CASE WHEN s.user_id IS NULL THEN 1 ELSE 0 END AS is_global
      FROM investissement_secteurs_inv is2
      JOIN secteurs_inv s ON s.id = is2.secteur_id
      WHERE is2.investissement_id IN (${placeholders})
      ORDER BY is_global DESC, s.nom
    `).all(...ids);
    const catMap  = {};
    const sectMap = {};
    for (const c of cats)  { if (!catMap[c.investissement_id])  catMap[c.investissement_id]  = []; catMap[c.investissement_id].push({ id: c.id, nom: c.nom, is_global: c.is_global }); }
    for (const s of sects) { if (!sectMap[s.investissement_id]) sectMap[s.investissement_id] = []; sectMap[s.investissement_id].push({ id: s.id, nom: s.nom, is_global: s.is_global }); }
    for (const r of rows) { r.categories_inv = catMap[r.id] || []; r.secteurs_inv = sectMap[r.id] || []; }
  }

  res.json(rows);
});

// Retourne les comptes bancaires d'un investisseur donné (pour le select dans le formulaire)
router.get('/comptes-par-investisseur/:investisseur_id', (req, res, next) => {
  try {
    const invId = Number(req.params.investisseur_id);
    // Vérifie que l'investisseur appartient à l'utilisateur
    const inv = db.prepare('SELECT id FROM investisseurs WHERE id = ? AND user_id = ?')
      .get(invId, req.user.id);
    if (!inv) throw new HttpError(403, 'Investisseur non autorisé');
    const rows = db.prepare(
      'SELECT id, nom, type, banque FROM comptes WHERE investisseur_id = ? AND user_id = ? ORDER BY type, nom'
    ).all(invId, req.user.id);
    res.json(rows);
  } catch (e) { next(e); }
});

router.get('/comptes-courants', (req, res) => {
  const rows = db.prepare(`
    SELECT DISTINCT i.investisseur_id, i.nom_compte_courant
    FROM investissements i
    JOIN investisseurs inv ON inv.id = i.investisseur_id AND inv.user_id = ?
    WHERE i.nom_compte_courant IS NOT NULL AND i.nom_compte_courant != ''
    ORDER BY i.nom_compte_courant
  `).all(req.user.id);
  res.json(rows);
});

// POST /api/investissements/fix-differe-dates
// Corrige date_premiere_echeance et date_cible des prêts différés dont les dates
// s'écartent de plus de 2 ans par rapport à date_souscription + duree_mois.
router.post('/fix-differe-dates', (req, res, next) => {
  try {
    const rows = db.prepare(`
      SELECT i.id, i.nom_projet, i.date_souscription, i.duree_mois,
             i.date_premiere_echeance, i.date_cible
      FROM investissements i
      JOIN investisseurs inv ON inv.id = i.investisseur_id AND inv.user_id = ?
      WHERE i.type_remb = 'differe'
        AND i.date_souscription IS NOT NULL
        AND i.duree_mois IS NOT NULL
    `).all(req.user.id);

    const SEUIL_JOURS = 730; // 2 ans

    function addMonths(dateStr, months) {
      const d = new Date(dateStr + 'T00:00:00Z');
      d.setUTCMonth(d.getUTCMonth() + months);
      return d.toISOString().slice(0, 10);
    }

    function diffJours(a, b) {
      return Math.abs((new Date(a + 'T00:00:00Z') - new Date(b + 'T00:00:00Z')) / 86400000);
    }

    const corriges = [];
    const stmt = db.prepare(`
      UPDATE investissements
      SET date_premiere_echeance = ?, date_cible = ?, updated_at = datetime('now')
      WHERE id = ?
    `);

    for (const inv of rows) {
      const dateCalculee = addMonths(inv.date_souscription, inv.duree_mois);
      const ecartEcheance = inv.date_premiere_echeance
        ? diffJours(inv.date_premiere_echeance, dateCalculee) : null;
      const ecartCible = inv.date_cible
        ? diffJours(inv.date_cible, dateCalculee) : null;

      const incoherent =
        (ecartEcheance !== null && ecartEcheance > SEUIL_JOURS) ||
        (ecartCible    !== null && ecartCible    > SEUIL_JOURS) ||
        (inv.date_premiere_echeance === null) ||
        (inv.date_cible             === null);

      if (incoherent) {
        stmt.run(dateCalculee, dateCalculee, inv.id);
        corriges.push({
          id: inv.id,
          nom_projet: inv.nom_projet,
          date_souscription: inv.date_souscription,
          duree_mois: inv.duree_mois,
          ancienne_date_premiere_echeance: inv.date_premiere_echeance,
          ancienne_date_cible: inv.date_cible,
          nouvelle_date: dateCalculee,
        });
      }
    }

    res.json({ updated: corriges.length, detail: corriges });
  } catch (err) {
    next(err);
  }
});

router.get('/:id', (req, res, next) => {
  try {
    const inv = db.prepare(`
      SELECT i.*, p.nom AS plateforme_nom, p.fiscalite AS plateforme_fiscalite, p.logo_filename AS plateforme_logo, cp.nom AS categorie_nom,
             c.id AS compte_id, c.nom AS compte_nom, c.type AS compte_type
      FROM investissements i
      JOIN plateformes p ON p.id = i.plateforme_id
      JOIN investisseurs inv ON inv.id = i.investisseur_id AND inv.user_id = ?
      LEFT JOIN categories_plateforme cp ON cp.id = i.categorie_id
      LEFT JOIN comptes c ON c.id = i.compte_id
      WHERE i.id = ?
    `).get(req.user.id, req.params.id);
    if (!inv) throw new HttpError(404, 'Not found');
    const remboursements = db.prepare(
      'SELECT * FROM remboursements WHERE investissement_id = ? ORDER BY date_remb'
    ).all(req.params.id);
    const simul = db.prepare(
      'SELECT * FROM simul_remboursements WHERE investissement_id = ? ORDER BY numero_echeance'
    ).all(req.params.id);
    const historique = db.prepare(
      'SELECT * FROM investissement_historique WHERE investissement_id = ? ORDER BY created_at ASC'
    ).all(req.params.id).map(h => ({ ...h, changements: JSON.parse(h.changements) }));
    const reinvestissements = db.prepare(
      'SELECT * FROM reinvestissements WHERE investissement_id = ? ORDER BY date_reinvestissement'
    ).all(req.params.id);
    const reinvestissements_total = reinvestissements.reduce((s, r) => s + r.montant, 0);
    const capital_total = inv.montant_investi + reinvestissements_total;
    // Associations catégories/secteurs
    const categories_inv = db.prepare(`
      SELECT c.id, c.nom, CASE WHEN c.user_id IS NULL THEN 1 ELSE 0 END AS is_global
      FROM investissement_categories_inv ic
      JOIN categories_inv c ON c.id = ic.categorie_id
      WHERE ic.investissement_id = ?
      ORDER BY is_global DESC, c.nom
    `).all(req.params.id);
    const secteurs_inv = db.prepare(`
      SELECT s.id, s.nom, CASE WHEN s.user_id IS NULL THEN 1 ELSE 0 END AS is_global
      FROM investissement_secteurs_inv is2
      JOIN secteurs_inv s ON s.id = is2.secteur_id
      WHERE is2.investissement_id = ?
      ORDER BY is_global DESC, s.nom
    `).all(req.params.id);
    res.json({ ...inv, capital_total, reinvestissements_total, remboursements, simul, historique, reinvestissements, categories_inv, secteurs_inv });
  } catch (e) { next(e); }
});

router.post('/', (req, res, next) => {
  try {
    const body = Schema.parse(req.body);
    const investisseurId = resolveInvestisseurId(req, body.investisseur_id);
    const r = db.prepare(`
      INSERT INTO investissements
        (investisseur_id, plateforme_id, nom_projet, emetteur, date_souscription,
         date_premiere_echeance, date_cible, date_debut_simul, montant_investi, taux_interet, duree_mois,
         type_remb, freq_interets, statut, reference, source, notes, categorie_id, echeance_fin_de_mois,
         methode_remboursement, nom_compte_courant, compte_id, pays_exposition)
      VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?, 'manuel', ?,?,?,?,?,?,?)
    `).run(
      investisseurId, body.plateforme_id, body.nom_projet, body.emetteur || null,
      body.date_souscription, body.date_premiere_echeance || null, body.date_cible || null,
      body.date_debut_simul || null, body.montant_investi, body.taux_interet ?? null, body.duree_mois ?? null,
      body.type_remb || null, body.freq_interets, body.statut, body.reference || null, body.notes || null,
      body.categorie_id ?? null, body.echeance_fin_de_mois ?? 0,
      body.methode_remboursement ?? null,
      body.nom_compte_courant || null,
      body.compte_id ?? null,
      body.pays_exposition ?? 'FR',
    );
    const newId = r.lastInsertRowid;
    recordHistory(newId, {
      type_evenement: 'creation',
      changements: [{ champ: 'creation', label: 'Création', ancienne_valeur: null, nouvelle_valeur: body.nom_projet }],
    });
    generateSimul(db, {
      id: newId,
      montant_investi: body.montant_investi,
      taux_interet: body.taux_interet ?? null,
      duree_mois: body.duree_mois ?? null,
      type_remb: body.type_remb || 'in_fine',
      freq_interets: body.freq_interets || 'mensuel',
      date_premiere_echeance: body.date_premiere_echeance || null,
      date_debut_simul: body.date_debut_simul || null,
      date_souscription: body.date_souscription,
      echeance_fin_de_mois: body.echeance_fin_de_mois ?? 0,
    });
    res.status(201).json({ id: newId, ...body });
  } catch (e) { next(e); }
});

router.put('/:id', (req, res, next) => {
  try {
    const body = Schema.parse(req.body);
    const investisseurId = resolveInvestisseurId(req, body.investisseur_id);
    const ancien = db.prepare(`
      SELECT inv_data.*
      FROM investissements inv_data
      JOIN investisseurs inv ON inv.id = inv_data.investisseur_id AND inv.user_id = ?
      WHERE inv_data.id = ?
    `).get(req.user.id, req.params.id);
    if (!ancien) throw new HttpError(404, 'Not found');
    const r = db.prepare(`
      UPDATE investissements
      SET investisseur_id=?, plateforme_id=?, nom_projet=?, emetteur=?, date_souscription=?,
          date_premiere_echeance=?, date_cible=?, date_debut_simul=?, montant_investi=?,
          taux_interet=?, duree_mois=?, type_remb=?, freq_interets=?, statut=?,
          reference=?, notes=?, categorie_id=?, echeance_fin_de_mois=?,
          methode_remboursement=?, nom_compte_courant=?, compte_id=?, pays_exposition=?, updated_at=datetime('now')
      WHERE id=?
    `).run(
      investisseurId, body.plateforme_id, body.nom_projet, body.emetteur || null,
      body.date_souscription, body.date_premiere_echeance || null, body.date_cible || null,
      body.date_debut_simul || null, body.montant_investi, body.taux_interet ?? null,
      body.duree_mois ?? null, body.type_remb || null, body.freq_interets, body.statut,
      body.reference || null, body.notes || null, body.categorie_id ?? null,
      body.echeance_fin_de_mois ?? 0,
      body.methode_remboursement ?? null,
      body.nom_compte_courant || null,
      body.compte_id ?? null,
      body.pays_exposition ?? 'FR', req.params.id,
    );
    if (r.changes === 0) throw new HttpError(404, 'Not found');

    // Cascade compte_id vers les remboursements existants de cet investissement
    if (body.compte_id !== undefined) {
      const newCompteId = body.methode_remboursement === 'compte_courant' ? (body.compte_id ?? null) : null;
      db.prepare(
        "UPDATE remboursements SET compte_id=? WHERE investissement_id=? AND methode_remboursement='compte_courant' AND compte_id IS NOT NULL"
      ).run(newCompteId, req.params.id);
    }

    const changements = detectChangements(ancien, {
      ...body,
      date_debut_simul: body.date_debut_simul || null,
      date_premiere_echeance: body.date_premiere_echeance || null,
      date_cible: body.date_cible || null,
    });
    if (changements.length > 0) {
      recordHistory(Number(req.params.id), {
        type_evenement: detectTypeEvenement(changements),
        changements,
      });
    }
    generateSimul(db, {
      id: Number(req.params.id),
      montant_investi: body.montant_investi,
      taux_interet: body.taux_interet ?? null,
      duree_mois: body.duree_mois ?? null,
      type_remb: body.type_remb || 'in_fine',
      freq_interets: body.freq_interets || 'mensuel',
      date_premiere_echeance: body.date_premiere_echeance || null,
      date_debut_simul: body.date_debut_simul || null,
      date_souscription: body.date_souscription,
      echeance_fin_de_mois: body.echeance_fin_de_mois ?? 0,
    });
    res.json({ id: Number(req.params.id), ...body });
  } catch (e) { next(e); }
});

// PUT /api/investissements/:id/fiscalite-override  { override: 'exonere' | null }
router.put('/:id/fiscalite-override', (req, res, next) => {
  try {
    const inv = db.prepare(
      'SELECT id FROM investissements WHERE id = ? AND investisseur_id IN (SELECT id FROM investisseurs WHERE user_id = ?)'
    ).get(req.params.id, req.user.id);
    if (!inv) throw new HttpError(404, 'Investissement introuvable');

    const override = req.body.override === 'exonere' ? 'exonere' : null;
    db.prepare('UPDATE investissements SET fiscalite_override = ? WHERE id = ?')
      .run(override, req.params.id);
    res.json({ fiscalite_override: override });
  } catch (e) { next(e); }
});

router.delete('/:id', (req, res, next) => {
  try {
    const r = db.prepare(`
      DELETE FROM investissements
      WHERE id = ? AND investisseur_id IN (SELECT id FROM investisseurs WHERE user_id = ?)
    `).run(req.params.id, req.user.id);
    if (r.changes === 0) throw new HttpError(404, 'Not found');
    res.status(204).end();
  } catch (e) { next(e); }
});

router.delete('/:id/historique/:hid', (req, res, next) => {
  try {
    const r = db.prepare(`
      DELETE FROM investissement_historique
      WHERE id = ? AND investissement_id = ?
        AND EXISTS (
          SELECT 1 FROM investissements
          WHERE id = ? AND investisseur_id = ?
        )
    `).run(req.params.hid, req.params.id, req.params.id, req.investisseur.id);
    if (r.changes === 0) throw new HttpError(404, 'Not found');
    res.status(204).end();
  } catch (e) { next(e); }
});

// PUT /api/investissements/:id/auto-reinvest  { active: true|false }
router.put('/:id/auto-reinvest', (req, res, next) => {
  try {
    const inv = db.prepare(
      'SELECT id FROM investissements WHERE id = ? AND investisseur_id = ?'
    ).get(req.params.id, req.investisseur.id);
    if (!inv) throw new HttpError(404, 'Investissement introuvable');

    const active = req.body.active ? 1 : 0;
    db.prepare('UPDATE investissements SET auto_reinvest = ? WHERE id = ?')
      .run(active, req.params.id);
    res.json({ auto_reinvest: !!active });
  } catch (e) { next(e); }
});

export default router;
