// Evidence & Compliance Matrix (ECM) — plattformens regelmotor.
//
// ECM är ett eget subsystem, inte en tabell i databasen: den avgör vilken
// dokumentation som krävs, när dokumentation saknas, vilken bevisnivå som
// uppnåtts, vilka regler som gäller (per ärendetyp) och om ett ärende kan
// avslutas. Systemet kan aldrig skriva en slutsats som ECM inte godkänt.
//
// Sex motorer:
//   1. Evidence Engine      — katalogiserar all bevisning ur loggen
//   2. Rule Engine          — dokumentationskraven (metodik + automatiska regler)
//   3. Compliance Engine    — vilka regler som gäller per ärendetyp
//   4. Validation Engine    — inga påståenden utan underlag ("Evidens saknas")
//   5. Completion Engine    — kvalitetsgrind: får ärendet avslutas/rapporteras?
//   6. Traceability Engine  — varje evidenspost hash:as och versionsmärks
//
// Regelbiblioteket är versionshanterat och skilt från applikationslogiken;
// vyerna anropar bara de rena funktionerna nedan. Nästa steg är server-
// distribuerade regelpaket (garantivillkor per tillverkare, försäkrings-
// krav, reklamationsregler) som laddas dynamiskt utan appändring.

import type { Arende, LoggPost } from "./domain";
import type { Metodik } from "./metodik";

export const ECM_VERSION = "2.0";

// ---- 1. Evidence Engine -----------------------------------------------

// Evidensnivåer: bevisvärdet för ett påstående, härlett ur loggen.
export type EvidensNiva = "E0" | "E1" | "E2" | "E3" | "E4" | "E5" | "E6";

export const EVIDENS_LABEL: Record<EvidensNiva, string> = {
  E0: "E0 · Inget underlag",
  E1: "E1 · Teknikerns observation",
  E2: "E2 · Foto",
  E3: "E3 · Video",
  E4: "E4 · Mätvärde",
  E5: "E5 · Diagnosdata/dokument",
  E6: "E6 · Flera oberoende källor",
};

export interface Evidenspost {
  id: string;
  tidpunkt: string;
  tekniker: string;
  kategori: string;
  niva: Exclude<EvidensNiva, "E0" | "E6">;
  sammanfattning: string;
  // Innehållshash för spårbarhet: samma post ger alltid samma hash,
  // varje ändringsförsök syns (loggen är dessutom append-only i DB:n).
  hash: string;
}

// FNV-1a — snabb, deterministisk innehållshash (spårbarhet, inte krypto;
// manipulationsskyddet ligger i databasens append-only-triggers).
export function innehallsHash(innehall: string): string {
  let h = 0x811c9dc5;
  for (let i = 0; i < innehall.length; i++) {
    h ^= innehall.charCodeAt(i);
    h = Math.imul(h, 0x01000193);
  }
  return (h >>> 0).toString(16).padStart(8, "0");
}

// Katalogiserar varje evidenspost i loggen med nivå, kategori och hash.
/**
 * Var instrumentet kalibrerat när mätningen gjordes?
 *
 * Frågan gäller mättillfället, inte i dag. Ett instrument vars
 * kalibrering gått ut i efterhand gör inte en gammal mätning ogiltig —
 * och ett nyligen kalibrerat instrument räddar inte en mätning som
 * gjordes när kalibreringen var utgången.
 */
export function kalibreradVid(kalibreradTill: string | undefined, matt: string): boolean {
  if (!kalibreradTill) return false;
  const giltigt = Date.parse(kalibreradTill);
  const tidpunkt = Date.parse(matt);
  return Number.isFinite(giltigt) && Number.isFinite(tidpunkt) && tidpunkt <= giltigt;
}

