import { describe, it, expect } from "vitest";
import {
  berekenAkteKostenOverzicht,
  berekenForfaitaireAfrekening,
  berekenHonorarium,
  kiesBarema,
  getRetributies,
  AKTEKOSTEN_2026,
  RECHT_OP_GESCHRIFTEN_ALGEMEEN,
  BTW_TARIEF,
} from "@/data/ereloon";

describe("kiesBarema", () => {
  it("verkoop zonder eigen woning kiest barema J", () => {
    expect(kiesBarema("verkoop", { eigenWoning: false })).toBe("J");
  });

  it("verkoop met eigen woning kiest het verlaagd barema Jbis", () => {
    expect(kiesBarema("verkoop", { eigenWoning: true })).toBe("Jbis");
  });

  it("hypotheek met eigen woning kiest Gbis, anders G", () => {
    expect(kiesBarema("hypotheek", { eigenWoning: true })).toBe("Gbis");
    expect(kiesBarema("hypotheek", { eigenWoning: false })).toBe("G");
  });

  it("schenking bij voorschot op erfdeel kiest F, buiten deel kiest H", () => {
    expect(kiesBarema("schenking-onroerend", { schenkingsWijze: "voorschot" })).toBe("F");
    expect(kiesBarema("schenking-onroerend", { schenkingsWijze: "buiten_deel" })).toBe("H");
    expect(kiesBarema("schenking-roerend", { schenkingsWijze: "voorschot" })).toBe("F");
  });

  it("verdeling kiest altijd H, ruil/erfpacht kiezen altijd J", () => {
    expect(kiesBarema("verdeling")).toBe("H");
    expect(kiesBarema("ruil")).toBe("J");
    expect(kiesBarema("erfpacht")).toBe("J");
  });
});

describe("berekenHonorarium — barema J (verkoop algemeen)", () => {
  it("250.000 EUR over zes schijven (KB 1950 Schaal J)", () => {
    // Schijven: 7.500*2,5% + 10.000*2,5% + 12.500*1,75% + 15.495*1,71% + 18.595*1,14% + 185.910*0,57%
    // = 187,50 + 250 + 218,75 + 264,9645 + 211,983 + 1.059,687 = 2.192,8845 (variabel)
    // + vast gedeelte 285 = 2.477,8845
    const r = berekenHonorarium(250_000, "J");
    expect(r.honorarium).toBeCloseTo(2477.8845, 4);
    expect(r.vastGedeelte).toBe(285);
    expect(r.detail).toHaveLength(6);
  });

  it("exact op de eerste schijfgrens (7.500): enkel de 2,5%-schijf", () => {
    // 7.500 * 2,5% = 187,50 + vast gedeelte 285 = 472,50
    const r = berekenHonorarium(7_500, "J");
    expect(r.honorarium).toBeCloseTo(472.5, 6);
    expect(r.detail).toHaveLength(1);
  });

  it("zeer hoog bedrag (1.000.000) doorloopt ook de laatste open schijf (0,2%)", () => {
    // Eerste 500.000 zoals barema J tot 500.000, plus 500.000 extra op 0,2% = 1.000
    const op500k = berekenHonorarium(500_000, "J").honorarium;
    const op1m = berekenHonorarium(1_000_000, "J").honorarium;
    expect(op1m - op500k).toBeCloseTo(1000, 6);
  });
});

describe("berekenHonorarium — Jbis vs. J (eigen woning korting)", () => {
  it("Jbis (eigen woning) geeft een lager honorarium dan J op hetzelfde bedrag", () => {
    const j = berekenHonorarium(250_000, "J").honorarium;
    const jbis = berekenHonorarium(250_000, "Jbis").honorarium;
    // Jbis: 7.500*2,5% + 10.000*2,5% + 12.500*2% + 15.495*1,5% + 18.595*0,5% + 185.910*0,485%
    // = 187,5 + 250 + 250 + 232,425 + 92,975 + 901,6635 = 1.914,5635 (variabel) + 257 = 2.171,5635
    expect(jbis).toBeCloseTo(2171.5635, 4);
    expect(jbis).toBeLessThan(j);
  });
});

