import { describe, it, expect } from "vitest";
// ── Boekhoudmodule (AUT-S8): het verkoopdecompte ─────────────────────────────
// Bewaakt de drie financiële werkdocumenten per verkoopakte: het décompte van
// de koper, het décompte van de verkoper en het betalingsoverzicht van het
// kantoor — inclusief de kernregel van de notaris: een makelaarsfactuur die
// al op het voorschot is ingehouden, wordt NIET nogmaals betaald.
import { stelVerkoopDecompteSamen } from "./verkoopdecompte";
import { stelAfrekeningSamenAutonoom } from "./afrekening";
import { maakVeld, type Dossier, type DerdengeldOntvangst } from "./types";

function maakVerkoop(overschrijf: Partial<Dossier> = {}): Dossier {
  return {
    id: "vd-1",
    dossiertype: "verkoop-zonder-krediet",
    status: "klaar-voor-akte",
    partijen: maakVeld(
      [
        { naam: "PEETERS", voornaam: "Jan", rol: "verkoper" },
        { naam: "MAES", voornaam: "Sofie", rol: "koper" },
      ],
      "compromis"
    ),
    goed: maakVeld({ adres: "Bosstraat 45, 3080 Tervuren", type: "woning" }, "compromis"),
    prijs: maakVeld(400000, "compromis"),
    voorschot: maakVeld(40000, "compromis"),
    compromisdatum: maakVeld("2026-05-12", "compromis"),
    makelaarNaam: "IMMO TERVUREN",
    makelaarsFactuur: { bedrag: 12000, ingehoudenOpVoorschot: false },
    hypothecaireLasten: maakVeld([{ schuldeiser: "KBC Bank", bedrag: 150000, moetGeroyeerdWorden: true }], "hypotheekattest"),
    ontbrekendeStukken: [],
    tegenstrijdigheden: [],
    aangemaakt: "2026-06-01T00:00:00.000Z",
    aangepast: "2026-06-01T00:00:00.000Z",
    ...overschrijf,
  } as Dossier;
}

const ONTVANGST_VOORSCHOT: DerdengeldOntvangst = { id: "o1", datum: "2026-06-20", bedrag: 40000, van: "makelaar", mededeling: "doorstorting voorschot" };

describe("stelVerkoopDecompteSamen — décompte koper", () => {
  it("telt prijs + aktekosten + verkooprecht en trekt voorschot en ontvangsten van de koperszijde af", () => {
    const dossier = maakVerkoop({ derdengeldenOntvangsten: [ONTVANGST_VOORSCHOT] });
    const d = stelVerkoopDecompteSamen(dossier, "Vlaanderen")!;
    const afrekening = stelAfrekeningSamenAutonoom(dossier, "Vlaanderen");
    const kosten = afrekening.aktekosten!.totaal;
    const heffing = afrekening.registratiebelasting!.bedrag;
    expect(heffing).toBe(48000); // 12% algemeen tarief (koperEnigeEigenWoning onbekend)
    expect(d.koper.totaalVerschuldigd).toBeCloseTo(400000 + kosten + heffing, 2);
    // Voorschot (compromis) + doorstorting agentschap tellen samen als voldaan.
    expect(d.koper.reedsVoldaan).toBe(40000 + 40000);
    expect(d.koper.saldoTeOntvangen).toBeCloseTo(400000 + kosten + heffing - 80000, 2);
  });

  it("gebruikt exact dezelfde kosten en heffing als de afrekeningspijler (één bron van waarheid)", () => {
    const dossier = maakVerkoop();
    const d = stelVerkoopDecompteSamen(dossier, "Vlaanderen")!;
    const afrekening = stelAfrekeningSamenAutonoom(dossier, "Vlaanderen");
    const kostenRegel = d.koper.regels.find((r) => /aktekosten/i.test(r.omschrijving))!;
    expect(kostenRegel.bedrag).toBe(afrekening.aktekosten!.totaal);
  });

  it("meldt een dubbel geteld voorschot in plaats van stil een negatief saldo te tonen", () => {
    // Voorschot al in het dossier ÉN een ontvangst 'van de koper' voor het
    // volledige verschuldigde bedrag → meer voldaan dan verschuldigd.
    const d = stelVerkoopDecompteSamen(
      maakVerkoop({ derdengeldenOntvangsten: [{ id: "o2", datum: "2026-06-25", bedrag: 500000, van: "koper" }] }),
      "Vlaanderen"
    )!;
    expect(d.koper.saldoTeOntvangen).toBeLessThan(0);
    expect(d.onzekerheden.some((o) => /méér voldaan/i.test(o))).toBe(true);
  });
});