export function evidensposter(arende: Arende): Evidenspost[] {
  const poster: Evidenspost[] = [];
  const lagg = (post: LoggPost, kategori: string, niva: Evidenspost["niva"], sammanfattning: string) =>
    poster.push({
      id: post.id,
      tidpunkt: post.tidpunkt,
      tekniker: post.anvandare,
      kategori,
      niva,
      sammanfattning,
      hash: innehallsHash(JSON.stringify(post.handelse)),
    });
  for (const post of arende.handelser) {
    const h = post.handelse;
    if (h.typ === "foto") lagg(post, "foto", "E2", h.beskrivning);
    if (h.typ === "video") lagg(post, "video", "E3", h.beskrivning);
    if (h.typ === "matvarde") {
      // Ett mätvärde är E4 bara när det går att visa vad som mätte och
      // att instrumentet var kalibrerat vid mättillfället. Utan det är
      // det teknikerns observation av en siffra — E1 (QUALITY-AUDIT M-1).
      //
      // Nedgraderingen är tyst i loggen men syns i rapporten: att låtsas
      // att en okalibrerad mätning väger som en kalibrerad är precis den
      // sortens obefogade säkerhet produkten finns för att undvika.
      const spårbar = Boolean(h.matdonId) && kalibreradVid(h.matdonKalibreradTill, post.tidpunkt);
      const märkning = [
        h.matdonBeteckning
          ? `${h.matdonBeteckning}${spårbar ? "" : " — kalibrering saknas eller utgången"}`
          : "instrument ej angivet",
        // Härkomsten står i rapporten, inte bara i loggen: den som läser
        // ska se att siffran är maskinläst utan att gräva (TÜV T-5).
        h.kalla,
      ]
        .filter(Boolean)
        .join("; ");
      lagg(
        post,
        "mätvärde",
        spårbar ? "E4" : "E1",
        `${h.beskrivning} = ${h.varde}${h.enhet ? ` ${h.enhet}` : ""} (${märkning})`,
      );
    }
    if (h.typ === "matarstallning" && !h.undantag) {
      // Ett foto på mätaren är E2 — något som visar vad som stod där.
      // En inskriven siffra utan foto är teknikerns påstående om vad som
      // stod där, alltså E1. Skillnaden syns i rapporten, eftersom
      // mätarställningen är det som avgör garanti- och försäkringsfrågor
      // i efterhand och den enda uppgift någon kan ha ett intresse av att
      // skriva fel.
      const fotograferad = Boolean(h.bilagaId || h.dataUrl);
      lagg(
        post,
        "mätarställning",
        fotograferad ? "E2" : "E1",
        `${h.lage === "ingaende" ? "In" : "Ut"}: ${h.varde}${fotograferad ? "" : " (inskriven, ej fotograferad)"}`,
      );
    }
    if (h.typ === "arbetsorder_skannad") lagg(post, "dokument", "E5", `Arbetsorder, ${h.falt.length} fält`);
    if (h.typ === "observation") lagg(post, "observation", "E1", h.text);
    if (h.typ === "kontroll_utford" && !h.undantag) lagg(post, "kontroll", "E1", h.text);
  }
  return poster;
}

// Ärendets samlade evidensnivå: starkaste kombination loggen innehåller.
export function evidensNiva(arende: Arende): EvidensNiva {
  const poster = evidensposter(arende);
  const har = (n: string) => poster.some((p) => p.niva === n);
  const kallor = [har("E2"), har("E3"), har("E4"), har("E5")].filter(Boolean).length;
  if (kallor >= 2) return "E6";
  if (har("E5")) return "E5";
  if (har("E4")) return "E4";
  if (har("E3")) return "E3";
  if (har("E2")) return "E2";
  if (har("E1")) return "E1";
  return "E0";
}

// ---- 2. Rule Engine ---------------------------------------------------

// Automatiska regler (kodade i orkesterns grundprompt och i metodikens
// krav-fält): kan det fotograferas → begär foto; låter det → video med
// ljud; rör det sig → video; mäts det → mätvärde; visar en display
// informationen → fota displayen; finns ett dokument → fota dokumentet.

// Godkända orsaker när underlag inte kan tas fram (fri text tillåts
// också — men en orsak är alltid obligatorisk).
export const UNDANTAGSORSAKER = [
  "Komponenten är oåtkomlig",
  "Fordonet kan inte lyftas säkert",
  "Kunden avböjde demontering",
  "Dålig sikt/åtkomst",
  "Utrustning saknas",
];

// Markörtexter för pre-diagnostikens kvitteringar (loggas som kommentar).
export const MARKOR_FELBESKRIVNING_VERIFIERAD = "Kundens felbeskrivning verifierad vid mottagandet";
export const MARKOR_INGA_TIDIGA_OBSERVATIONER = "Inga ytterligare observationer vid mottagandet";
export const MARKOR_TIDIGA_OBSERVATIONER_KLARA = "Tidiga observationer vid mottagandet dokumenterade";

// -- Felorsaksanalys (Root Cause Analysis) --
// Ett ärende avslutas aldrig med enbart "komponent trasig, byt komponent":
// varje konstaterat fel kräver avvikelse, bedömd grundorsak, underlag och
// säkerhetsnivå — eller en motivering till varför orsaken inte fastställts.

export const ORSAKSKATEGORIER = [
  "Normalt slitage",
  "Ålder",
  "Körsträcka",
  "Materialutmattning",
  "Tillverkningsfel",
  "Bristande underhåll",
  "Felaktig tidigare reparation",
  "Yttre påverkan",
  "Korrosion",
  "Överhettning",
  "Förorening",
  "Felaktig användning",
  "Modifiering",
  "Olycka eller skada",
  "Okänd orsak",
];