describe("berekenHonorarium — schenkingsbarema's F (voorschot) en H (buiten deel)", () => {
  it("F op 50.000 EUR (schenking als voorschot op erfdeel)", () => {
    // 7.500*1,425% + 10.000*1,14% + 12.500*0,684% + 15.495*0,57% + 4.505*0,456%
    // = 106,875 + 114 + 85,5 + 88,3215 + 20,5428 = 415,2393 (geen vast gedeelte)
    const r = berekenHonorarium(50_000, "F");
    expect(r.honorarium).toBeCloseTo(415.2393, 4);
    expect(r.vastGedeelte).toBe(0);
  });

  it("H op 50.000 EUR (schenking buiten deel / verdeling) ligt hoger dan F", () => {
    // 7.500*2,85% + 10.000*1,71% + 12.500*1,425% + 15.495*1,14% + 4.505*0,855%
    // = 213,75 + 171 + 178,125 + 176,643 + 38,51775 = 778,03575
    const f = berekenHonorarium(50_000, "F").honorarium;
    const h = berekenHonorarium(50_000, "H").honorarium;
    expect(h).toBeCloseTo(778.03575, 4);
    expect(h).toBeGreaterThan(f);
  });
});

describe("getRetributies — hypothecaire retributies (btw-vrije posten)", () => {
  it("verkoop: overschrijving (285, btw-vrij) en opzoeking (180, btw-plichtig)", () => {
    const r = getRetributies("verkoop", 250_000);
    const overschrijving = r.find((k) => k.id === "ret-overschrijving");
    const opzoeking = r.find((k) => k.id === "ret-opzoeking");
    expect(overschrijving?.bedrag).toBe(285);
    expect(overschrijving?.btwPlichtig).toBe(false);
    expect(opzoeking?.bedrag).toBe(180);
    expect(opzoeking?.btwPlichtig).toBeUndefined(); // default = btw-plichtig
  });

  it("hypotheek onder 300.000 (verzekerd bedrag): inschrijving 270 EUR", () => {
    // Verzekerd bedrag = 200.000 * 1,1 = 220.000 <= 300.000
    const r = getRetributies("hypotheek", 200_000);
    expect(r[0].bedrag).toBe(270);
    expect(r[0].btwPlichtig).toBe(false);
  });

  it("hypotheek boven 300.000 (verzekerd bedrag): inschrijving 1.160 EUR", () => {
    // Verzekerd bedrag = 280.000 * 1,1 = 308.000 > 300.000
    const r = getRetributies("hypotheek", 280_000);
    expect(r[0].bedrag).toBe(1160);
  });

  it("hypotheek exact op de grens (verzekerd bedrag = 300.000) telt nog als 'tot 300k'", () => {
    // bedrag * 1.1 = 300.000 → bedrag = 272.727,27...
    const r = getRetributies("hypotheek", 300_000 / 1.1);
    expect(r[0].bedrag).toBe(270);
  });

  it("schenking-roerend en hypotheek-mandaat hebben geen retributies", () => {
    expect(getRetributies("schenking-roerend", 50_000)).toEqual([]);
    expect(getRetributies("hypotheek-mandaat", 100_000)).toEqual([]);
  });
});

