// Wie viel eines realen QAF-Workbooks kann der Anonymizer überhaupt zuordnen?
//
// Der Anonymizer skaliert nach Whitelist: eine Zahlzelle wird nur dann als
// Geldbetrag behandelt, wenn der Parser sie einem kanonischen Feld zuordnet und
// dessen `unit` sie als Geld ausweist. Jede Zahlzelle OHNE Zuordnung bleibt
// stehen — und stünde damit als echter Wert in der Fixture.
//
// Diese Sonde misst dieses Verhältnis, statt es zu schätzen. Sie ist der
// Grund, warum die Leck-Prüfung des Generators gegen ALLE Originalwerte läuft
// und nicht nur gegen die zugeordneten.
//
// An die realen Dateien gebunden (skipIf) — die Originale dürfen nicht ins
// Repo. Das ist bewusst eine Messung, keine Abnahme: die Abnahme läuft gegen
// die committete Fixture und ohne skipIf.

import { describe, it, expect } from 'vitest'
import { existsSync, readFileSync } from 'node:fs'
import path from 'node:path'
import { loadExcelWorkbook, parseQafFile } from '@/lib/qaf-differences'
import {
  CANONICAL_FIELDS,
  QAF_FIELD_KEY_TO_CANONICAL,
} from '@/lib/qaf-differences/internal/canonical-fields'
import { decideScaling } from '../scale-rule'

const REFERENCE_DIR = path.resolve(__dirname, '../../../../.local/qaf-reference/cases/hice')
const AWARD = path.join(REFERENCE_DIR, '220705_QAF_HICE_Vergabe.xlsx')

const unitById = new Map(CANONICAL_FIELDS.map((f) => [f.id, f.unit]))

describe.skipIf(!existsSync(AWARD))('Abdeckung der Zellzuordnung im realen 8.8-Workbook', () => {
  it('misst zugeordnete gegen nicht zugeordnete Zahlzellen', async () => {
    const buffer = readFileSync(AWARD)
    const wb = await loadExcelWorkbook(buffer)

    // Referenzen im selben Format, das der Parser für sourceCells verwendet
    // ("Blattname!C42"), damit die Schnittmenge überhaupt gebildet werden kann.
    const numericRefs = new Set<string>()
    for (const ws of wb.worksheets) {
      ws.eachRow({ includeEmpty: false }, (row) => {
        row.eachCell({ includeEmpty: false }, (cell) => {
          if (typeof cell.value === 'number') numericRefs.add(`${ws.name}!${cell.address}`)
        })
      })
    }
    const numericCells = numericRefs.size

    const file = new File([buffer], path.basename(AWARD))
    const { steps } = await parseQafFile(file)

    const mapped = new Set<string>()
    const money = new Set<string>()
    for (const step of steps) {
      for (const [key, ref] of Object.entries(step.sourceCells ?? {})) {
        if (typeof ref !== 'string') continue
        mapped.add(ref)
        const canonicalId = QAF_FIELD_KEY_TO_CANONICAL[key as keyof typeof QAF_FIELD_KEY_TO_CANONICAL]
        if (canonicalId && decideScaling(unitById.get(canonicalId)).scale) money.add(ref)
      }
    }

    // Die entscheidende Größe ist die Schnittmenge: eine Zahlzelle OHNE
    // Zuordnung bleibt unskaliert und stünde als echter Wert in der Fixture.
    // `mapped` enthält auch Textfelder und ist deshalb allein nicht aussagekräftig.
    const numericMapped = [...numericRefs].filter((r) => mapped.has(r))
    const numericMoney = [...numericRefs].filter((r) => money.has(r))
    const unmapped = numericCells - numericMapped.length

    // Kein Schwellwert-Assert: Diese Sonde soll berichten, nicht durchfallen.
    // Der Wert steuert den Zuschnitt des Generators.
    console.log(
      `[Abdeckung] Zahlzellen: ${numericCells} · davon zugeordnet: ${numericMapped.length} · davon Geld: ${numericMoney.length} · OHNE Zuordnung: ${unmapped}`,
    )
    console.log(`[Abdeckung] sourceCells-Referenzen gesamt (inkl. Textfelder): ${mapped.size}`)
    console.log(`[Abdeckung] Fertigungsschritte geparst: ${steps.length}`)

    expect(numericCells).toBeGreaterThan(0)
    expect(steps.length).toBeGreaterThan(0)
  })
})
