import { describe, it, expect } from "vitest"
import { HIGHER_IS_BETTER_FIELDS } from "@/lib/qaf/comparison"
import {
  diffDirection,
  diffStatusClass,
  diffStatusLabel,
  severityClass,
  plausibilityKindClass,
  STRUCTURAL_STATUS,
  DIFF_STATUS_LABEL,
} from "@/components/qaf-differences/qaf-diff-status"

const delta = (
  abs: number | null,
  pp: number | null = null,
  pct: number | null = null
) => ({
  delta_absolute: abs,
  delta_percentage_points: pp,
  delta_percent: pct,
})

describe("diffDirection — Vorrang absolut → Prozentpunkte → Prozent", () => {
  it("liest die Richtung aus dem absoluten Delta", () => {
    expect(diffDirection(delta(5))).toBe("up")
    expect(diffDirection(delta(-5))).toBe("down")
    expect(diffDirection(delta(0))).toBe("flat")
  })

  it("nimmt Prozentpunkte, wenn kein absolutes Delta da ist", () => {
    expect(diffDirection(delta(null, 2))).toBe("up")
    expect(diffDirection(delta(null, -2))).toBe("down")
  })

  it("nimmt Prozent erst als Letztes", () => {
    expect(diffDirection(delta(null, null, 3))).toBe("up")
    expect(diffDirection(delta(null, null, -3))).toBe("down")
  })

  // Der Vorrang ist nicht kosmetisch: stünden die Felder gleichrangig, würde
  // ein widersprüchliches Paar zufällig entschieden.
  it("lässt das absolute Delta gegen ein gegenläufiges Prozent gewinnen", () => {
    expect(diffDirection(delta(-10, null, 99))).toBe("down")
    expect(diffDirection(delta(10, -99, -99))).toBe("up")
  })

  it("ist bei komplett leeren Deltas flach, nicht undefined", () => {
    expect(diffDirection(delta(null))).toBe("flat")
  })
})

describe("diffStatusClass — färbt nach Günstigkeit, nicht nach Richtung", () => {
  // Der teuerste denkbare Fehler in dieser Datei: eine Kostensenkung rot.
  it("färbt eine Kostensenkung grün und einen Kostenanstieg rot", () => {
    expect(diffStatusClass("auffaellig_25", "down", "kosten")).toContain(
      "status-green"
    )
    expect(diffStatusClass("auffaellig_25", "up", "kosten")).toContain(
      "status-red"
    )
  })

  it("dreht die Bewertung für HIGHER_IS_BETTER-Felder um", () => {
    const feld = [...HIGHER_IS_BETTER_FIELDS][0]
    expect(feld).toBeTruthy()
    // Bei "mehr ist besser" ist ein Anstieg günstig — grün.
    expect(diffStatusClass("auffaellig_25", "up", feld)).toContain(
      "status-green"
    )
    expect(diffStatusClass("auffaellig_25", "down", feld)).toContain(
      "status-red"
    )
  })

  it("behandelt ein unbekanntes Feld wie 'weniger ist besser'", () => {
    expect(diffStatusClass("kritisch_50", "up", "voellig_unbekannt")).toContain(
      "status-red"
    )
    expect(diffStatusClass("kritisch_50", "up", undefined)).toContain(
      "status-red"
    )
  })

  it("färbt strukturelle Status unabhängig von der Richtung", () => {
    expect(diffStatusClass("neu", "down")).toContain("primary-tint")
    expect(diffStatusClass("entfallen", "up")).toContain("status-grey")
    expect(diffStatusClass("blockiert", "down")).toContain("status-red")
  })

  // KAR-900/P2.1: die beiden Formel-Status sind unterschiedlich schwer und
  // dürfen nicht gleich aussehen.
  it("unterscheidet 'Formel durch Konstante ersetzt' von 'Formel geändert'", () => {
    const konstante = diffStatusClass("formel_zu_konstante", "flat")
    const geaendert = diffStatusClass("formel_geaendert", "flat")
    expect(konstante).toContain("status-red")
    expect(geaendert).toContain("status-yellow")
    expect(konstante).not.toBe(geaendert)
  })

  it("ist bei flacher Richtung neutral", () => {
    expect(diffStatusClass("anstieg", "flat")).toContain("muted")
  })

  it("zeigt nicht_berechenbar und nicht_anwendbar neutral, nie als Bewertung", () => {
    expect(diffStatusClass("nicht_berechenbar", "up")).toContain("muted")
    expect(diffStatusClass("nicht_anwendbar", "down")).toContain("muted")
  })
})