describe("berekenAkteKostenOverzicht — verkoop", () => {
  it("geeft null terug voor een bedrag van 0 of minder", () => {
    expect(berekenAkteKostenOverzicht("verkoop", 0, "Vlaanderen")).toBeNull();
    expect(berekenAkteKostenOverzicht("verkoop", -10, "Vlaanderen")).toBeNull();
  });

  it("250.000 EUR, Vlaanderen, geen eigen woning: correcte opbouw en btw 21%", () => {
    const r = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen");
    expect(r).not.toBeNull();
    if (!r) return;

    expect(r.baremaCode).toBe("J");
    // Honorarium barema J op 250.000 (zie berekenHonorarium-test): 2.477,8845
    expect(r.honorarium).toBeCloseTo(2477.8845, 4);
    // Forfaitaire aktekost hoofdakte (niet-verbonden)
    expect(r.aktekost).toBe(855);

    // Individualiseerbare (btw-plichtige) kosten: pack-immo 55 + pandregister 7*2 (default aantal)
    // + recht-op-geschriften 100 + hypothecaire opzoeking 180 (btw-plichtig) = 349
    expect(r.indBedrag).toBeCloseTo(349, 6);

    // Btw-vrije kosten: enkel de overschrijving van de akte (285) op 1 overdrager (default)
    expect(r.vrijgesteldBedrag).toBe(285);

    // Leveringskosten Vlaanderen voor verkoop: bodemattest 40,5 (standaard) + maatregelenregister 7,9
    // (stedenbouwkundig uittreksel is bij verkoop niet standaard aangevinkt)
    expect(r.levBedrag).toBeCloseTo(48.4, 6);

    const subtotaalVerwacht = 2477.8845 + 855 + 349 + 48.4;
    expect(r.subtotaalExclBtw).toBeCloseTo(subtotaalVerwacht, 4);
    expect(r.btw).toBeCloseTo(subtotaalVerwacht * BTW_TARIEF, 4);
    expect(r.totaal).toBeCloseTo(subtotaalVerwacht * (1 + BTW_TARIEF) + 285, 4);
  });

  it("btw wordt nooit toegepast op de vrijgesteldeKosten (hypothecaire retributies)", () => {
    const r = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen");
    expect(r).not.toBeNull();
    if (!r) return;
    // De btw is louter btw% * subtotaalExclBtw, dat vrijgesteldBedrag niet bevat.
    expect(r.btw).toBeCloseTo(r.subtotaalExclBtw * BTW_TARIEF, 6);
    // En vrijgesteldBedrag wordt apart, zonder btw, bij het totaal opgeteld.
    expect(r.totaal).toBeCloseTo(r.subtotaalExclBtw + r.btw + r.vrijgesteldBedrag, 6);
  });

  it("eigen woning (Jbis) geeft een lager totaal dan algemeen tarief (J) op hetzelfde bedrag", () => {
    const algemeen = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen", { eigenWoning: false });
    const eigenWoning = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen", { eigenWoning: true });
    expect(algemeen).not.toBeNull();
    expect(eigenWoning).not.toBeNull();
    if (!algemeen || !eigenWoning) return;
    expect(eigenWoning.baremaCode).toBe("Jbis");
    expect(eigenWoning.honorarium).toBeLessThan(algemeen.honorarium);
    expect(eigenWoning.totaal).toBeLessThan(algemeen.totaal);
  });

  it("meer overdragers verhoogt de btw-vrije retributie (overschrijving 'per overdrager')", () => {
    const eenOverdrager = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen", { aantalOverdragers: 1 });
    const drieOverdragers = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen", { aantalOverdragers: 3 });
    expect(eenOverdrager).not.toBeNull();
    expect(drieOverdragers).not.toBeNull();
    if (!eenOverdrager || !drieOverdragers) return;
    // ret-overschrijving (285, btw-vrij) en ret-opzoeking (180, btw-plichtig) zijn "per overdrager".
    expect(drieOverdragers.vrijgesteldBedrag).toBe(285 * 3);
    expect(eenOverdrager.vrijgesteldBedrag).toBe(285);
    // Verschil in indBedrag = 2 extra opzoekingen van 180.
    expect(drieOverdragers.indBedrag - eenOverdrager.indBedrag).toBeCloseTo(180 * 2, 6);
  });

  it("meer pandregister-opzoekingen verhoogt enkel het pandregister-deel (btw-plichtig, €7/stuk)", () => {
    const tweeOpzoekingen = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen", { aantalPandregister: 2 });
    const vijfOpzoekingen = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen", { aantalPandregister: 5 });
    expect(tweeOpzoekingen).not.toBeNull();
    expect(vijfOpzoekingen).not.toBeNull();
    if (!tweeOpzoekingen || !vijfOpzoekingen) return;
    expect(vijfOpzoekingen.indBedrag - tweeOpzoekingen.indBedrag).toBeCloseTo(7 * 3, 6);
  });

  it("extraLeveringskostenIds neemt een niet-standaard leveringskost (bv. lev-syndicus) mee op", () => {
    const zonder = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen");
    const met = berekenAkteKostenOverzicht("verkoop", 250_000, "Vlaanderen", {
      extraLeveringskostenIds: ["lev-syndicus"],
    });
    expect(zonder).not.toBeNull();
    expect(met).not.toBeNull();
    if (!zonder || !met) return;
    expect(zonder.levKosten.some((k) => k.id === "lev-syndicus")).toBe(false);
    expect(met.levKosten.some((k) => k.id === "lev-syndicus")).toBe(true);
    expect(met.levBedrag - zonder.levBedrag).toBeCloseTo(75, 6);
  });
});

