import { describe, it, expect } from 'vitest'
import { parseSimVsmJsonText, parseSimVsmValue, PARSER_VERSION } from '../parser'
import { buildSyntheticSimVsmFile } from './fixtures'

describe('parseSimVsmValue', () => {
  it('parses a well-formed synthetic file into modelName/description/version/alternatives', () => {
    const result = parseSimVsmValue(buildSyntheticSimVsmFile(), 'werk-nord.json')
    expect(result.ok).toBe(true)
    if (!result.ok) return
    expect(result.modelName).toBe('Werk Nord Testmodell')
    expect(result.description).toBe('Synthetische Testdatei für Unit-Tests')
    expect(result.createdWithVersion).toBe('3.36.0')
    expect(result.mainVersion).toBe('3.20.0')
    expect(result.productCount).toBe(1)
    expect(result.shiftCalendarCount).toBe(1)
    expect(result.alternatives).toHaveLength(2)
  })

  it('reads all 7 nodes and 5 links of the Ist-Zustand alternative', () => {
    const result = parseSimVsmValue(buildSyntheticSimVsmFile(), 'werk-nord.json')
    if (!result.ok) throw new Error('expected ok')
    const ist = result.alternatives[0]
    expect(ist.nodes).toHaveLength(7)
    expect(ist.links).toHaveLength(5)
    expect(ist.name).toBe('Ist-Zustand')
    expect(ist.isMainFlag).toBe(false)
  })

  it('reads parameters into a class-keyed map', () => {
    const result = parseSimVsmValue(buildSyntheticSimVsmFile(), 'werk-nord.json')
    if (!result.ok) throw new Error('expected ok')
    const stanzen = result.alternatives[0].nodes.find((n) => n.key === '2')
    expect(stanzen?.params.get('Availability')?.value).toBe(95)
    expect(stanzen?.params.has('NumberOfWorkers')).toBe(true)
  })

  it('falls back to the file name for a missing/blank "name"', () => {
    const raw = buildSyntheticSimVsmFile() as Record<string, unknown>
    delete raw.name
    const result = parseSimVsmValue(raw, 'unbenannt.json')
    if (!result.ok) throw new Error('expected ok')
    expect(result.modelName).toBe('unbenannt')
  })

  it('falls back to "Alternative N" for a blank alternative name', () => {
    const alt = buildSyntheticSimVsmFile({ alternatives: [{ model: { nodeDataArray: [], linkDataArray: [] } }] }) as { alternatives: unknown[] }
    const result = parseSimVsmValue(alt, 'x.json')
    if (!result.ok) throw new Error('expected ok')
    expect(result.alternatives[0].name).toBe('Alternative 1')
  })

  it('skips a node with no "key" and records an issue, never throwing', () => {
    const raw = buildSyntheticSimVsmFile({
      alternatives: [{ name: 'A', model: { nodeDataArray: [{ category: 'item', class: 'singleProcess', nodeName: 'ohne key' }], linkDataArray: [] } }],
    })
    const result = parseSimVsmValue(raw, 'x.json')
    if (!result.ok) throw new Error('expected ok')
    expect(result.alternatives[0].nodes).toHaveLength(0)
    expect(result.issues.some((i) => i.code === 'node_missing_key')).toBe(true)
  })

  it('drops a link whose endpoint does not resolve to a known node key, recording an issue', () => {
    const raw = buildSyntheticSimVsmFile({
      alternatives: [
        {
          name: 'A',
          model: {
            nodeDataArray: [{ key: 1, category: 'item', class: 'singleProcess', nodeName: 'P1' }],
            linkDataArray: [{ key: -1, class: 'materialFlow', from: 1, to: 999 }],
          },
        },
      ],
    })
    const result = parseSimVsmValue(raw, 'x.json')
    if (!result.ok) throw new Error('expected ok')
    expect(result.alternatives[0].links).toHaveLength(0)
    expect(result.issues.some((i) => i.code === 'link_dangling_endpoint')).toBe(true)
  })

  it('fails structurally (ok:false) for a non-object top level, never throws', () => {
    expect(parseSimVsmValue([1, 2, 3], 'x.json').ok).toBe(false)
    expect(parseSimVsmValue('a string', 'x.json').ok).toBe(false)
    expect(parseSimVsmValue(null, 'x.json').ok).toBe(false)
    expect(parseSimVsmValue(42, 'x.json').ok).toBe(false)
  })

  it('fails (ok:false) when "alternatives" is missing or empty', () => {
    expect(parseSimVsmValue({ name: 'x' }, 'x.json').ok).toBe(false)
    expect(parseSimVsmValue({ name: 'x', alternatives: [] }, 'x.json').ok).toBe(false)
  })

  it('never throws for a pathological getter-based object', () => {
    const evil = {
      get alternatives() {
        throw new Error('boom')
      },
    }
    const result = parseSimVsmValue(evil, 'evil.json')
    expect(result.ok).toBe(false)
  })
})

describe('parseSimVsmJsonText', () => {
  it('round-trips through JSON.stringify', () => {
    const result = parseSimVsmJsonText(JSON.stringify(buildSyntheticSimVsmFile()), 'werk-nord.json')
    expect(result.ok).toBe(true)
  })

  it('fails gracefully (ok:false) for invalid JSON text, never throws', () => {
    const result = parseSimVsmJsonText('{not valid json', 'broken.json')
    expect(result.ok).toBe(false)
    if (!result.ok) expect(result.error).toContain('broken.json')
  })

  it('exposes a stable PARSER_VERSION', () => {
    expect(PARSER_VERSION).toBe('simvsm-parser-1')
  })
})
