/**
 * Three-scenario comparison — the simulator's signature output:
 * A. current declared status, B. RD portage without optimisation,
 * C. RD portage optimised (NDF + May + PER + frais réels).
 * "Vous laissez X €/an sur la table" = C.disposable − A.disposable (signé).
 *
 * All scenarios are ANNUAL and use simplified, documented assumptions
 * (see AUDIT.md §3) — the output is indicative, never tax advice.
 */
import {
  cagnotteRetenue,
  type CagnotteChoice,
  MARKET_AVG_MANAGEMENT_FEE,
  MAY_2026,
  MICRO_BNC_2026,
  RD_PORTAGE_2026,
} from "./constants";
import { computeIr, type HouseholdInput, type IrInput } from "./ir";
import { computePortage } from "./portage";

export type CurrentStatus = "salarie_esn" | "freelance_micro" | "freelance_sasu" | "porte_ailleurs" | "transition";

export interface SimulationInput {
  status: CurrentStatus;
  /** Daily rate (freelance/portage) — for salaried, monthly gross salary. */
  tjmOrMonthlyGross: number;
  /** Invoiced days per year (ignored for salaried). */
  daysPerYear: number;
  household: HouseholdInput;
  /**
   * Frais professionnels remboursés en NDF (étape Activité), sur l'année.
   * Levier du scénario C, plafonné à 30 % du brut dans computePortage.
   * Retours #1/#11 : ces frais n'atteignaient jamais le résultat, le
   * scénario C prenait à leur place les « frais réels » de l'étape Foyer.
   */
  fraisProAnnual?: number;
  /** Optimisation levers (scenario C). Frais réels = déduction d'IMPÔT (étape Foyer). */
  fraisReelsAnnual?: number;
  versementsPER?: number;
  dons?: number;
  revenusFonciers?: number;
  impatrie?: boolean;
  /** Benefits wallet provider for scenario C (default: May reference). */
  cagnotteChoice?: CagnotteChoice;
  /** Gross monthly wallet funding before the provider's service fees. */
  cagnotteMonthly?: number;
  /** Spouse / other household net taxable income (annual), constant across scenarios. */
  revenuConjoint?: number;
}

export interface ScenarioResult {
  id: "actuel" | "portage_rd" | "portage_rd_optimise";
  label: string;
  grossInflow: number;
  netPerceived: number;
  benefits: number;
  tax: number;
  /** Net + benefits − household income tax. */
  disposable: number;
  averageTaxRate: number;
  marginalRate: number;
  details: Record<string, number>;
}

export interface SimulationResult {
  scenarios: ScenarioResult[];
  /**
   * The signature figure: optimised RD portage vs current status.
   * PEUT ÊTRE NÉGATIF — à revenu égal la micro-entreprise reste plus
   * favorable que le portage. Tout consommateur de ce champ doit traiter les
   * deux signes (voir types/simulation-result.ts → laisseSurLaTableSens).
   */
  economieAnnuelleEur: number;
  /** Quick-range output for the flash diagnostic (±15%), bornes ordonnées. */
  economieRange: { low: number; high: number };
}

/** Salaried: net paid ≈ 78.5% of gross, taxable ≈ 81.35% of gross (cadre).
 *  Documented approximation — see AUDIT.md §3. */
const SALARIE = { netRate: 0.785, taxableRate: 0.8135 };

/** SASU (assimilé salarié president): accounting overhead then ≈45% employer /
 *  ≈25% employee blended contributions. Documented approximation. */
const SASU = { overhead: 2_500, employerRate: 0.45, employeeRate: 0.25, taxableUplift: 1.03 };

function irFor(input: SimulationInput, salaryTaxable: number, optimised: boolean) {
  const irInput: IrInput = {
    household: input.household,
    salaryTaxable,
    revenusFonciers: input.revenusFonciers,
    autresRevenus: input.revenuConjoint,
    impatrie: input.impatrie,
    ...(optimised
      ? { fraisReels: input.fraisReelsAnnual, versementsPER: input.versementsPER, dons: input.dons }
      : {}),
  };
  return computeIr(irInput);
}