describe("berekenAkteKostenOverzicht — schenking-roerend (geen leveringskosten/retributies)", () => {
  it("50.000 EUR buiten deel (H): correcte opbouw, geen levering en geen vrijgestelde kosten", () => {
    const r = berekenAkteKostenOverzicht("schenking-roerend", 50_000, "Vlaanderen", {
      schenkingsWijze: "buiten_deel",
    });
    expect(r).not.toBeNull();
    if (!r) return;
    expect(r.baremaCode).toBe("H");
    // Honorarium H op 50.000 = 778,03575 (zie berekenHonorarium-test)
    expect(r.honorarium).toBeCloseTo(778.03575, 4);
    // Forfait schenking roerend
    expect(r.aktekost).toBe(300);
    // Individualiseerbaar: pack-famille 25 + pandregister 7*2 + recht-op-geschriften 50 (roerend) = 89
    expect(r.indBedrag).toBeCloseTo(89, 6);
    // Geen hypothecaire retributies en geen leveringskosten bij schenking-roerend.
    expect(r.vrijgesteldBedrag).toBe(0);
    expect(r.levBedrag).toBe(0);
    expect(r.levKosten).toEqual([]);

    const subtotaalVerwacht = 778.03575 + 300 + 89;
    expect(r.subtotaalExclBtw).toBeCloseTo(subtotaalVerwacht, 4);
    expect(r.btw).toBeCloseTo(subtotaalVerwacht * BTW_TARIEF, 4);
    expect(r.totaal).toBeCloseTo(subtotaalVerwacht * (1 + BTW_TARIEF), 4);
  });

  it("recht op geschriften is lager voor schenking-roerend (50) dan voor andere akten (100)", () => {
    const roerend = berekenAkteKostenOverzicht("schenking-roerend", 50_000, "Vlaanderen");
    const onroerend = berekenAkteKostenOverzicht("schenking-onroerend", 50_000, "Vlaanderen", {
      schenkingsWijze: "voorschot",
    });
    expect(roerend).not.toBeNull();
    expect(onroerend).not.toBeNull();
    if (!roerend || !onroerend) return;
    const geschriftenRoerend = 50;
    const geschriftenOnroerend = 100;
    expect(geschriftenRoerend).toBeLessThan(geschriftenOnroerend);
  });
});

describe("berekenAkteKostenOverzicht — schenking-onroerend (voorschot, Wallonië)", () => {
  it("50.000 EUR als voorschot op erfdeel (F): leveringskosten en btw-vrije overschrijving", () => {
    const r = berekenAkteKostenOverzicht("schenking-onroerend", 50_000, "Wallonië", {
      schenkingsWijze: "voorschot",
    });
    expect(r).not.toBeNull();
    if (!r) return;
    expect(r.baremaCode).toBe("F");
    expect(r.honorarium).toBeCloseTo(415.2393, 4);
    expect(r.aktekost).toBe(1200);
    // Individualiseerbaar: pack-famille 25 + pandregister 7*2 (=14) + recht-op-geschriften 100 + opzoeking 180 = 319
    expect(r.indBedrag).toBeCloseTo(319, 6);
    // Btw-vrije overschrijving van de akte: 285 (1 overdrager default)
    expect(r.vrijgesteldBedrag).toBe(285);
    // Leveringskosten Wallonië, schenking-onroerend: bodemattest 40 + stedenbouw 100
    // (stedenStandaard is true in Wallonië voor schenking-onroerend)
    expect(r.levBedrag).toBe(140);

    const subtotaalVerwacht = 415.2393 + 1200 + 319 + 140;
    expect(r.subtotaalExclBtw).toBeCloseTo(subtotaalVerwacht, 4);
    expect(r.totaal).toBeCloseTo(subtotaalVerwacht * (1 + BTW_TARIEF) + 285, 4);
  });
});

