import { describe, it, expect } from 'vitest'
import {
  parseWorkshopImportRows,
  sanitizeFormulaCell,
  type WorkshopImportRow,
} from '../workshop-import'

function makeRow(overrides: Partial<WorkshopImportRow> = {}): WorkshopImportRow {
  return {
    stationName: 'Station A',
    observedCtRaw: '45',
    possibleCtRaw: '38',
    comment: 'Testkommentar',
    rowIndex: 2,
    ...overrides,
  }
}

describe('sanitizeFormulaCell', () => {
  it('passes through normal strings unchanged', () => {
    expect(sanitizeFormulaCell('Station A')).toBe('Station A')
    expect(sanitizeFormulaCell('45')).toBe('45')
    expect(sanitizeFormulaCell('')).toBe('')
  })

  it('prefixes formula-starting characters with apostrophe', () => {
    expect(sanitizeFormulaCell('=SUM(A1:A10)')).toBe("'=SUM(A1:A10)")
    expect(sanitizeFormulaCell('+1')).toBe("'+1")
    expect(sanitizeFormulaCell('-1')).toBe("'-1")
    expect(sanitizeFormulaCell('@USER')).toBe("'@USER")
  })

  it('does not double-escape already escaped strings', () => {
    // A string that starts with apostrophe is not a formula
    expect(sanitizeFormulaCell("'normal")).toBe("'normal")
  })
})

describe('parseWorkshopImportRows', () => {
  const stations = new Map<string, string>([
    ['station a', 'step-uuid-1'],
    ['station b', 'step-uuid-2'],
  ])

  it('parses a valid row correctly', () => {
    const result = parseWorkshopImportRows([makeRow()], stations)
    expect(result.errors).toHaveLength(0)
    expect(result.rows).toHaveLength(1)
    const row = result.rows[0]
    expect(row?.action).toBe('create')
    expect(row?.processStepId).toBe('step-uuid-1')
    expect(row?.observed_ct_sec).toBe(45)
    expect(row?.possible_ct_sec).toBe(38)
    expect(row?.comment).toBe('Testkommentar')
  })

  it('marks as update when station already has data', () => {
    const existingStepIds = new Set(['step-uuid-1'])
    const result = parseWorkshopImportRows([makeRow()], stations, existingStepIds)
    expect(result.rows[0]?.action).toBe('update')
  })

  it('marks as create when station has no existing data', () => {
    const result = parseWorkshopImportRows([makeRow()], stations, new Set())
    expect(result.rows[0]?.action).toBe('create')
  })

  it('adds error for unknown station name', () => {
    const result = parseWorkshopImportRows(
      [makeRow({ stationName: 'Unbekannte Station' })],
      stations,
    )
    expect(result.errors).toHaveLength(1)
    expect(result.errors[0]).toContain('Zeile 2')
    expect(result.errors[0]).toContain('nicht zugeordnet')
    expect(result.rows).toHaveLength(0)
  })

  it('adds error for a non-numeric observed CT', () => {
    const result = parseWorkshopImportRows(
      [makeRow({ observedCtRaw: 'nicht_eine_zahl' })],
      stations,
    )
    expect(result.errors).toHaveLength(1)
    expect(result.errors[0]).toContain('Beobachtete CT')
  })

  it('accepts empty possible CT (null)', () => {
    const result = parseWorkshopImportRows(
      [makeRow({ possibleCtRaw: '' })],
      stations,
    )
    expect(result.errors).toHaveLength(0)
    expect(result.rows[0]?.possible_ct_sec).toBeNull()
  })

  it('adds a duplicate warning for station seen twice in the same import', () => {
    const rows = [
      makeRow({ rowIndex: 2 }),
      makeRow({ stationName: 'Station A', rowIndex: 3 }),
    ]
    const result = parseWorkshopImportRows(rows, stations)
    expect(result.errors.some((e) => e.includes('doppelt'))).toBe(true)
    // Only the first occurrence is imported
    expect(result.rows).toHaveLength(1)
  })

  it('flags formula injection in station name', () => {
    const stationsWithFormula = new Map<string, string>([
      ['=evil', 'step-uuid-evil'],
    ])
    const result = parseWorkshopImportRows(
      [makeRow({ stationName: '=evil' })],
      stationsWithFormula,
    )
    // Station name is sanitized; formula entries go into the error report
    expect(result.errors.some((e) => e.includes('Formel'))).toBe(true)
    expect(result.rows).toHaveLength(0)
  })

  it('flags formula injection in CT field', () => {
    const result = parseWorkshopImportRows(
      [makeRow({ observedCtRaw: '=MALICIOUS()' })],
      stations,
    )
    expect(result.errors.some((e) => e.includes('Formel'))).toBe(true)
    expect(result.rows).toHaveLength(0)
  })

  it('uses comma as decimal separator', () => {
    const result = parseWorkshopImportRows(
      [makeRow({ observedCtRaw: '45,5', possibleCtRaw: '38,2' })],
      stations,
    )
    expect(result.errors).toHaveLength(0)
    expect(result.rows[0]?.observed_ct_sec).toBeCloseTo(45.5)
    expect(result.rows[0]?.possible_ct_sec).toBeCloseTo(38.2)
  })

  it('handles station name matching case-insensitively with leading/trailing spaces', () => {
    const result = parseWorkshopImportRows(
      [makeRow({ stationName: '  Station A  ' })],
      stations,
    )
    expect(result.errors).toHaveLength(0)
    expect(result.rows[0]?.processStepId).toBe('step-uuid-1')
  })
})