function currentScenario(input: SimulationInput): ScenarioResult {
  const h = input.household;
  let gross = 0;
  let netPerceived = 0;
  let taxable = 0;
  const details: Record<string, number> = {};

  switch (input.status) {
    case "salarie_esn": {
      gross = input.tjmOrMonthlyGross * 12;
      netPerceived = gross * SALARIE.netRate;
      taxable = gross * SALARIE.taxableRate;
      details.salaireBrutAnnuel = Math.round(gross);
      break;
    }
    case "freelance_micro": {
      gross = input.tjmOrMonthlyGross * input.daysPerYear;
      const cotisations = gross * MICRO_BNC_2026.cotisations;
      netPerceived = gross - cotisations;
      taxable = gross * (1 - MICRO_BNC_2026.abattement);
      details.cotisationsSociales = Math.round(cotisations);
      break;
    }
    case "freelance_sasu": {
      gross = input.tjmOrMonthlyGross * input.daysPerYear;
      const envelope = Math.max(gross - SASU.overhead, 0);
      const brut = envelope / (1 + SASU.employerRate);
      netPerceived = brut * (1 - SASU.employeeRate);
      taxable = netPerceived * SASU.taxableUplift;
      details.fraisComptables = SASU.overhead;
      details.salaireBrutAnnuel = Math.round(brut);
      break;
    }
    case "porte_ailleurs": {
      // Same portage mechanics with the market-average 8% management fee,
      // no May wallet, no meal vouchers (worst observed configurations).
      gross = input.tjmOrMonthlyGross * input.daysPerYear;
      const fee = gross * MARKET_AVG_MANAGEMENT_FEE;
      const insurance = gross * RD_PORTAGE_2026.insuranceTaxRate;
      const available = gross - fee - insurance;
      const brut = available / (1 + RD_PORTAGE_2026.employerRate);
      netPerceived = brut * (1 - RD_PORTAGE_2026.employeeRate);
      taxable = netPerceived + brut * RD_PORTAGE_2026.csgNonDeductibleRate;
      details.fraisGestionConcurrent = Math.round(fee);
      break;
    }
    case "transition": {
      // Between two missions: baseline = future revenue handled as micro,
      // the most common "default" first status.
      gross = input.tjmOrMonthlyGross * input.daysPerYear;
      const cotisations = gross * MICRO_BNC_2026.cotisations;
      netPerceived = gross - cotisations;
      taxable = gross * (1 - MICRO_BNC_2026.abattement);
      break;
    }
  }

  // Current status: standard 10% deduction, no optimisation levers.
  const ir = computeIr({
    household: h,
    salaryTaxable: taxable,
    revenusFonciers: input.revenusFonciers,
    autresRevenus: input.revenuConjoint,
  });
  return {
    id: "actuel",
    label: "Votre statut actuel",
    grossInflow: Math.round(gross),
    netPerceived: Math.round(netPerceived),
    benefits: 0,
    tax: ir.tax,
    disposable: Math.round(netPerceived - ir.tax),
    averageTaxRate: ir.averageRate,
    marginalRate: ir.marginalRate,
    details,
  };
}

function portageScenario(input: SimulationInput, optimised: boolean): ScenarioResult {
  const annualFees =
    input.status === "salarie_esn"
      ? // ESN consultants moving to freelance: estimate TJM from gross salary
        // (common rule of thumb: TJM ≈ monthly gross / 13.3 working value) —
        // here we keep their billing potential = salary cost × 1.25.
        input.tjmOrMonthlyGross * 12 * 1.25
      : input.tjmOrMonthlyGross * input.daysPerYear;
  const days = input.status === "salarie_esn" ? 215 : input.daysPerYear;

  // NDF : le plafond (30 % du brut) est appliqué dans computePortage.
  const ndf = optimised ? input.fraisProAnnual ?? 0 : 0;
  // Cagnotte : valeur nette de frais et coût prélevé, plafonnés à 20 % du CA
  // — même règle que l'aperçu (cagnotteRetenue, source unique).
  const cagnotteGrossMonthly = input.cagnotteMonthly ?? MAY_2026.referenceMonthlyAmount;
  const cagnotte = optimised
    ? cagnotteRetenue(input.cagnotteChoice ?? "may", cagnotteGrossMonthly * 12, annualFees)
    : { value: 0, cost: 0 };

  const p = computePortage({
    tjm: annualFees / Math.max(days, 1),
    days,
    ndf,
    cagnotteMay: cagnotte.value,
    cagnotteCost: cagnotte.cost,
    mealVouchers: optimised,
  });

  const ir = irFor(input, p.netTaxable, optimised);
  const netPerceived = p.netWithExpenses + p.mealVoucherCredit;
  return {
    id: optimised ? "portage_rd_optimise" : "portage_rd",
    label: optimised ? "Portage RD optimisé" : "Portage RD sans optimisation",
    grossInflow: p.fees,
    netPerceived: Math.round(netPerceived),
    benefits: p.cagnotteMay,
    tax: ir.tax,
    disposable: Math.round(netPerceived + p.cagnotteMay - ir.tax),
    averageTaxRate: ir.averageRate,
    marginalRate: ir.marginalRate,
    details: {
      fraisGestion: p.managementFee,
      assurancesTaxes: p.insuranceTax,
      salaireBrutAnnuel: p.grossSalary,
      ndf: p.ndf,
      cagnotteMay: p.cagnotteMay,
      perDeduit: ir.perDeduction,
    },
  };
}

export function simulate(input: SimulationInput): SimulationResult {
  const actuel = currentScenario(input);
  const portage = portageScenario(input, false);
  const optimise = portageScenario(input, true);

  // L'écart est rendu TEL QUEL, signe compris. Il était auparavant écrêté par
  // un Math.max(..., 0) : à revenu égal, la micro-entreprise reste plus
  // favorable que le portage, et l'écrêtage transformait cet écart négatif en
  // un « 0 € » affiché sans explication — le profil par défaut
  // (freelance_micro) tombait systématiquement dessus (BUG-02).
  // Décision client du 15/09 : afficher l'écart réel, même négatif, et le
  // mettre en regard de ce que le portage apporte en plus (protection
  // sociale, chômage, avantages). Aucun barème ni aucune cascade n'est
  // modifié ici : seul l'écrêtage disparaît.
  const economie = optimise.disposable - actuel.disposable;
  const bounds = [Math.round(economie * 0.85), Math.round(economie * 1.15)];
  return {
    scenarios: [actuel, portage, optimise],
    economieAnnuelleEur: economie,
    // min/max explicites : avec un écart négatif, ×1,15 est la borne BASSE.
    economieRange: { low: Math.min(...bounds), high: Math.max(...bounds) },
  };
}