describe("berekenAkteKostenOverzicht — hypotheek (kredietakte, nu actief)", () => {
  it("200.000 EUR krediet, Vlaanderen: barema G, hypotheekinschrijving als btw-vrije retributie", () => {
    const r = berekenAkteKostenOverzicht("hypotheek", 200_000, "Vlaanderen");
    expect(r).not.toBeNull();
    if (!r) return;
    expect(r.baremaCode).toBe("G");
    // Inschrijving van de hypotheek is een btw-vrije retributie (verzekerd bedrag
    // 220.000 ≤ 300.000 → 270 EUR, zie getRetributies-test).
    expect(r.vrijgesteldKosten.some((k) => k.id === "ret-inschrijving")).toBe(true);
    expect(r.vrijgesteldBedrag).toBe(270);
    // Btw raakt de btw-vrije inschrijving nooit aan.
    expect(r.btw).toBeCloseTo(r.subtotaalExclBtw * BTW_TARIEF, 6);
    expect(r.totaal).toBeCloseTo(r.subtotaalExclBtw + r.btw + r.vrijgesteldBedrag, 6);
  });

  it("eigen woning (Gbis) geeft een lager honorarium dan algemeen tarief (G)", () => {
    const algemeen = berekenAkteKostenOverzicht("hypotheek", 200_000, "Vlaanderen", { eigenWoning: false });
    const eigenWoning = berekenAkteKostenOverzicht("hypotheek", 200_000, "Vlaanderen", { eigenWoning: true });
    expect(algemeen).not.toBeNull();
    expect(eigenWoning).not.toBeNull();
    if (!algemeen || !eigenWoning) return;
    expect(algemeen.baremaCode).toBe("G");
    expect(eigenWoning.baremaCode).toBe("Gbis");
    expect(eigenWoning.honorarium).toBeLessThan(algemeen.honorarium);
  });
});

describe("berekenAkteKostenOverzicht — verdeling", () => {
  it("verdeling heeft geen pandregister-aftrek per overdrager maar wel standaard kosten", () => {
    const r = berekenAkteKostenOverzicht("verdeling", 100_000, "Vlaanderen");
    expect(r).not.toBeNull();
    if (!r) return;
    expect(r.baremaCode).toBe("H");
    expect(r.aktekost).toBe(1200);
  });
});

describe("berekenForfaitaireAfrekening — akte van onbepaalde waarde", () => {
  it("hoofdakte: forfaitaire aktekost + recht op geschriften, btw enkel op de aktekost", () => {
    const r = berekenForfaitaireAfrekening("hoofdakte");
    expect(r.aktekost.bedrag).toBe(AKTEKOSTEN_2026.hoofdakte);
    expect(r.rechtOpGeschriften).toBe(RECHT_OP_GESCHRIFTEN_ALGEMEEN);
    // Btw enkel op de aktekost (het recht op geschriften is btw-vrij).
    expect(r.btw).toBeCloseTo(AKTEKOSTEN_2026.hoofdakte * BTW_TARIEF, 6);
    expect(r.totaal).toBeCloseTo(r.subtotaalExclBtw + r.btw + r.rechtOpGeschriften, 6);
    // Het honorarium blijft uitdrukkelijk aan de notaris.
    expect(r.honorariumToelichting).toMatch(/notaris/i);
  });

  it("bv-standaard gebruikt het lagere BV-forfait (€314), niet het hoofdaktetarief", () => {
    const bv = berekenForfaitaireAfrekening("bv-standaard");
    expect(bv.aktekost.bedrag).toBe(AKTEKOSTEN_2026.bvStandaard);
    expect(bv.aktekost.bedrag).toBeLessThan(AKTEKOSTEN_2026.hoofdakte);
  });
});
