// Dossierkoppeling — relaties tussen dossiers van het kantoor (verkoop ↔
// kredietdossier, nalatenschap → verkoop uit de nalatenschap, deeldossiers,
// verwante dossiers). Pure functies: koppelen/ontkoppelen gebeurt altijd
// SYMMETRISCH — beide dossiers dragen de koppeling, elk met de inverse
// relatie — zodat de band vanuit beide fiches zichtbaar is en blijft. Dit is
// tevens het aanknopingspunt voor latere lagen die over een dossierketen heen
// werken: dossierregie (laag 2) en de boekhoudmodule (laag 6, AUT-S8) die
// afrekeningen per gekoppelde keten kan groeperen — zie AUTONOMIE-ROADMAP.md.

import {
  DOSSIERTYPE_LABEL,
  isVerkoopType,
  type Dossier,
  type DossierKoppeling,
  type DossierRelatie,
} from "./types";

/** Mensleesbaar label per relatie, gezien vanuit het dossier dat de koppeling draagt ("dit dossier … het andere dossier"). */
export const RELATIE_LABEL: Record<DossierRelatie, string> = {
  "volgt-op": "volgt op",
  "gevolgd-door": "wordt gevolgd door",
  "kredietdossier-van": "is het kredietdossier bij",
  "heeft-kredietdossier": "heeft als kredietdossier",
  "deeldossier-van": "is een deeldossier van",
  "omvat-deeldossier": "omvat als deeldossier",
  verwant: "is verwant met",
};

/** Inverse per relatie: A "volgt-op" B ⇔ B "gevolgd-door" A. "verwant" is symmetrisch (zijn eigen inverse). */
export const RELATIE_INVERS: Record<DossierRelatie, DossierRelatie> = {
  "volgt-op": "gevolgd-door",
  "gevolgd-door": "volgt-op",
  "kredietdossier-van": "heeft-kredietdossier",
  "heeft-kredietdossier": "kredietdossier-van",
  "deeldossier-van": "omvat-deeldossier",
  "omvat-deeldossier": "deeldossier-van",
  verwant: "verwant",
};

/** Alle relaties, voor keuzelijsten. */
export const DOSSIER_RELATIES = Object.keys(RELATIE_LABEL) as DossierRelatie[];

/**
 * Kan `van` aan `naar` gekoppeld worden? Eén koppeling per dossierpaar: een
 * tweede relatie tussen dezelfde dossiers vergt eerst ontkoppelen, zodat de
 * symmetrische boekhouding nooit uiteen kan lopen. Beide zijden worden
 * gecontroleerd (ook een eenzijdig restant telt als "al gekoppeld").
 */
export function kanKoppelen(van: Dossier, naar: Dossier): { ok: true } | { ok: false; reden: string } {
  if (van.id === naar.id) {
    return { ok: false, reden: "Een dossier kan niet aan zichzelf gekoppeld worden." };
  }
  const alGekoppeld =
    (van.koppelingen ?? []).some((k) => k.dossierId === naar.id) ||
    (naar.koppelingen ?? []).some((k) => k.dossierId === van.id);
  if (alGekoppeld) {
    return { ok: false, reden: "Deze dossiers zijn al gekoppeld — ontkoppel eerst om de relatie te wijzigen." };
  }
  return { ok: true };
}

/**
 * Koppelt twee dossiers symmetrisch: `van` krijgt de gegeven relatie naar
 * `naar`, `naar` de inverse relatie naar `van`. Geeft bijgewerkte kopieën
 * terug (de invoer wordt niet gemuteerd) met `aangepast` = tijdstip.
 * Ongeldige koppeling (zelfkoppeling, al gekoppeld) → Error; toets vooraf
 * met kanKoppelen.
 */