describe("stelVerkoopDecompteSamen — décompte verkoper", () => {
  it("verkoper ontvangt prijs − commissie − hypothecaire aflossingen", () => {
    const d = stelVerkoopDecompteSamen(maakVerkoop(), "Vlaanderen")!;
    expect(d.verkoper.uitTeBetalen).toBe(400000 - 12000 - 150000);
  });

  it("het nettosaldo van de verkoper is identiek mét of zónder inhouding op het voorschot", () => {
    const zonder = stelVerkoopDecompteSamen(maakVerkoop(), "Vlaanderen")!;
    const met = stelVerkoopDecompteSamen(
      maakVerkoop({ makelaarsFactuur: { bedrag: 12000, ingehoudenOpVoorschot: true } }),
      "Vlaanderen"
    )!;
    expect(met.verkoper.uitTeBetalen).toBe(zonder.verkoper.uitTeBetalen);
    expect(met.verkoper.regels.find((r) => /ingehouden/i.test(r.omschrijving))).toBeTruthy();
  });

  it("waarschuwt wanneer de aftrekposten de prijs overtreffen (verkoper moet bijpassen)", () => {
    const d = stelVerkoopDecompteSamen(
      maakVerkoop({ hypothecaireLasten: maakVeld([{ schuldeiser: "KBC Bank", bedrag: 395000, moetGeroyeerdWorden: true }], "hypotheekattest") }),
      "Vlaanderen"
    )!;
    expect(d.verkoper.uitTeBetalen).toBeLessThan(0);
    expect(d.onzekerheden.some((o) => /bijpassen/i.test(o))).toBe(true);
  });
});