export const UNDERLAGSKALLOR = [
  "Foto",
  "Video",
  "Mätresultat",
  "Diagnosutläsning",
  "Tidigare historik",
  "Servicehistorik",
  "Teknisk dokumentation",
  "Direkt observation",
];

// Kvalitetsregeln: generella formuleringar utan förklaring avvisas.
const GENERISKA_FRASER = /^(trasig|defekt|sliten|utsliten|kass|död|behöver bytas|byt(es)?( ut)?|fungerar inte|går sönder|sönder)[.!]?$/i;

export function granskaAvvikelse(text: string): string | null {
  const ren = text.trim();
  if (ren.length < 20 || GENERISKA_FRASER.test(ren)) {
    return "Felorsak saknas. Beskriv den konstaterade avvikelsen — inte bara komponenten. Exempel: ”Startmotorn aktiverar inte trots korrekt matningsspänning och god jordförbindelse.”";
  }
  return null;
}

// Underlaget måste finnas: en vald evidenskälla godtas bara om loggen
// faktiskt innehåller den sortens evidens.
export function underlagFinns(arende: Arende, kalla: string): boolean {
  const h = arende.handelser.map((p) => p.handelse);
  switch (kalla) {
    case "Foto":
      return h.some((x) => x.typ === "foto");
    case "Video":
      return h.some((x) => x.typ === "video");
    case "Mätresultat":
      return h.some((x) => x.typ === "matvarde");
    case "Diagnosutläsning":
      return h.some((x) => x.typ === "foto" && /instrument|diagnos/i.test(x.beskrivning));
    case "Tidigare historik":
    case "Servicehistorik":
      return h.some((x) => x.typ === "historik_kontrollerad" && x.kontrollerad);
    case "Direkt observation":
      return h.some((x) => x.typ === "observation" || x.typ === "kontroll_utford");
    default:
      // Teknisk dokumentation: kan ännu inte verifieras maskinellt.
      return true;
  }
}

export function felorsaker(arende: Arende) {
  return arende.handelser.filter((p) => p.handelse.typ === "felorsak");
}

// -- Åtgärdsfasen (Repair & Verification) --
// Loopen som symptomverifieringen öppnade sluts här: åtgärden
// dokumenteras (eller motiveras varför den uteblev) och kvalitets-
// kontrollen visar om symptomet faktiskt är borta.

export const INGEN_ATGARD_ORSAKER = [
  "Kunden avböjde åtgärd",
  "Väntar på reservdel",
  "Åtgärd utförs av annan verkstad",
  "Endast utredning beställd",
  "Kostnadsförslag lämnat, inväntar besked",
];

// Kanaler för kundens besked — beskedet ska alltid gå att härleda till
// ett faktiskt samtal, besök eller meddelande.
export const KUNDKANALER = ["Telefon", "På plats", "E-post", "SMS", "Delningslänk"];

export function atgardsforslag(arende: Arende) {
  return arende.handelser.filter((p) => p.handelse.typ === "atgardsforslag");
}

export function kundbeslut(arende: Arende) {
  let senaste: { beslut: "godkant" | "avbojt" | "delvis"; kanal: string; kommentar?: string } | undefined;
  for (const post of arende.handelser) {
    if (post.handelse.typ === "kundbeslut") senaste = post.handelse;
  }
  return senaste;
}

export function atgarder(arende: Arende) {
  return arende.handelser.filter((p) => p.handelse.typ === "atgard_utford");
}

export function kvalitetskontroll(arende: Arende) {
  let senaste: { resultat: "symptomet_borta" | "kvarstar" | "delvis" | "ej_verifierbar"; beskrivning: string } | undefined;
  for (const post of arende.handelser) {
    if (post.handelse.typ === "kvalitetskontroll") senaste = post.handelse;
  }
  return senaste;
}

// -- Symptom Verification Protocol (SVP) --
// Kundens beskrivning → förtydligande (metodikens symptomfrågor) →
// reproducering (eller dokumenterat ej reproducerbar). Rapporten skiljer
// alltid kundens upplevelse från teknikerns verifierade observationer.

export function reproducering(arende: Arende) {
  let senaste: { status: "ja" | "delvis" | "nej"; beskrivning: string } | undefined;
  for (const post of arende.handelser) {
    if (post.handelse.typ === "reproducering") senaste = post.handelse;
  }
  return senaste;
}