export function koppelDossiers(
  van: Dossier,
  naar: Dossier,
  relatie: DossierRelatie,
  tijdstip: string,
  toelichting?: string
): { van: Dossier; naar: Dossier } {
  const controle = kanKoppelen(van, naar);
  if (!controle.ok) throw new Error(controle.reden);
  const heen: DossierKoppeling = { dossierId: naar.id, relatie, toelichting, aangemaakt: tijdstip };
  const terug: DossierKoppeling = { dossierId: van.id, relatie: RELATIE_INVERS[relatie], toelichting, aangemaakt: tijdstip };
  return {
    van: { ...van, koppelingen: [...(van.koppelingen ?? []), heen], aangepast: tijdstip },
    naar: { ...naar, koppelingen: [...(naar.koppelingen ?? []), terug], aangepast: tijdstip },
  };
}

/**
 * Ontkoppelt twee dossiers: verwijdert op beide zijden elke koppeling naar
 * elkaar. Geeft bijgewerkte kopieën terug; bestond er geen koppeling, dan
 * blijven de dossiers (inclusief `aangepast`) ongewijzigd.
 */
export function ontkoppelDossiers(van: Dossier, naar: Dossier, tijdstip: string): { van: Dossier; naar: Dossier } {
  const zonder = (dossier: Dossier, anderId: string): Dossier => {
    const koppelingen = (dossier.koppelingen ?? []).filter((k) => k.dossierId !== anderId);
    if (koppelingen.length === (dossier.koppelingen ?? []).length) return dossier;
    return { ...dossier, koppelingen: koppelingen.length ? koppelingen : undefined, aangepast: tijdstip };
  };
  return { van: zonder(van, naar.id), naar: zonder(naar, van.id) };
}

/**
 * Verwijdert de koppeling naar het gegeven dossier-id op één zijde — enkel
 * voor VERBROKEN koppelingen (het doeldossier bestaat niet meer, bv. na
 * verwijdering); een bestaand paar ontkoppel je met ontkoppelDossiers.
 */
export function verwijderVerbrokenKoppeling(dossier: Dossier, dossierId: string, tijdstip: string): Dossier {
  const koppelingen = (dossier.koppelingen ?? []).filter((k) => k.dossierId !== dossierId);
  if (koppelingen.length === (dossier.koppelingen ?? []).length) return dossier;
  return { ...dossier, koppelingen: koppelingen.length ? koppelingen : undefined, aangepast: tijdstip };
}

/** Kort mensleesbaar label van een dossier, voor keuzelijsten en koppelingweergave. */
export function dossierKortLabel(dossier: Dossier): string {
  const kern = isVerkoopType(dossier.dossiertype)
    ? dossier.goed?.waarde.adres
    : dossier.partijen.waarde.map((p) => p.naam).join(", ");
  const delen = [dossier.referentie, kern || "—", DOSSIERTYPE_LABEL[dossier.dossiertype]];
  return delen.filter(Boolean).join(" · ");
}

/** Eén koppeling, opgelost naar haar doeldossier, klaar voor weergave. */
export interface KoppelingWeergave {
  koppeling: DossierKoppeling;
  /** Relatielabel vanuit het dragende dossier ("volgt op", …). */
  label: string;
  /** Het doeldossier; undefined = verbroken koppeling (doel bestaat niet meer). */
  doel?: Dossier;
  doelLabel: string;
}

/** Lost de koppelingen van een dossier op naar hun doeldossiers; verbroken koppelingen blijven zichtbaar (doel undefined). */
export function koppelingOverzicht(dossier: Dossier, alleDossiers: Dossier[]): KoppelingWeergave[] {
  return (dossier.koppelingen ?? []).map((koppeling) => {
    const doel = alleDossiers.find((d) => d.id === koppeling.dossierId);
    return {
      koppeling,
      label: RELATIE_LABEL[koppeling.relatie],
      doel,
      doelLabel: doel ? dossierKortLabel(doel) : "dossier bestaat niet meer (verwijderd?)",
    };
  });
}
