import { describe, it, expect } from 'vitest'
import { buildImportReport } from '../report'
import { mapStream, resolveCurrentStreamIndex } from '../streams'
import { parseSimVsmValue } from '../parser'
import { buildSyntheticSimVsmFile } from './fixtures'
import type { MappedStream } from '../streams'

function mappedStreams() {
  const result = parseSimVsmValue(buildSyntheticSimVsmFile(), 'x.json')
  if (!result.ok) throw new Error('fixture must parse')
  const currentIndex = resolveCurrentStreamIndex(result.alternatives)
  return result.alternatives.map((a) => mapStream(a, currentIndex))
}

function makeStreamWithLinkMappings(linkMappings: MappedStream['linkMappings']): MappedStream {
  return { index: 0, name: 'x', isMainFlag: false, scenarioRole: 'current', hasResultData: false, nodes: [], connections: [], nodeMappings: [], linkMappings }
}

describe('buildImportReport', () => {
  it('returns all-zero/empty for an empty stream list', () => {
    const report = buildImportReport([])
    expect(report.importedEntityCount).toBe(0)
    expect(report.skippedEntityCount).toBe(0)
    expect(report.transformed).toEqual([])
    expect(report.unsupported).toEqual([])
    expect(report.assumptionsDe).toEqual([])
    expect(report.visualLayoutDifferenceDe).toBeNull()
  })

  it('counts imported nodes+links and skipped (unsupported) nodes separately', () => {
    const report = buildImportReport(mappedStreams())
    expect(report.importedEntityCount).toBeGreaterThan(0)
    expect(report.skippedEntityCount).toBeGreaterThan(0)
  })

  it('lists noteVSM and foreignOrders under "unsupported" with a reason each', () => {
    const report = buildImportReport(mappedStreams())
    const classes = report.unsupported.map((u) => u.simvsmClass)
    expect(classes).toContain('noteVSM')
    expect(classes).toContain('foreignOrders')
    for (const u of report.unsupported) expect(u.reasonDe.length).toBeGreaterThan(0)
  })

  it('lists productionControl and multiProcess under "transformed" (generalized onto process)', () => {
    const report = buildImportReport(mappedStreams())
    const classes = report.transformed.map((t) => t.simvsmClass)
    expect(classes).toContain('productionControl')
    expect(classes).toContain('multiProcess')
  })

  it('counts the noteVSM-bound informationFlow link as a missing relationship', () => {
    const report = buildImportReport(mappedStreams())
    expect(report.missingRelationshipCount).toBeGreaterThanOrEqual(1)
  })

  it('includes the PPS assumption only when productionControl was actually present', () => {
    const withPps = buildImportReport(mappedStreams())
    expect(withPps.assumptionsDe.some((a) => a.includes('PPS'))).toBe(true)

    const withoutPps = buildImportReport([mappedStreams()[1]]) // Alternative A has no productionControl node
    expect(withoutPps.assumptionsDe.some((a) => a.includes('PPS'))).toBe(false)
  })

  it('always states the visual-layout-difference note when at least one node was imported', () => {
    const report = buildImportReport(mappedStreams())
    expect(report.visualLayoutDifferenceDe).toContain('Rundtrip')
  })

  it('never lists a class twice — repeated occurrences aggregate into one row with an incremented count', () => {
    const report = buildImportReport(mappedStreams())
    const classes = report.unsupported.map((u) => u.simvsmClass)
    expect(new Set(classes).size).toBe(classes.length)
  })

  describe('C18 Review-Fix (adversarial review PR #360): missing-endpoint vs. unsupported-link-class are counted and worded separately', () => {
    it('a link with a supported class but an unmapped endpoint counts ONLY as missingRelationshipCount, never in unsupported[]', () => {
      const stream = makeStreamWithLinkMappings([
        { simvsmKey: '-1', simvsmClass: 'informationFlow', supported: false, unsupportedReason: 'missing_endpoint', reasonDe: 'Verbindung verweist auf einen nicht übernommenen Knoten.', connection: null },
      ])
      const report = buildImportReport([stream])
      expect(report.missingRelationshipCount).toBe(1)
      expect(report.unsupported).toEqual([])
    })

    it('a link whose own CLASS is unsupported (e.g. "image") counts ONLY in unsupported[], never in missingRelationshipCount', () => {
      const stream = makeStreamWithLinkMappings([
        { simvsmKey: '-1', simvsmClass: 'image', supported: false, unsupportedReason: 'class', labelDe: 'Bild-Verknüpfung', reasonDe: 'Verknüpfung eines Foto-Anhangs — außerhalb des Scopes.', connection: null },
      ])
      const report = buildImportReport([stream])
      expect(report.missingRelationshipCount).toBe(0)
      expect(report.unsupported).toHaveLength(1)
      expect(report.unsupported[0]).toMatchObject({ simvsmClass: 'image', kind: 'link', labelDe: 'Bild-Verknüpfung' })
    })

    it('a node class and a link class sharing the same raw simvsmClass string ("image") never silently merge into one row', () => {
      const stream: MappedStream = {
        index: 0,
        name: 'x',
        isMainFlag: false,
        scenarioRole: 'current',
        hasResultData: false,
        nodes: [],
        connections: [],
        nodeMappings: [
          { simvsmKey: '1', simvsmClass: 'image', category: 'item', nodeName: 'Foto', supported: false, confidence: 0, labelDe: 'Foto', reasonDe: 'Bild-Anhang — außerhalb des Scopes.', vsmNode: null },
        ],
        linkMappings: [
          { simvsmKey: '-1', simvsmClass: 'image', supported: false, unsupportedReason: 'class', labelDe: 'Bild-Verknüpfung', reasonDe: 'Verknüpfung eines Foto-Anhangs — außerhalb des Scopes.', connection: null },
        ],
      }
      const report = buildImportReport([stream])
      expect(report.unsupported).toHaveLength(2)
      const kinds = report.unsupported.map((u) => u.kind).sort()
      expect(kinds).toEqual(['link', 'node'])
      expect(report.unsupported.find((u) => u.kind === 'node')?.labelDe).toBe('Foto')
      expect(report.unsupported.find((u) => u.kind === 'link')?.labelDe).toBe('Bild-Verknüpfung')
    })

    it('the missing-relationship sentence stays accurate now that it ONLY covers the endpoint case (real fixture: informationFlow into the unsupported noteVSM node)', () => {
      const report = buildImportReport(mappedStreams())
      expect(report.missingRelationshipCount).toBeGreaterThanOrEqual(1)
      // The synthetic fixture has no unsupported LINK class (only materialFlow/
      // informationFlow, both supported) — every unsupported[] row here is a
      // NODE class, confirming the two buckets stayed cleanly separated.
      expect(report.unsupported.every((u) => u.kind === 'node')).toBe(true)
    })
  })
})