// Rapportformuleringen styrs av verifieringsläget — aldrig "felet
// konstaterat" utan reproducering eller annan dokumenterad verifiering.
export function reproduceringsText(status: "ja" | "delvis" | "nej"): string {
  if (status === "ja") return "Symptomet reproducerades vid undersökningen.";
  if (status === "delvis") return "Symptomet kunde delvis reproduceras vid undersökningen.";
  return "Kundens beskrivning kunde inte reproduceras under de förhållanden som rådde vid undersökningen.";
}

// ---- 3. Compliance Engine ---------------------------------------------

// Ärendetypen styr vilka dokumentationskrav som gäller utöver metodiken.
export const ARENDETYPER = [
  "Privat kund",
  "Företagskund",
  "Garanti",
  "Goodwill",
  "Försäkring",
  "Reklamation",
  "Begagnatgaranti",
  "Intern kvalitetskontroll",
  "Teknisk utredning",
] as const;

export type Arendetyp = (typeof ARENDETYPER)[number];

export function arendetyp(arende: Arende): Arendetyp {
  let typ: Arendetyp = "Privat kund";
  for (const post of arende.handelser) {
    const h = post.handelse;
    if (h.typ === "arendetyp_satt" && (ARENDETYPER as readonly string[]).includes(h.arendetyp)) {
      typ = h.arendetyp as Arendetyp;
    }
  }
  return typ;
}

// ---- ECM Knowledge Library --------------------------------------------
// Regelpaketet är data, inte kod: reglerna är deklarativa (krav-typ i
// stället för funktioner) och kan därför distribueras från servern
// (GET /api/ecm/regler, filen services/plattform/ecm-regler.json — i
// klustret utbytbar via ConfigMap) och uppdateras utan appändring.
// Klienten cachar senast hämtade paket och faller tillbaka till det
// inbyggda standardpaketet offline/i lokalt läge.

export type ComplianceKravTyp = "miltal" | "historik" | "claim" | "skadenummer" | "foto";

export interface ComplianceRegel {
  id: string;
  rubrik: string;
  krav: ComplianceKravTyp;
  detaljVidBrist: string;
}

export interface RegelPaket {
  version: string;
  arendetypRegler: Partial<Record<string, ComplianceRegel[]>>;
  undantagsorsaker: string[];
  orsakskategorier: string[];
  underlagskallor: string[];
}

function objektFalt(arende: Arende, falt: "miltal" | "claim" | "skadenummer" | "arbetsorder"): string | undefined {
  for (const post of arende.handelser) {
    if (post.handelse.typ === "objekt_identifierat") return post.handelse.objekt[falt];
  }
  return undefined;
}

// Krav-typ → verifiering mot händelseloggen. Nya krav-typer läggs till
// här; vilka regler som använder dem styrs helt av regelpaketet.
const KRAV_KONTROLLER: Record<ComplianceKravTyp, (arende: Arende) => boolean> = {
  miltal: (a) =>
    a.handelser.some((p) => p.handelse.typ === "matarstallning" && !p.handelse.undantag) ||
    !!objektFalt(a, "miltal"),
  historik: (a) => a.handelser.some((p) => p.handelse.typ === "historik_kontrollerad" && p.handelse.kontrollerad),
  claim: (a) => !!objektFalt(a, "claim"),
  skadenummer: (a) => !!objektFalt(a, "skadenummer"),
  foto: (a) => a.handelser.some((p) => p.handelse.typ === "foto"),
};

// Inbyggt standardpaket = fallback när servern inte nåtts ännu.
export const STANDARD_REGELPAKET: RegelPaket = {
  version: ECM_VERSION,
  arendetypRegler: {
    Garanti: [
      { id: "garanti_miltal", rubrik: "Miltal dokumenterat", krav: "miltal", detaljVidBrist: "Garantiärenden kräver dokumenterad mätarställning." },
      { id: "garanti_historik", rubrik: "Servicehistorik kontrollerad", krav: "historik", detaljVidBrist: "Garantiärenden kräver kontrollerad servicehistorik." },
      { id: "garanti_claim", rubrik: "Claim-/garantinummer registrerat", krav: "claim", detaljVidBrist: "Ange claim-/garantinummer (läses ur arbetsordern)." },
    ],
    Goodwill: [
      { id: "goodwill_miltal", rubrik: "Miltal dokumenterat", krav: "miltal", detaljVidBrist: "Goodwillärenden kräver dokumenterad mätarställning." },
      { id: "goodwill_historik", rubrik: "Servicehistorik kontrollerad", krav: "historik", detaljVidBrist: "Goodwillärenden kräver kontrollerad servicehistorik." },
    ],
    Försäkring: [
      { id: "forsakring_skadenummer", rubrik: "Skadenummer registrerat", krav: "skadenummer", detaljVidBrist: "Försäkringsärenden kräver skadenummer (läses ur arbetsordern)." },
      { id: "forsakring_bildbevis", rubrik: "Bildbevis finns", krav: "foto", detaljVidBrist: "Försäkringsärenden kräver bilddokumentation." },
    ],
    Reklamation: [
      { id: "reklamation_historik", rubrik: "Historik och tidigare försök kontrollerade", krav: "historik", detaljVidBrist: "Reklamationer kräver kontrollerad historik (tidigare reparationer/försök)." },
    ],
    Begagnatgaranti: [
      { id: "begagnat_miltal", rubrik: "Miltal dokumenterat", krav: "miltal", detaljVidBrist: "Begagnatgaranti kräver dokumenterad mätarställning." },
    ],
  },
  undantagsorsaker: UNDANTAGSORSAKER,
  orsakskategorier: ORSAKSKATEGORIER,
  underlagskallor: UNDERLAGSKALLOR,
};

