import { describe, it, expect, beforeEach } from 'vitest'
import ExcelJS from 'exceljs'
import {
  colLetter,
  headerLabel,
  headerFill,
  writeHeader,
  writeExampleRow,
  prefillFormulas,
  AnleitungSheet,
  TEMPLATE_FILL,
  EXAMPLE_ROW_SENTINEL,
  STANDARD_NOTE_LEGEND,
  DEFAULT_DATA_ROWS,
  type ColumnSpec,
} from '../template-helpers'

describe('colLetter', () => {
  it('returns the matching A1-style letter for 1-based column numbers', () => {
    expect(colLetter(1)).toBe('A')
    expect(colLetter(26)).toBe('Z')
    expect(colLetter(27)).toBe('AA')
    expect(colLetter(52)).toBe('AZ')
    expect(colLetter(53)).toBe('BA')
    expect(colLetter(702)).toBe('ZZ')
    expect(colLetter(703)).toBe('AAA')
  })
})

describe('headerLabel + headerFill', () => {
  it('appends the role suffix to the label', () => {
    expect(headerLabel({ name: 'foo', role: 'mandatory' })).toBe('foo *')
    expect(headerLabel({ name: 'foo', role: 'mandatory-basis' })).toBe('foo **')
    expect(headerLabel({ name: 'foo', role: 'reference' })).toBe('foo (Referenz)')
    expect(headerLabel({ name: 'foo', role: 'optional' })).toBe('foo')
    expect(headerLabel({ name: 'foo', role: 'computed' })).toBe('foo')
  })

  it('maps each role to a distinct fill color', () => {
    expect(headerFill({ name: 'a', role: 'mandatory' })).toEqual(TEMPLATE_FILL.BRAND_PETROL)
    expect(headerFill({ name: 'a', role: 'mandatory-basis' })).toEqual(TEMPLATE_FILL.BASIS_PETROL)
    expect(headerFill({ name: 'a', role: 'optional' })).toEqual(TEMPLATE_FILL.OPTIONAL_TEAL)
    expect(headerFill({ name: 'a', role: 'reference' })).toEqual(TEMPLATE_FILL.OPTIONAL_TEAL)
    expect(headerFill({ name: 'a', role: 'computed' })).toEqual(TEMPLATE_FILL.COMPUTED_TEAL)
  })
})

describe('writeHeader', () => {
  let wb: ExcelJS.Workbook
  let ws: ExcelJS.Worksheet
  beforeEach(() => {
    wb = new ExcelJS.Workbook()
    ws = wb.addWorksheet('Test')
  })

  const cols: ColumnSpec[] = [
    { name: 'a', role: 'mandatory', width: 12 },
    { name: 'b', role: 'optional' },
    { name: 'c', role: 'computed' },
  ]

  it('writes only the header row when no noteText is provided and returns the data start row (2)', () => {
    const dataStart = writeHeader(ws, cols)
    expect(dataStart).toBe(2)
    expect(ws.getCell('A1').value).toBe('a *')
    expect(ws.getCell('B1').value).toBe('b')
    expect(ws.getCell('C1').value).toBe('c')
  })

  it('writes a merged note in row 1, header in row 2, and returns 3', () => {
    const dataStart = writeHeader(ws, cols, { noteText: 'Hinweis: ' + STANDARD_NOTE_LEGEND })
    expect(dataStart).toBe(3)
    expect(ws.getCell('A1').value).toMatch(/Hinweis:/)
    expect(ws.getCell('A2').value).toBe('a *')
  })

  it('applies the configured column widths (default 16 when omitted)', () => {
    writeHeader(ws, cols)
    expect(ws.getColumn(1).width).toBe(12)
    expect(ws.getColumn(2).width).toBe(16)
    expect(ws.getColumn(3).width).toBe(16)
  })

  it('paints header cells with the role-based fill color', () => {
    writeHeader(ws, cols)
    const a = ws.getCell('A1').fill as ExcelJS.FillPattern
    const b = ws.getCell('B1').fill as ExcelJS.FillPattern
    const c = ws.getCell('C1').fill as ExcelJS.FillPattern
    expect(a.fgColor).toEqual(TEMPLATE_FILL.BRAND_PETROL)
    expect(b.fgColor).toEqual(TEMPLATE_FILL.OPTIONAL_TEAL)
    expect(c.fgColor).toEqual(TEMPLATE_FILL.COMPUTED_TEAL)
  })

  it('freezes panes below the header by default', () => {
    writeHeader(ws, cols)
    expect(ws.views?.[0]).toMatchObject({ state: 'frozen', ySplit: 1 })
  })

  it('skips pane freezing when freezePanes: false', () => {
    writeHeader(ws, cols, { freezePanes: false })
    // ExcelJS default views array is empty/no-op — we just want no frozen state set by us
    const v = ws.views?.[0]
    if (v) expect(v.state).not.toBe('frozen')
  })
})

