// KAR-959/P2 §33-L1 real-file validation — KERNABNAHME, env-gated.
//
// Prüft `computeWorkbookCapabilityMatrix` gegen reale QAF-Mappen aus dem
// lokalen Korpus. Deckt die vier Familien ab, die zur Kernabnahme der
// 8-Modul-Matrix gehören:
//
//   1. LEGACY_DE_SUMMARY + Fertigungskosten — die größte reale Familie
//                              (267/374 Dateien laut capability-matrix.md)
//   2. V9_SUMMARY EN         — die zweitgrößte Familie (79/374)
//   3. ohne-INPUT            — eine Nicht-G60-Mappe (kein 'INPUT'-Reiter),
//                              die weitaus häufigste Form in diesem Korpus
//   4. BT-Datei              — eine "*_BT_*"-benannte Korpusdatei
//
// **G60-Detail und Multi-QAF stehen NICHT hier.** Beide sind
// `deferred_by_product_owner` (Produktentscheidung 2026-08-04) und liegen in
// `capability-detector.deferred-scope.real-files.test.ts` — gleicher Umfang,
// nichts gelöscht, aber getrennt, damit sie kein Abnahmekriterium dieser
// Matrix sind und ihr Fehlen im Korpus die Kernabnahme nicht bewertet.
//
// Ein Test JE Familie statt einem Test für alle sechs: in der Vorgängerfassung
// brachte ein Fehlschlag in einer Familie die gesamte Prüfung zu Fall, und eine
// im Korpus fehlende Familie verschwand in einer Berichtszeile, die niemand
// liest. Jetzt ist ein fehlender Repräsentant ein sichtbares `skip` mit Grund.
// Die Klassifikation läuft trotzdem nur einmal je Datei (`classifyCorpusOnce`).
//
// VERTRAULICHKEIT (KAR-926 F2): hier steht NIEMALS ein realer Dateiname als
// Quelltext-Literal. Jeder Repräsentant wird zur Laufzeit über seine
// Klassifikation gefunden. Begründung und Präzedenzfall stehen im Kopf von
// `real-corpus.ts`.
//
// tdd-guard:skip — real-file regression/observation test, not a unit-level
// behavior spec (the detection algorithm itself is unit-tested in
// capability-detector.test.ts via synthetic fixtures).

import { describe, expect, it } from 'vitest'
import { summarizeCoverage } from '../module-capability-resolver'
import {
  CORPUS_DIR,
  SUITE_TIMEOUT_MS,
  berichtszeile,
  classifyCorpusOnce,
  computeMatrixFor,
  corpusVorhanden,
  laeuftAufFixtures,
  quellUmfang,
  btFamilieImBestand,
  assertStructurallyPlausible,
  type ClassifiedFile,
} from './real-corpus'

// Die Quelle steht im describe-Titel, nicht in einem `console.log`. Der
// Standard-Reporter von vitest — der in CI läuft — gibt Konsolenausgaben nicht
// aus; ein grüner Lauf wäre sonst nicht davon zu unterscheiden, ob gegen 374
// reale Dateien oder gegen vier Fixtures geprüft wurde. Genau diese Lücke sollte
// eine frühere Fassung schließen und tat es nicht: sie baute
// `laeuftAufFixtures()` und `quellUmfang()` und rief beide nirgends auf.
const QUELLE = laeuftAufFixtures()
  ? `FIXTURES (${quellUmfang()} Dateien, eingeschraenkte Aussage)`
  : `KORPUS (${quellUmfang()} Dateien)`