const REGELPAKET_NYCKEL = "gf-ecm-regelpaket";

// Validerar och normaliserar ett paket från servern/cachen — trasiga
// eller ofullständiga paket faller tillbaka till standard per fält.
export function normaliseraRegelpaket(data: unknown): RegelPaket {
  const d = (data ?? {}) as Partial<RegelPaket>;
  const lista = (varden: unknown, standard: string[]) =>
    Array.isArray(varden) && varden.length > 0 && varden.every((v) => typeof v === "string")
      ? (varden as string[])
      : standard;
  const regler: RegelPaket["arendetypRegler"] = {};
  if (d.arendetypRegler && typeof d.arendetypRegler === "object") {
    for (const [typ, rader] of Object.entries(d.arendetypRegler)) {
      if (!Array.isArray(rader)) continue;
      const giltiga = rader.filter(
        (r): r is ComplianceRegel =>
          !!r && typeof r.id === "string" && typeof r.rubrik === "string" &&
          typeof r.detaljVidBrist === "string" && typeof r.krav === "string" && r.krav in KRAV_KONTROLLER,
      );
      if (giltiga.length > 0) regler[typ] = giltiga;
    }
  }
  return {
    version: typeof d.version === "string" && d.version ? d.version : STANDARD_REGELPAKET.version,
    arendetypRegler: Object.keys(regler).length > 0 ? regler : STANDARD_REGELPAKET.arendetypRegler,
    undantagsorsaker: lista(d.undantagsorsaker, STANDARD_REGELPAKET.undantagsorsaker),
    orsakskategorier: lista(d.orsakskategorier, STANDARD_REGELPAKET.orsakskategorier),
    underlagskallor: lista(d.underlagskallor, STANDARD_REGELPAKET.underlagskallor),
  };
}

export function aktivtRegelpaket(): RegelPaket {
  try {
    const rad = localStorage.getItem(REGELPAKET_NYCKEL);
    return rad ? normaliseraRegelpaket(JSON.parse(rad)) : STANDARD_REGELPAKET;
  } catch {
    return STANDARD_REGELPAKET;
  }
}

// Hämtar aktuellt regelpaket från plattformen (inloggat läge) och cachar
// det. Anropas vid sidladdning — misslyckas hämtningen gäller cachen
// eller standardpaketet.
export async function hamtaRegelpaket(): Promise<RegelPaket> {
  try {
    const { plattformAktiv, plattformToken, plattformFetch } = await import("./plattform");
    if (plattformAktiv() && plattformToken()) {
      const res = await plattformFetch("/api/ecm/regler");
      if (res.ok) {
        const paket = normaliseraRegelpaket(await res.json());
        localStorage.setItem(REGELPAKET_NYCKEL, JSON.stringify(paket));
        return paket;
      }
    }
  } catch {
    // Nätverksfel: cache/standard gäller.
  }
  return aktivtRegelpaket();
}

// ---- 4. Validation Engine ---------------------------------------------

// Kärnprincipen — inga påståenden utan underlag — verkar i tre lager:
//  (a) orkesterns grundprompt: aldrig "OK/kontrollerad/inga fel" utan
//      evidens; skriv "Evidens saknas" och begär rätt underlag,
//  (b) projektionerna: hypoteser kan aldrig bli konstaterade fel,
//  (c) kvalitetsgrinden nedan: rapport/avslut blockeras tills varje
//      obligatoriskt påstående har evidens eller dokumenterat undantag.

// ---- Pre-Diagnostic Validation ----------------------------------------

export interface PreDiagRad {
  id: "historik" | "matarstallning_in" | "felbeskrivning" | "tidiga_observationer";
  rubrik: string;
  klar: boolean;
  varning?: string;
}