describe("diffStatusLabel — Pfeil nur wo Richtung Sinn ergibt", () => {
  it("hängt Pfeile an richtungsbehaftete Status", () => {
    expect(diffStatusLabel("anstieg", "up")).toBe("Anstieg ▲")
    expect(diffStatusLabel("senkung", "down")).toBe("Senkung ▼")
    expect(diffStatusLabel("anstieg", "flat")).toBe("Anstieg")
  })

  it("hängt an strukturelle Status KEINEN Pfeil — 'Neu' steigt nicht", () => {
    for (const status of STRUCTURAL_STATUS) {
      const label = diffStatusLabel(status, "up")
      expect(label).not.toContain("▲")
      expect(label).toBe(DIFF_STATUS_LABEL[status] ?? status)
    }
  })

  it("gibt bei fehlendem Status einen leeren String statt 'null'", () => {
    expect(diffStatusLabel(null, "up")).toBe("")
  })

  it("reicht einen unbekannten Status roh durch, statt ihn zu verschlucken", () => {
    expect(diffStatusLabel("voellig_neuer_status", "flat")).toBe(
      "voellig_neuer_status"
    )
  })
})

// Die EINORDNUNGS-Regel (Blockiert/Nicht prüfbar/Schwere aus issue_type +
// severity, Master-Prompt §13) lebt seit Loop 10 Modul 3 in plausibilityBadge
// (appendix-run.ts) und ist dort getestet (appendix-run.test.ts). Hier bleibt
// die Klassen-Seite derselben Regel: jede Badge-Art muss sichtbar
// unterscheidbar eingefärbt sein.
describe("plausibilityKindClass — Master-Prompt §13, Klassen-Seite", () => {
  it("hebt 'blockiert' als harten Stopp hervor", () => {
    expect(plausibilityKindClass("blockiert")).toContain("font-semibold")
    expect(plausibilityKindClass("blockiert")).toContain("bg-status-red-strong")
  })

  // Der Kern der Regel: eine nicht durchführbare Prüfung darf weder wie eine
  // bestandene noch wie ein gewöhnlicher Hinweis aussehen.
  it("hebt 'nicht_pruefbar' sichtbar von einem gewöhnlichen Hinweis ab", () => {
    expect(plausibilityKindClass("nicht_pruefbar")).not.toBe(
      plausibilityKindClass("hinweis")
    )
    expect(plausibilityKindClass("nicht_pruefbar")).toContain("italic")
  })

  it("färbt die Schweregrade über dieselbe severityClass wie zuvor", () => {
    expect(plausibilityKindClass("kritisch")).toBe(severityClass("kritisch"))
    expect(plausibilityKindClass("pruefen")).toBe(severityClass("pruefen"))
    expect(plausibilityKindClass("hinweis")).toBe(severityClass("hinweis"))
  })

  it("färbt eine unbekannte Badge-Art neutral-grau statt zu raten", () => {
    expect(plausibilityKindClass("unbekannt")).toContain("bg-status-grey")
  })
})

describe("severityClass", () => {
  it("gibt jeder Schwere eine eigene Farbe", () => {
    const k = severityClass("kritisch")
    const p = severityClass("pruefen")
    const h = severityClass("hinweis")
    expect(new Set([k, p, h]).size).toBe(3)
    expect(k).toContain("status-red")
    expect(p).toContain("status-yellow")
  })
})
