import { describe, it, expect } from "vitest";
import {
  bepaalSuiGenerisContract,
  afleidenAktefamilie,
  gedegradeerdeLagen,
  ontbrekendeInvarianten,
  SKELETMODEL_PER_FAMILIE,
  type Dekking,
} from "./suiGeneris";

const LEGE_DEKKING: Dekking = {
  gedekteModellen: new Set(),
  gedekteChecklists: new Set(),
  gedekteOpvolging: new Set(),
  gedekteAfrekening: new Set(),
  gedekteRollen: new Set(),
};

const VOLLEDIGE_DEKKING = (akteType: string): Dekking => ({
  gedekteModellen: new Set([akteType]),
  gedekteChecklists: new Set([akteType]),
  gedekteOpvolging: new Set([akteType]),
  gedekteAfrekening: new Set([akteType]),
  gedekteRollen: new Set([akteType]),
});

describe("afleidenAktefamilie", () => {
  it("herkent de families uit trefwoorden", () => {
    expect(afleidenAktefamilie("verkoopakte - lijfrente")).toBe("vastgoed");
    expect(afleidenAktefamilie("statutenwijziging vennootschap")).toBe("vennootschap");
    expect(afleidenAktefamilie("keuzetestament")).toBe("familie");
    expect(afleidenAktefamilie("dading over een onbenoemde overeenkomst")).toBe("algemeen");
  });
});

describe("bepaalSuiGenerisContract — atypische akte", () => {
  const contract = bepaalSuiGenerisContract("gemengde-overeenkomst-verkoop-en-erfpacht", {
    dekking: LEGE_DEKKING,
    betreftOnroerendGoed: true,
    aktefamilie: "vastgoed",
  });

  it("markeert de akte als sui generis en degradeert álle lagen met uitleg", () => {
    expect(contract.isSuiGeneris).toBe(true);
    expect(gedegradeerdeLagen(contract)).toHaveLength(5);
    expect(contract.lagen.every((l) => l.uitleg.length > 0)).toBe(true);
  });

  it("valt terug op het skeletmodel van de aktefamilie", () => {
    expect(contract.skeletModelId).toBe(SKELETMODEL_PER_FAMILIE.vastgoed);
    expect(contract.skeletModelId).toBe("algemeen-kader-vastgoedakte");
  });

  it("houdt de harde invarianten onaantastbaar (beschrijvingsclausule + voorbehoudgrens)", () => {
    expect(contract.hardeInvarianten).toContain("verplichte-beschrijvingsclausule");
    expect(contract.hardeInvarianten).toContain("voorbehoudgrens-ambtelijke-handeling");
    expect(ontbrekendeInvarianten(contract)).toEqual([]);
  });
});

describe("bepaalSuiGenerisContract — gedekt akteType", () => {
  it("degradeert geen enkele laag wanneer alles gedekt is", () => {
    const contract = bepaalSuiGenerisContract("verkoopakte", {
      dekking: VOLLEDIGE_DEKKING("verkoopakte"),
      betreftOnroerendGoed: true,
    });
    expect(contract.isSuiGeneris).toBe(false);
    expect(gedegradeerdeLagen(contract)).toEqual([]);
  });
});

describe("harde invarianten los van de degradatie", () => {
  it("laat de voorbehoudgrens ook gelden zonder onroerend goed", () => {
    const contract = bepaalSuiGenerisContract("onbenoemde-dienstenovereenkomst", {
      dekking: LEGE_DEKKING,
      betreftOnroerendGoed: false,
    });
    expect(contract.hardeInvarianten).toEqual(["voorbehoudgrens-ambtelijke-handeling"]);
    expect(ontbrekendeInvarianten(contract)).toEqual([]);
  });

  it("meldt een ontbrekende beschrijvingsclausule als guard-signaal", () => {
    // Simuleer een defect contract dat de invariant zou laten vallen.
    const defect = {
      akteType: "x",
      isSuiGeneris: true,
      betreftOnroerendGoed: true,
      skeletModelId: null,
      aktefamilie: "vastgoed" as const,
      lagen: [],
      hardeInvarianten: ["voorbehoudgrens-ambtelijke-handeling" as const],
    };
    expect(ontbrekendeInvarianten(defect)).toEqual(["verplichte-beschrijvingsclausule"]);
  });
});
