import { describe, it, expect } from 'vitest'
import { buildG60Rows } from '../persistence'
import { parseG60Workbook, extractFullTab, sheetFromCells, workbookFromSheets } from '../parser'
import { parseG60WorkbookGuarded } from '../structure-guard'
import { makeWorkbook, tabCells, RATES_CELLS } from './fixtures'

describe('buildG60Rows', () => {
  const wb = makeWorkbook(100, 200)
  const parsed = parseG60Workbook(wb)
  const fullTabs = Object.fromEntries(
    Object.keys(parsed.tabs).map((name) => [name, extractFullTab(wb.sheet(name)!)]),
  )
  const rows = buildG60Rows({ projectId: 'proj-1', fileId: 'file-1' }, parsed, fullTabs)

  it('maps g60_meta with rates and volumes', () => {
    expect(rows.meta.rates.ovFK_d).toBeCloseTo(0.1)
    expect(rows.meta.volumes.years).toEqual([2026, 2027])
  })

  it('maps one qaf_g60_tab row per cost tab in natural order with ownership', () => {
    expect(rows.tabRows.map((t) => t.tab_name)).toEqual(['2_2', '10_2'])
    const first = rows.tabRows[0]
    expect(first).toMatchObject({ project_id: 'proj-1', file_id: 'file-1', tab_index: 0, part: 'P-A' })
    expect((first.aggregate as { Sales: number }).Sales).toBe(20)
    expect(Array.isArray(first.component_rows)).toBe(true)
  })

  it('maps qaf_input_card rows with numeric/text split', () => {
    const lohn = rows.cardRows.find((c) => c.code === 'C42')!
    expect(lohn).toMatchObject({ project_id: 'proj-1', file_id: 'file-1', label: 'Lohnsatz DE', value_numeric: 38 })
    expect(lohn.value_text).toBeNull()
  })

  it('keeps non-numeric card values as text', () => {
    const rowsWithText = buildG60Rows(
      { projectId: 'p', fileId: 'f' },
      { ...parsed, card: { C50: { label: 'Incoterm', value: 'FCA Werk' } } },
      {},
    )
    expect(rowsWithText.cardRows[0]).toMatchObject({ code: 'C50', value_numeric: null, value_text: 'FCA Werk' })
  })
})

describe('buildG60Rows — structure guard passthrough (KAR-888)', () => {
  it('embeds inputStructure in meta and defaults to "ok" for a plain (unguarded) parse result', () => {
    const wb = makeWorkbook(100, 200)
    const parsed = parseG60Workbook(wb)
    const rows = buildG60Rows({ projectId: 'proj-1', fileId: 'file-1' }, parsed, {})
    expect(rows.meta.inputStructure).toEqual({ ok: true, mismatches: [], confidence: 1 })
  })

  it('carries the guarded inputStructure/excludedTabs through to meta, and per-tab structure into each aggregate', () => {
    const wb = makeWorkbook(100, 200)
    const guarded = parseG60WorkbookGuarded(wb)
    const fullTabs = Object.fromEntries(Object.keys(guarded.tabs).map((name) => [name, extractFullTab(wb.sheet(name)!)]))
    const rows = buildG60Rows({ projectId: 'proj-1', fileId: 'file-1' }, guarded, fullTabs)
    expect(rows.meta.inputStructure).toEqual({ ok: true, mismatches: [], confidence: 1 })
    expect(rows.meta.excludedTabs).toEqual({})
    const first = rows.tabRows[0]
    expect((first.aggregate as { structure: unknown }).structure).toEqual({ ok: true, mismatches: [], confidence: 1 })
  })

  // PR #328 review fix (finding [2]/[3]): rateDegradation used to be computed
  // by parseG60WorkbookGuarded but never threaded into G60FileMeta — this is
  // the regression anchor proving buildG60Rows now carries it additively,
  // same "undefined by default, present only on the guarded+degraded path"
  // contract as inputStructure/excludedTabs above.
  it('carries rateDegradation through to meta when the rate-card is hard-broken, omits it otherwise', () => {
    const intactWb = makeWorkbook(100, 200)
    const intactGuarded = parseG60WorkbookGuarded(intactWb)
    const intactRows = buildG60Rows({ projectId: 'proj-1', fileId: 'file-1' }, intactGuarded, {})
    expect(intactRows.meta.rateDegradation).toBeUndefined()

    const brokenWb = workbookFromSheets({
      INPUT: sheetFromCells({ ...RATES_CELLS, B22: undefined, B23: undefined, B24: undefined, B25: undefined }),
      '2_2': sheetFromCells(tabCells({ part: 'P-A', sales: 20, w: 4, at15: 100 })),
    })
    const brokenGuarded = parseG60WorkbookGuarded(brokenWb)
    const brokenRows = buildG60Rows({ projectId: 'proj-1', fileId: 'file-1' }, brokenGuarded, {})
    expect(brokenRows.meta.rateDegradation).toEqual({
      facet: 'g60_rates',
      reason: 'PARSE_FAILED',
      sheet: 'INPUT',
      message: expect.stringContaining('SGA/Profit'),
    })
  })

  it('omits rateDegradation for a plain (unguarded) parse result', () => {
    const wb = makeWorkbook(100, 200)
    const parsed = parseG60Workbook(wb)
    const rows = buildG60Rows({ projectId: 'proj-1', fileId: 'file-1' }, parsed, {})
    expect(rows.meta.rateDegradation).toBeUndefined()
  })
})