describe('writeExampleRow', () => {
  let wb: ExcelJS.Workbook
  let ws: ExcelJS.Worksheet
  beforeEach(() => {
    wb = new ExcelJS.Workbook()
    ws = wb.addWorksheet('Test')
    // Header at row 1
    ws.addRow(['a', 'b', 'c'])
  })

  it('writes the example values in the next row and returns its 1-based number', () => {
    const rowNum = writeExampleRow(ws, [
      { value: EXAMPLE_ROW_SENTINEL },
      { value: 42 },
      { value: { formula: 'A2&"!"' } },
    ])
    expect(rowNum).toBe(2)
    expect(ws.getCell('A2').value).toBe(EXAMPLE_ROW_SENTINEL)
    expect(ws.getCell('B2').value).toBe(42)
    expect(ws.getCell('C2').value).toMatchObject({ formula: 'A2&"!"' })
  })

  it('paints every cell amber with italic grey font', () => {
    writeExampleRow(ws, [{ value: 'x' }, { value: 1 }, { value: 2 }])
    const a = ws.getCell('A2').fill as ExcelJS.FillPattern
    expect(a.fgColor).toEqual(TEMPLATE_FILL.EXAMPLE_AMBER)
    const font = ws.getCell('A2').font as ExcelJS.Font
    expect(font.italic).toBe(true)
    expect(font.color).toEqual(TEMPLATE_FILL.GREY)
  })

  it('applies per-cell numFmt when provided', () => {
    writeExampleRow(ws, [
      { value: 'x' },
      { value: 0.5, numFmt: '0.00%' },
      { value: 7 },
    ])
    expect(ws.getCell('B2').numFmt).toBe('0.00%')
    expect(ws.getCell('C2').numFmt).toBeFalsy()
  })
})

describe('prefillFormulas', () => {
  it('writes a formula and numFmt into each (row, col) pair', () => {
    const wb = new ExcelJS.Workbook()
    const ws = wb.addWorksheet('Test')
    prefillFormulas(
      ws,
      [
        { colIndex: 3, build: (r) => `A${r}+B${r}`, numFmt: '0.00' },
        { colIndex: 4, build: (r) => `A${r}*B${r}`, numFmt: '0.00%' },
      ],
      4,
      3, // 3 data rows: 4, 5, 6
    )
    expect(ws.getCell('C4').value).toMatchObject({ formula: 'A4+B4' })
    expect(ws.getCell('D4').value).toMatchObject({ formula: 'A4*B4' })
    expect(ws.getCell('C6').value).toMatchObject({ formula: 'A6+B6' })
    expect(ws.getCell('D6').value).toMatchObject({ formula: 'A6*B6' })
    expect(ws.getCell('C4').numFmt).toBe('0.00')
    expect(ws.getCell('D5').numFmt).toBe('0.00%')
    // Row 7 must not be touched
    expect(ws.getCell('C7').value).toBeNull()
  })

  it('exposes DEFAULT_DATA_ROWS = 100 as the canonical pre-fill length', () => {
    expect(DEFAULT_DATA_ROWS).toBe(100)
  })
})

describe('AnleitungSheet', () => {
  it('builds a new worksheet with the chosen name + column width', () => {
    const wb = new ExcelJS.Workbook()
    const a = new AnleitungSheet(wb, 'Hilfe', 50)
    expect(wb.getWorksheet('Hilfe')).toBeDefined()
    expect(a.worksheet().getColumn(1).width).toBe(50)
  })

  it('title/section/line/blank chain in order', () => {
    const wb = new ExcelJS.Workbook()
    const a = new AnleitungSheet(wb, 'A')
    a.title('Titel').section('Abschnitt').line('Zeile 1').blank().line('Zeile 2')
    const ws = a.worksheet()
    expect(ws.getCell('A1').value).toBe('Titel')
    expect(ws.getCell('A2').value).toBe('Abschnitt')
    expect(ws.getCell('A3').value).toBe('Zeile 1')
    expect(ws.getCell('A4').value).toBe('')
    expect(ws.getCell('A5').value).toBe('Zeile 2')
  })

  it('paints title with petrol+white and section with light teal+dark', () => {
    const wb = new ExcelJS.Workbook()
    const a = new AnleitungSheet(wb, 'A')
    a.title('T').section('S')
    const titleFill = a.worksheet().getCell('A1').fill as ExcelJS.FillPattern
    const sectionFill = a.worksheet().getCell('A2').fill as ExcelJS.FillPattern
    expect(titleFill.fgColor).toEqual(TEMPLATE_FILL.BRAND_PETROL)
    expect(sectionFill.fgColor).toEqual(TEMPLATE_FILL.SECTION_LIGHT)
  })

  it('columnDescriptions emits one line per [label, description] pair, label padded', () => {
    const wb = new ExcelJS.Workbook()
    const a = new AnleitungSheet(wb, 'A')
    a.columnDescriptions(
      [
        ['col_one *',    'Pflicht-Spalte eins'],
        ['col_two',      'Optionale Spalte zwei'],
      ],
      14,
    )
    expect(a.worksheet().getCell('A1').value).toBe('  col_one *     Pflicht-Spalte eins')
    expect(a.worksheet().getCell('A2').value).toBe('  col_two       Optionale Spalte zwei')
  })
})
