// Import-Report builder (Wertstrom P6, Baustein 6, §14.4). Pure aggregation
// over already-mapped streams — the SAME builder is used by the preview
// (forecast: "this is what WOULD happen") and by confirm (actual result for
// only the streams that were really selected/persisted), so the two can
// never drift apart the way two independently-written summaries could.
// Simple-first, details-on-demand is a UI concern (the dialog/report
// component renders `simple`/`advanced` separately) — this module just
// produces the full structured data both views read from.

import type { MappedStream } from './streams'

export interface ReportUnsupportedEntity {
  simvsmClass: string
  /** Node and link registries can legitimately share a raw class STRING
   * (e.g. both define `image` — a photo-attachment node vs. a photo-
   * attachment link, two genuinely different things) — `kind` disambiguates
   * them so they never silently merge into one aggregated row (Review-Fix
   * C18, adversarial review PR #360) and gives the UI a collision-free React
   * list key. */
  kind: 'node' | 'link'
  labelDe: string
  count: number
  reasonDe: string
}

export interface ReportTransformedEntity {
  simvsmClass: string
  labelDe: string
  count: number
  noteDe: string
}

export interface SimvsmImportReport {
  /** Nodes + connections successfully mapped (§14.4 "imported entities"). */
  importedEntityCount: number
  /** Unsupported nodes/links, total (§14.4 "skipped entities"). */
  skippedEntityCount: number
  /** Supported-but-generalized node classes (§14.4 "transformed entities"),
   * one row per SimVSM class actually encountered. */
  transformed: ReportTransformedEntity[]
  /** Unsupported node/link classes (§14.4 "unsupported entities"), one row
   * per SimVSM class actually encountered. */
  unsupported: ReportUnsupportedEntity[]
  /** Links dropped because an endpoint didn't map (§14.4 "missing
   * relationships"). */
  missingRelationshipCount: number
  /** §14.4 "assumptions" — plain-language, only the ones that actually
   * apply to this import (never a static boilerplate list). */
  assumptionsDe: string[]
  /** §14.4 "visual layout differences" — always present when any node was
   * imported, since positions are always recomputed (see streams.ts). */
  visualLayoutDifferenceDe: string | null
}

export function buildImportReport(streams: readonly MappedStream[]): SimvsmImportReport {
  let importedEntityCount = 0
  let missingRelationshipCount = 0
  const transformedByClass = new Map<string, ReportTransformedEntity>()
  // Keyed by `${kind}:${simvsmClass}`, NOT the raw class alone — node and
  // link registries can share a class string (see ReportUnsupportedEntity
  // doc comment on `kind`).
  const unsupportedByClass = new Map<string, ReportUnsupportedEntity>()
  let hasProductionControl = false
  let anyNodeImported = false
  let hasUnresolvedDemand = false

  for (const stream of streams) {
    for (const m of stream.nodeMappings) {
      if (m.supported) {
        importedEntityCount++
        anyNodeImported = true
        if (m.simvsmClass === 'productionControl') hasProductionControl = true
        if (m.demandIntervalMissing) hasUnresolvedDemand = true
        if (m.generalizedNote) {
          const existing = transformedByClass.get(m.simvsmClass)
          if (existing) existing.count++
          else transformedByClass.set(m.simvsmClass, { simvsmClass: m.simvsmClass, labelDe: m.labelDe, count: 1, noteDe: m.generalizedNote })
        }
      } else {
        const key = `node:${m.simvsmClass}`
        const existing = unsupportedByClass.get(key)
        if (existing) existing.count++
        else unsupportedByClass.set(key, { simvsmClass: m.simvsmClass, kind: 'node', labelDe: m.labelDe, count: 1, reasonDe: m.reasonDe ?? 'Nicht unterstützt.' })
      }
    }
    for (const l of stream.linkMappings) {
      if (l.supported) {
        importedEntityCount++
        continue
      }
      // Review-Fix C18 (adversarial review PR #360): a link can be dropped
      // for two DIFFERENT reasons that used to be conflated into the same
      // missingRelationshipCount bucket with the same sentence — the link's
      // own CLASS is unsupported (e.g. `image`, dropped regardless of its
      // endpoints) vs. a supported class whose endpoint didn't map (the
      // ACTUAL "missing relationship" case). Class-unsupported links now
      // join the SAME unsupported[] list nodes already use (same shape,
      // same UI section, own accurate reasonDe per class) — so
      // missingRelationshipCount's own doc comment ("dropped because an
      // endpoint didn't map") finally matches what it counts.
      if (l.unsupportedReason === 'class') {
        const key = `link:${l.simvsmClass}`
        const existing = unsupportedByClass.get(key)
        if (existing) existing.count++
        else unsupportedByClass.set(key, { simvsmClass: l.simvsmClass, kind: 'link', labelDe: l.labelDe ?? 'Nicht unterstützte Verbindungsart', count: 1, reasonDe: l.reasonDe ?? 'Nicht unterstützt.' })
      } else {
        missingRelationshipCount++
      }
    }
  }

  const assumptionsDe: string[] = []
  if (anyNodeImported) {
    assumptionsDe.push('Nur die erste Produktzeile je Prozess bzw. Kunde wurde übernommen (Mehrprodukt-Tabellen werden nicht importiert).')
  }
  if (hasProductionControl) {
    // C11 (declared deviation, adversarial review PR #360): the Parent
    // decision was "mark PPS-imported nodes + exclude them from bottleneck
    // confidence, IF that's possible without an Engine-Vertragsbruch". No
    // marker was added: VsmNode has no field for "SimVSM origin class" (zero
    // schema changes, per README "Warum keine Migration"), and the only
    // available carrier — `notes` — is a real user-visible field (leaking an
    // internal bookkeeping string into it would itself be a new §14.1
    // violation, "no technical jargon in the simple view"). Teaching
    // lib/vsm-engine/internal/bottleneck.ts a SimVSM-specific convention
    // would also couple a deliberately source-agnostic engine to one
    // importer's internal shape. Falling back to the brief's own explicit
    // fallback: declare it here instead, and in TODO.md.
    assumptionsDe.push(
      'Produktionssteuerung (PPS) wurde als nicht-wertschöpfender Prozess angenähert — kein eigener PPS-Typ im Datenmodell. Da PPS-Knoten dadurch nie Zykluszeit-Daten haben, zählen sie im Wertstrom-Editor als „ungemessene Station" — die Engpass-Konfidenz kann für Wertströme mit PPS-Knoten strukturell nicht „hoch" werden.',
    )
  }
  if (hasUnresolvedDemand) {
    assumptionsDe.push(
      'Bedarfsrate (Stk/Tag) konnte für mindestens einen Kunden-Knoten nicht berechnet werden — das SimVSM-Intervall fehlt oder ist ungültig, es wurde kein Bedarf angenommen.',
    )
  }

  return {
    importedEntityCount,
    skippedEntityCount: [...unsupportedByClass.values()].reduce((sum, e) => sum + e.count, 0),
    transformed: [...transformedByClass.values()].sort((a, b) => a.labelDe.localeCompare(b.labelDe)),
    unsupported: [...unsupportedByClass.values()].sort((a, b) => a.labelDe.localeCompare(b.labelDe)),
    missingRelationshipCount,
    assumptionsDe,
    visualLayoutDifferenceDe: anyNodeImported
      ? 'Die Positionen wurden automatisch neu angeordnet — die Anordnung aus SimVSM wird nicht übernommen (kein Rundtrip-Anspruch, siehe §14.5).'
      : null,
  }
}