// Ingen felsökning påbörjas förrän grundkontrollerna är genomförda —
// eller dokumenterat motiverade. Allt härleds ur loggen.
export function preDiagnostik(arende: Arende): PreDiagRad[] {
  let historik: PreDiagRad = { id: "historik", rubrik: "Fordonshistorik kontrollerad", klar: false };
  let matarstallning = false;
  let felbeskrivningVerifierad = false;
  let tidiga = false;
  for (const post of arende.handelser) {
    const h = post.handelse;
    if (h.typ === "historik_kontrollerad") {
      historik = {
        id: "historik",
        rubrik: "Fordonshistorik kontrollerad",
        klar: true,
        varning: h.kontrollerad ? undefined : `Ej kontrollerad — orsak: ${h.kommentar ?? "saknas"}`,
      };
    }
    if (h.typ === "matarstallning" && h.lage === "ingaende") matarstallning = true;
    if (h.typ === "kommentar" && h.text.startsWith(MARKOR_FELBESKRIVNING_VERIFIERAD)) felbeskrivningVerifierad = true;
    if (
      h.typ === "kommentar" &&
      (h.text.startsWith(MARKOR_INGA_TIDIGA_OBSERVATIONER) || h.text.startsWith(MARKOR_TIDIGA_OBSERVATIONER_KLARA))
    ) {
      tidiga = true;
    }
  }
  return [
    historik,
    { id: "matarstallning_in", rubrik: "Ingående mätarställning dokumenterad", klar: matarstallning },
    { id: "felbeskrivning", rubrik: "Kundens felbeskrivning verifierad", klar: felbeskrivningVerifierad },
    { id: "tidiga_observationer", rubrik: "Tidiga observationer hanterade", klar: tidiga },
  ];
}

export function preDiagnostikKlar(arende: Arende): boolean {
  return preDiagnostik(arende).every((rad) => rad.klar);
}

// ---- 5. Completion Engine ---------------------------------------------

export interface GrindRad {
  id: string;
  rubrik: string;
  ok: boolean;
  // Obligatorisk: blockerar slutrapporten tills den är grön.
  kravs: boolean;
  detalj?: string;
}

