import { describe, it, expect } from 'vitest'
import { buildSimvsmImportPreview, mapFileToStreams } from '../preview'
import { computeSourceSignature } from '../signature'
import { buildSyntheticSimVsmFile } from './fixtures'

const SYNTHETIC_TEXT = JSON.stringify(buildSyntheticSimVsmFile())

describe('buildSimvsmImportPreview', () => {
  it('produces one family per JSON file, never touching a database', () => {
    const preview = buildSimvsmImportPreview({ jsonFiles: [{ fileName: 'werk-nord.json', text: SYNTHETIC_TEXT }], svgFileNames: [] })
    expect(preview.families).toHaveLength(1)
    expect(preview.families[0].parseOk).toBe(true)
    expect(preview.families[0].modelName).toBe('Werk Nord Testmodell')
  })

  it('computes a stable sourceSignature equal to computeSourceSignature(text)', () => {
    const preview = buildSimvsmImportPreview({ jsonFiles: [{ fileName: 'werk-nord.json', text: SYNTHETIC_TEXT }], svgFileNames: [] })
    expect(preview.families[0].sourceSignature).toBe(computeSourceSignature(SYNTHETIC_TEXT))
  })

  it('reports 2 streams with the first as "current" and the second as "alternative"', () => {
    const preview = buildSimvsmImportPreview({ jsonFiles: [{ fileName: 'werk-nord.json', text: SYNTHETIC_TEXT }], svgFileNames: [] })
    const streams = preview.families[0].streams
    expect(streams).toHaveLength(2)
    expect(streams[0].scenarioRole).toBe('current')
    expect(streams[1].scenarioRole).toBe('alternative')
  })

  it('pairs an SVG whose name matches the naming convention', () => {
    const preview = buildSimvsmImportPreview({
      jsonFiles: [{ fileName: 'werk-nord.json', text: SYNTHETIC_TEXT }],
      svgFileNames: ['werk-nord_Ist-Zustand.svg'],
    })
    expect(preview.families[0].pairedSvgFileNames).toEqual(['werk-nord_Ist-Zustand.svg'])
    expect(preview.orphanSvgFileNames).toEqual([])
  })

  it('reports an unrelated SVG as orphan, not silently dropped', () => {
    const preview = buildSimvsmImportPreview({
      jsonFiles: [{ fileName: 'werk-nord.json', text: SYNTHETIC_TEXT }],
      svgFileNames: ['completely-unrelated.svg'],
    })
    expect(preview.orphanSvgFileNames).toEqual(['completely-unrelated.svg'])
  })

  it('produces a parseOk:false family with an error message for invalid JSON, never throwing', () => {
    const preview = buildSimvsmImportPreview({ jsonFiles: [{ fileName: 'broken.json', text: '{not json' }], svgFileNames: [] })
    expect(preview.families[0].parseOk).toBe(false)
    expect(preview.families[0].parseError).toBeTruthy()
    expect(preview.families[0].report.importedEntityCount).toBe(0)
  })

  it('marks unknownClasses empty for the fully-registered synthetic fixture', () => {
    const preview = buildSimvsmImportPreview({ jsonFiles: [{ fileName: 'werk-nord.json', text: SYNTHETIC_TEXT }], svgFileNames: [] })
    expect(preview.families[0].unknownClasses).toEqual([])
  })

  it('flags a genuinely unknown node class as unknownClasses (not just unsupported)', () => {
    const raw = buildSyntheticSimVsmFileWithUnknownClass()
    const preview = buildSimvsmImportPreview({ jsonFiles: [{ fileName: 'x.json', text: JSON.stringify(raw) }], svgFileNames: [] })
    expect(preview.families[0].unknownClasses).toContain('somethingBrandNew')
  })

  it('groups related filenames as a non-binding hint without merging their data', () => {
    const preview = buildSimvsmImportPreview({
      jsonFiles: [
        { fileName: '20240101_werk-nord.json', text: SYNTHETIC_TEXT },
        { fileName: '20240201_werk-nord.json', text: SYNTHETIC_TEXT },
      ],
      svgFileNames: [],
    })
    expect(preview.families).toHaveLength(2) // still 2 independent families
    expect(preview.families[0].relatedFileNames).toEqual(['20240201_werk-nord.json'])
    expect(preview.families[1].relatedFileNames).toEqual(['20240101_werk-nord.json'])
  })

  it('exposes stable parserVersion/mappingVersion', () => {
    const preview = buildSimvsmImportPreview({ jsonFiles: [], svgFileNames: [] })
    expect(preview.parserVersion).toBe('simvsm-parser-1')
    expect(preview.mappingVersion).toBe('simvsm-1')
  })

  it('classCoverage rows sum to the total node+link count', () => {
    const preview = buildSimvsmImportPreview({ jsonFiles: [{ fileName: 'werk-nord.json', text: SYNTHETIC_TEXT }], svgFileNames: [] })
    const totalFromCoverage = preview.families[0].classCoverage.reduce((sum, r) => sum + r.count, 0)
    const totalNodesLinks = preview.families[0].streams.reduce((sum, s) => sum + s.nodeCount + s.connectionCount, 0)
    expect(totalFromCoverage).toBe(totalNodesLinks)
  })
})

describe('mapFileToStreams', () => {
  it('re-derives the exact same sourceSignature and stream shape a preview would', () => {
    const previewSig = computeSourceSignature(SYNTHETIC_TEXT)
    const { sourceSignature, streams } = mapFileToStreams(SYNTHETIC_TEXT, 'werk-nord.json')
    expect(sourceSignature).toBe(previewSig)
    expect(streams).toHaveLength(2)
  })

  it('returns an empty streams array (never throws) for unparseable text', () => {
    const { result, streams } = mapFileToStreams('{not json', 'broken.json')
    expect(result.ok).toBe(false)
    expect(streams).toEqual([])
  })
})

function buildSyntheticSimVsmFileWithUnknownClass(): unknown {
  return {
    name: 'Testdatei mit unbekannter Klasse',
    alternatives: [
      {
        name: 'A',
        model: {
          nodeDataArray: [{ key: 1, category: 'item', class: 'somethingBrandNew', nodeName: 'x', parameters: [] }],
          linkDataArray: [],
        },
      },
    ],
  }
}