describe("stelVerkoopDecompteSamen — betalingen door het kantoor", () => {
  it("plant fiscus, royement, makelaar, kantoor en verkopersaldo wanneer de factuur NIET is ingehouden", () => {
    const d = stelVerkoopDecompteSamen(maakVerkoop(), "Vlaanderen")!;
    const begunstigden = d.betalingen.map((b) => b.begunstigde);
    expect(begunstigden).toContain("Vlaamse Belastingdienst");
    expect(begunstigden).toContain("KBC Bank");
    expect(begunstigden).toContain("IMMO TERVUREN");
    expect(begunstigden).toContain("Kantoorrekening");
    expect(begunstigden).toContain("Verkoper");
    expect(d.betalingen.find((b) => b.begunstigde === "IMMO TERVUREN")!.status).not.toBe("vervalt-ingehouden");
  });

  it("laat de makelaarsfactuur VERVALLEN wanneer ze al op het voorschot is ingehouden (niet tweemaal betalen)", () => {
    const d = stelVerkoopDecompteSamen(
      maakVerkoop({ makelaarsFactuur: { bedrag: 12000, ingehoudenOpVoorschot: true } }),
      "Vlaanderen"
    )!;
    const makelaar = d.betalingen.filter((b) => b.begunstigde === "IMMO TERVUREN");
    expect(makelaar).toHaveLength(1);
    expect(makelaar[0].status).toBe("vervalt-ingehouden");
    // ... en ze telt niet mee in het uit te voeren totaal.
    expect(d.derdenrekening.totaalUitTeVoeren).toBe(
      stelVerkoopDecompteSamen(maakVerkoop(), "Vlaanderen")!.derdenrekening.totaalUitTeVoeren - 12000
    );
  });

  it("markeert betalingen sequentieel als 'gedekt' zodra de geëncodeerde ontvangsten ze dragen (saldo nooit negatief)", () => {
    const d = stelVerkoopDecompteSamen(
      maakVerkoop({ derdengeldenOntvangsten: [{ id: "o3", datum: "2026-06-28", bedrag: 60000, van: "koper" }] }),
      "Vlaanderen"
    )!;
    // Eerste betaling (verkooprecht 48.000) is gedekt; de volgende (royement
    // 150.000) niet meer — 12.000 restsaldo dekt ze niet.
    expect(d.betalingen[0].status).toBe("gedekt");
    expect(d.betalingen[1].status).toBe("te-betalen");
  });

  it("het dossier sluit: ontvangsten die het décompte van de koper volledig dekken ⇒ saldo na betalingen is nul", () => {
    const basis = stelVerkoopDecompteSamen(maakVerkoop(), "Vlaanderen")!;
    // De koperszijde stort exact alles (voorschot via agentschap + saldo).
    const d = stelVerkoopDecompteSamen(
      maakVerkoop({
        derdengeldenOntvangsten: [
          ONTVANGST_VOORSCHOT,
          { id: "o4", datum: "2026-07-01", bedrag: basis.koper.totaalVerschuldigd - 40000, van: "koper" },
        ],
      }),
      "Vlaanderen"
    )!;
    expect(d.derdenrekening.saldoNaBetalingen).toBeCloseTo(0, 2);
    expect(d.betalingen.every((b) => b.status === "gedekt" || b.status === "vervalt-ingehouden")).toBe(true);
  });
});

describe("stelVerkoopDecompteSamen — eerlijkheid en grenzen", () => {
  it("geeft null voor niet-verkoopdossiers en lijfrenteverkopen (geen vaste prijsgrondslag)", () => {
    expect(stelVerkoopDecompteSamen(maakVerkoop({ dossiertype: "schenking" }), "Vlaanderen")).toBeNull();
    expect(stelVerkoopDecompteSamen(maakVerkoop({ verkoopRegime: "lijfrente" }), "Vlaanderen")).toBeNull();
  });

  it("meldt een agentschap zonder geëncodeerde factuur, een ontbrekend voorschot en een inhouding groter dan het voorschot", () => {
    const zonderFactuur = stelVerkoopDecompteSamen(maakVerkoop({ makelaarsFactuur: undefined }), "Vlaanderen")!;
    expect(zonderFactuur.onzekerheden.some((o) => /geen commissiefactuur/i.test(o))).toBe(true);

    const zonderVoorschot = stelVerkoopDecompteSamen(maakVerkoop({ voorschot: undefined }), "Vlaanderen")!;
    expect(zonderVoorschot.onzekerheden.some((o) => /voorschot/i.test(o))).toBe(true);

    const teGroteInhouding = stelVerkoopDecompteSamen(
      maakVerkoop({ makelaarsFactuur: { bedrag: 45000, ingehoudenOpVoorschot: true } }),
      "Vlaanderen"
    )!;
    expect(teGroteInhouding.onzekerheden.some((o) => /groter dan het voorschot/i.test(o))).toBe(true);
  });

  it("is deterministisch: tweemaal dezelfde invoer levert exact hetzelfde décompte", () => {
    const a = stelVerkoopDecompteSamen(maakVerkoop({ derdengeldenOntvangsten: [ONTVANGST_VOORSCHOT] }), "Vlaanderen");
    const b = stelVerkoopDecompteSamen(maakVerkoop({ derdengeldenOntvangsten: [ONTVANGST_VOORSCHOT] }), "Vlaanderen");
    expect(b).toEqual(a);
  });
});