describe.skipIf(!corpusVorhanden())(`WorkbookCapabilityDetector — real-file validation, Kernabnahme (KAR-959/P2, §33-L1) — Quelle: ${QUELLE}`, () => {
  // Die Voraussetzung wird im Test selbst nachgestellt, nicht nur im
  // `describe.skipIf`: der describe-Körper wird auch dann ausgewertet, wenn die
  // Bedingung greift, und ein Zugriff auf das Verzeichnis auf Registrierungsebene
  // liefe dann ins Leere.
  const repraesentant = async (
    waehle: (alle: ClassifiedFile[]) => ClassifiedFile | undefined,
  ): Promise<ClassifiedFile | undefined> => (corpusVorhanden() ? waehle(await classifyCorpusOnce()) : undefined)

  it(
    'findet mindestens eine der vier Kernfamilien im Korpus',
    async () => {
      const alle = await classifyCorpusOnce()
      expect(alle.length, `corpus directory ${CORPUS_DIR} exists but contains no readable .xlsx/.xlsm files`).toBeGreaterThan(0)
      const gefunden = [
        alle.some((c) => c.summaryTemplate === 'QAF_LEGACY_DE_SUMMARY' && c.hasManufacturingSheet),
        alle.some((c) => c.summaryTemplate === 'QAF_V9_SUMMARY' && c.isEnManufacturingLabel),
        alle.some((c) => !c.isG60 && !c.hasInputSheet && c.summaryTemplate !== null),
        alle.some((c) => c.isBtFile),
      ].filter(Boolean).length
      // Ohne diese Schranke wären vier übersprungene Familien ein grüner Lauf,
      // der nichts geprüft hat.
      expect(gefunden, `keine der vier Kernfamilien in ${CORPUS_DIR} gefunden — die Suite hätte nichts geprüft`).toBeGreaterThan(0)
    },
    SUITE_TIMEOUT_MS,
  )

  it(
    'LEGACY_DE_SUMMARY + Fertigungskosten: Summary und Manufacturing sind nutzbar',
    async (ctx) => {
      const datei = await repraesentant((alle) => alle.find((c) => c.summaryTemplate === 'QAF_LEGACY_DE_SUMMARY' && c.hasManufacturingSheet))
      if (!datei) return ctx.skip('keine LEGACY_DE_SUMMARY-Mappe mit Fertigungskosten-Reiter in diesem Korpusausschnitt')

      const ergebnis = await computeMatrixFor(datei.fullPath)
      assertStructurallyPlausible(ergebnis.matrix, ergebnis.sheetNames.length, ergebnis.sheetNames, ergebnis.facetSignals)
      const coverage = summarizeCoverage(ergebnis.matrix)
      console.log(berichtszeile('LEGACY_DE_SUMMARY + Fertigungskosten', datei.fullPath, ergebnis, coverage))

      const summaryFinding = ergebnis.matrix.modules.find((m) => m.module === 'SUMMARY')
      const manufacturingFinding = ergebnis.matrix.modules.find((m) => m.module === 'MANUFACTURING')
      expect(['AVAILABLE', 'PARTIAL']).toContain(summaryFinding?.status)
      expect(['AVAILABLE', 'PARTIAL']).toContain(manufacturingFinding?.status)
      expect(manufacturingFinding?.source?.sheet?.toLowerCase()).toContain('fertigungskosten')
    },
    SUITE_TIMEOUT_MS,
  )

  it(
    'V9_SUMMARY EN: Summary ist nutzbar',
    async (ctx) => {
      const datei = await repraesentant((alle) => alle.find((c) => c.summaryTemplate === 'QAF_V9_SUMMARY' && c.isEnManufacturingLabel))
      if (!datei) return ctx.skip('keine V9_SUMMARY-Mappe mit englischer Manufacturing-Beschriftung in diesem Korpusausschnitt')

      const ergebnis = await computeMatrixFor(datei.fullPath)
      assertStructurallyPlausible(ergebnis.matrix, ergebnis.sheetNames.length, ergebnis.sheetNames, ergebnis.facetSignals)
      const coverage = summarizeCoverage(ergebnis.matrix)
      console.log(berichtszeile('V9_SUMMARY EN', datei.fullPath, ergebnis, coverage))

      const usable = new Set(
        ergebnis.matrix.modules.filter((m) => m.status === 'AVAILABLE' || m.status === 'PARTIAL' || m.status === 'DERIVABLE').map((m) => m.module),
      )
      expect(usable.has('SUMMARY'), 'expected SUMMARY to be usable on a V9 EN file').toBe(true)
      // MANUFACTURING/MATERIAL are the corpus's near-universally-covered
      // groups (capability-matrix.md: 95-100% VORHANDEN for both major
      // families) — logged, not hard-asserted, since a specific
      // dynamically-discovered file could legitimately be an exception;
      // the report line above is what a human reviewer checks.
    },
    SUITE_TIMEOUT_MS,
  )

  it(
    'ohne-INPUT: kein erfundener input-Reiter, Manufacturing folgt dem tatsächlichen Blattbestand',
    async (ctx) => {
      const datei = await repraesentant((alle) => alle.find((c) => !c.isG60 && !c.hasInputSheet && c.summaryTemplate !== null))
      if (!datei) return ctx.skip('keine Nicht-G60-Mappe ohne INPUT-Reiter mit erkannter Summary-Vorlage in diesem Korpusausschnitt')

      const ergebnis = await computeMatrixFor(datei.fullPath)
      assertStructurallyPlausible(ergebnis.matrix, ergebnis.sheetNames.length, ergebnis.sheetNames, ergebnis.facetSignals)
      const coverage = summarizeCoverage(ergebnis.matrix)
      console.log(berichtszeile('ohne-INPUT', datei.fullPath, ergebnis, coverage))

      // KAR-959 review finding #5 fix (PR #326): the original block here only
      // re-asserted the same tautological "status is a member of the
      // CapabilityStatus union" check finding #4 already flagged for
      // assertStructurallyPlausible — never wired to what this specific
      // representative actually IS (a non-G60 workbook with no G60 INPUT
      // rate-card tab, per its own classification above). Both checks below are
      // derived from that same classification (`!c.isG60 && !c.hasInputSheet`,
      // `datei.hasManufacturingSheet`), never a hardcoded expectation about one
      // specific corpus file.
      expect(
        ergebnis.matrix.sheetRoles.some((r) => r.role === 'input'),
        'ohne-INPUT representative was classified as having no G60 INPUT tab — the resolver must not invent an input-role sheet for it',
      ).toBe(false)

      // The G60 INPUT rate-card is a G60-only concept (SheetRole 'input', not
      // one of this matrix's 8 capability modules) — none of the 8 modules is
      // "INPUT-dependent" in this non-G60 matrix, so the only INPUT-shaped
      // claim this file supports is the sheetRoles check above. What IS
      // independently verifiable from the classification's own signal is
      // whether a Fertigungskosten/MANUFACTURING sheet exists: if it does, that
      // facet was genuinely attempted and must not be silently reported as
      // MISSING/DERIVABLE; if it doesn't, MANUFACTURING correctly has nothing
      // to report but MISSING/DERIVABLE.
      const manufacturingFinding = ergebnis.matrix.modules.find((m) => m.module === 'MANUFACTURING')
      if (datei.hasManufacturingSheet) {
        expect(
          ['AVAILABLE', 'PARTIAL', 'PARSE_FAILED'],
          'ohne-INPUT representative has a recognized Fertigungskosten sheet — MANUFACTURING must be attempted, not silently MISSING/DERIVABLE',
        ).toContain(manufacturingFinding?.status)
      } else {
        expect(['MISSING', 'DERIVABLE']).toContain(manufacturingFinding?.status)
      }

      // Die Auswahl verlangt `summaryTemplate !== null` (eine erkannte
      // LEGACY_DE_SUMMARY/V9_SUMMARY-Vorlage wurde gefunden) — SUMMARY muss
      // daher mindestens nutzbar sein, nie MISSING.
      const summaryFinding = ergebnis.matrix.modules.find((m) => m.module === 'SUMMARY')
      expect(['AVAILABLE', 'PARTIAL'], 'ohne-INPUT representative has a classified summary template — SUMMARY must be at least PARTIAL').toContain(
        summaryFinding?.status,
      )

      expect(ergebnis.matrix.sheetRoles).toHaveLength(ergebnis.sheetNames.length)
    },
    SUITE_TIMEOUT_MS,
  )

  it(
    btFamilieImBestand()
      ? 'BT-Datei: wohlgeformte Matrix, kein Absturz'
      : 'BT-Datei: uebersprungen — Familie im Fixture-Bestand nicht vertreten',
    async (ctx) => {
      const datei = await repraesentant((alle) => alle.find((c) => c.isBtFile))
      if (!datei) return ctx.skip('keine "*_BT_*"-benannte Datei in diesem Korpusausschnitt')

      const ergebnis = await computeMatrixFor(datei.fullPath)
      // Keine modulspezifische Erwartung: der Inhalt einer "*_BT_*"-benannten
      // Datei ist sonst nicht eingeschränkt. Die Struktur-Invarianten decken
      // "stürzt nie ab, ist immer wohlgeformt" bereits ab.
      assertStructurallyPlausible(ergebnis.matrix, ergebnis.sheetNames.length, ergebnis.sheetNames, ergebnis.facetSignals)
      console.log(berichtszeile('BT-Datei', datei.fullPath, ergebnis, summarizeCoverage(ergebnis.matrix)))
    },
    SUITE_TIMEOUT_MS,
  )
})