// Kvalitetsgrind före slutrapport/avslut. Varje rad är en verifierbar
// regel mot händelseloggen; regel-id och ECM-version följer med i
// exporten (Traceability Engine).
export function kvalitetsgrind(arende: Arende, metodik: Metodik): GrindRad[] {
  const rader: GrindRad[] = [];
  const handelser = arende.handelser.map((p) => p.handelse);
  const avslutat = handelser.some((h) => h.typ === "arende_avslutat");

  const objektFinns = handelser.some((h) => h.typ === "objekt_identifierat");
  rader.push({
    id: "objekt",
    rubrik: "Fordons-/objektidentifiering verifierad",
    ok: objektFinns,
    kravs: true,
    detalj: objektFinns ? undefined : "Evidens saknas — identifiera objektet.",
  });

  const arbetsorder = handelser.some((h) => h.typ === "arbetsorder_skannad") || !!objektFalt(arende, "arbetsorder");
  rader.push({
    id: "arbetsorder",
    rubrik: "Arbetsorder inläst",
    ok: arbetsorder,
    kravs: false,
    detalj: arbetsorder ? undefined : "Ärendet startades utan skannad arbetsorder.",
  });

  // Pre-diagnostiken ingår i grinden: historik + ingående mätarställning
  // är obligatoriska (dokumenterade eller motiverade).
  const pre = preDiagnostik(arende);
  const historik = pre.find((r) => r.id === "historik")!;
  rader.push({
    id: "historik",
    rubrik: "Fordonshistorik kontrollerad eller motiverad",
    ok: historik.klar,
    kravs: true,
    detalj: historik.klar ? historik.varning : "Kontrollera historiken eller dokumentera varför det inte gått.",
  });
  const matIn = pre.find((r) => r.id === "matarstallning_in")!;
  rader.push({
    id: "matarstallning_in",
    rubrik: "Ingående mätarställning dokumenterad",
    ok: matIn.klar,
    kravs: true,
    detalj: matIn.klar ? undefined : "Fotografera instrumentpanelen (eller dokumentera undantag).",
  });
  const felb = pre.find((r) => r.id === "felbeskrivning")!;
  rader.push({
    id: "felbeskrivning_verifierad",
    rubrik: "Kundens felbeskrivning verifierad",
    ok: felb.klar,
    kravs: false,
    detalj: felb.klar ? undefined : "Bekräfta att kundens beskrivning är korrekt återgiven.",
  });

  // SVP: symptomet reproducerat eller dokumenterat ej reproducerbart —
  // obligatoriskt innan ärendet avslutas.
  const repro = reproducering(arende);
  rader.push({
    id: "svp",
    rubrik: "Symptomverifiering: reproducerat eller dokumenterat ej reproducerbart",
    ok: !!repro,
    kravs: avslutat,
    detalj: repro
      ? reproduceringsText(repro.status)
      : "Dokumentera reproduceringen (Ja/Delvis/Nej med motivering) innan ärendet avslutas.",
  });

  // Felorsaksanalys: minst en dokumenterad felorsak (eller motiverad
  // okänd orsak) krävs för att avsluta — aldrig bara komponent + åtgärd.
  const orsaker = felorsaker(arende);
  rader.push({
    id: "felorsak",
    rubrik: "Felorsaksanalys dokumenterad",
    ok: orsaker.length > 0,
    kravs: avslutat,
    detalj:
      orsaker.length > 0
        ? undefined
        : "Beskriv varför felet uppstått — eller ange varför orsaken inte kunnat fastställas.",
  });

  // Åtgärdsfasen: reparationen dokumenterad — eller motiverat varför
  // ingen åtgärd utfördes. Obligatoriskt innan ärendet avslutas.
  const atgardsposter = atgarder(arende);
  const utfordAtgard = atgardsposter.some((p) => p.handelse.typ === "atgard_utford" && p.handelse.utford);
  rader.push({
    id: "atgard",
    rubrik: "Åtgärd dokumenterad eller motiverad",
    ok: atgardsposter.length > 0,
    kravs: avslutat,
    detalj:
      atgardsposter.length === 0
        ? "Dokumentera vad som gjordes — eller varför ingen åtgärd utfördes."
        : utfordAtgard
          ? undefined
          : "Ingen åtgärd utförd; orsaken är dokumenterad.",
  });

  // Kundgodkännande: har ett åtgärdsförslag lämnats ska kundens besked
  // vara registrerat innan arbetet utförs — arbete utan besked flaggas.
  const forslag = atgardsforslag(arende);
  const beslut = kundbeslut(arende);
  if (forslag.length > 0 || beslut) {
    rader.push({
      id: "kundgodkannande",
      rubrik: "Kundens besked på åtgärdsförslaget registrerat",
      ok: !!beslut,
      kravs: utfordAtgard,
      detalj: beslut
        ? `${beslut.beslut === "godkant" ? "Godkänt" : beslut.beslut === "avbojt" ? "Avböjt" : "Delvis godkänt"} via ${beslut.kanal}.`
        : "Åtgärdsförslag lämnat — registrera kundens besked innan arbetet utförs.",
    });
    if (beslut?.beslut === "avbojt" && utfordAtgard) {
      rader.push({
        id: "godkannande_konflikt",
        rubrik: "Utfört arbete trots avböjt åtgärdsförslag",
        ok: false,
        kravs: true,
        detalj: "Kunden avböjde åtgärden men arbete är dokumenterat som utfört — kontrollera underlaget.",
      });
    }
  }

  // Kvalitetskontroll: är symptomet borta? Krävs när en åtgärd faktiskt
  // utförts — annars finns inget att verifiera.
  const kk = kvalitetskontroll(arende);
  rader.push({
    id: "kvalitetskontroll",
    rubrik: "Kvalitetskontroll genomförd — symptomet verifierat",
    ok: !!kk,
    kravs: avslutat && utfordAtgard,
    detalj: kk
      ? kk.resultat === "symptomet_borta"
        ? "Symptomet är verifierat borta efter åtgärd."
        : kk.resultat === "ej_verifierbar"
          ? `Kunde inte verifieras: ${kk.beskrivning}`
          : `Symptomet kvarstår helt eller delvis — ärendet bör inte avslutas som åtgärdat.`
      : utfordAtgard
        ? "Verifiera att symptomet är borta efter åtgärden (provkörning/återtest)."
        : "Ingen åtgärd utförd — inget att verifiera.",
  });

  // Utgående mätarställning: obligatorisk först när ärendet avslutas.
  const matUt = handelser.some((h) => h.typ === "matarstallning" && h.lage === "utgaende");
  rader.push({
    id: "matarstallning_ut",
    rubrik: "Utgående mätarställning dokumenterad",
    ok: matUt,
    kravs: avslutat,
    detalj: matUt ? undefined : "Fotografera instrumentpanelen när arbetet är klart.",
  });

  // Metodikens kontroller: evidens eller dokumenterat undantag per kontroll.
  const hanterade = new Map<string, { undantag?: string }>();
  for (const h of handelser) {
    if (h.typ === "kontroll_utford") hanterade.set(`${h.stegId}/${h.kontrollId}`, { undantag: h.undantag });
  }
  const fotonFinns = handelser.filter((h) => h.typ === "foto").length;
  const saknade: string[] = [];
  const undantagna: string[] = [];
  let fotoKravUtanFoto = 0;
  for (const steg of metodik.steg) {
    for (const kontroll of steg.kontroller ?? []) {
      const status = hanterade.get(`${steg.id}/${kontroll.id}`);
      if (!status) saknade.push(kontroll.text);
      else if (status.undantag) undantagna.push(`${kontroll.text} (${status.undantag})`);
      else if (kontroll.krav === "foto" && fotonFinns === 0) fotoKravUtanFoto += 1;
    }
  }
  rader.push({
    id: "kontroller",
    rubrik: "Metodikens kontroller: evidens eller dokumenterat undantag",
    ok: saknade.length === 0,
    kravs: true,
    detalj:
      saknade.length > 0
        ? `Evidens saknas: ${saknade.join("; ")}`
        : undantagna.length > 0
          ? `Undantag med orsak: ${undantagna.join("; ")}`
          : undefined,
  });

  rader.push({
    id: "fotokrav",
    rubrik: "Foton finns för fotokrävande kontroller",
    ok: fotoKravUtanFoto === 0,
    kravs: true,
    detalj: fotoKravUtanFoto > 0 ? `${fotoKravUtanFoto} fotokrävande kontroll(er) utan bild i loggen.` : undefined,
  });

  // Compliance Engine: ärendetypens regler ur det aktiva regelpaketet
  // (serverdistribuerat via ECM Knowledge Library, standard som fallback).
  const paket = aktivtRegelpaket();
  const typ = arendetyp(arende);
  for (const regel of paket.arendetypRegler[typ] ?? []) {
    const ok = KRAV_KONTROLLER[regel.krav](arende);
    rader.push({
      id: regel.id,
      rubrik: `${typ}: ${regel.rubrik}`,
      ok,
      kravs: true,
      detalj: ok ? undefined : regel.detaljVidBrist,
    });
  }

  const avslutEvent = handelser.find((h) => h.typ === "arende_avslutat");
  rader.push({
    id: "signering",
    rubrik: "Teknikerns slutsats signerad",
    ok: !avslutat || (avslutEvent?.typ === "arende_avslutat" && !!avslutEvent.signatur),
    kravs: false,
    detalj:
      avslutat && avslutEvent?.typ === "arende_avslutat" && avslutEvent.signatur
        ? `Signerad av ${avslutEvent.signatur}.`
        : avslutat
          ? "Avslutet saknar signatur (äldre ärende)."
          : "Signeras automatiskt när ärendet avslutas.",
  });

  const hypoteser = handelser.filter((h) => h.typ === "hypotes").length;
  rader.push({
    id: "hypoteser",
    rubrik: "Hypoteser redovisas som ej verifierade",
    ok: true,
    kravs: false,
    detalj: hypoteser > 0 ? `${hypoteser} hypotes(er) markeras som ej verifierade i rapporten — aldrig som konstaterade fel.` : undefined,
  });

  rader.push({
    id: "evidensniva",
    rubrik: `Evidensnivå: ${EVIDENS_LABEL[evidensNiva(arende)]}`,
    ok: evidensNiva(arende) !== "E0",
    kravs: true,
    detalj: evidensNiva(arende) === "E0" ? "Ingen evidens i loggen ännu." : undefined,
  });

  return rader;
}

// Sant när alla obligatoriska grindrader är gröna — först då kan
// slutrapporten genereras.
export function grindGodkand(arende: Arende, metodik: Metodik): boolean {
  return kvalitetsgrind(arende, metodik).every((rad) => rad.ok || !rad.kravs);
}

// ---- 6. Traceability Engine -------------------------------------------

// Spårbarhetspaketet som följer med varje export: regelverkets version,
// ärendets evidensnivå och samtliga evidensposter med innehållshash.
// Tillsammans med den append-only-loggen kan varje slutsats härledas:
// vilken bild, vilken mätning, vilken tekniker, vilken regel, när.
export function sparbarhetspaket(arende: Arende, metodik: Metodik) {
  return {
    ecmVersion: ECM_VERSION,
    regelpaketVersion: aktivtRegelpaket().version,
    arendetyp: arendetyp(arende),
    evidensniva: evidensNiva(arende),
    kvalitetsgrind: kvalitetsgrind(arende, metodik).map(({ id, rubrik, ok, kravs }) => ({ id, rubrik, ok, kravs })),
    evidensposter: evidensposter(arende),
  };
}
