import assert from 'node:assert/strict'
import test from 'node:test'
import { createSpreadsheet } from '../src/facade/spreadsheet'

test('Spreadsheet evaluates unit formulas and aliases deterministically', () => {
  const sheet = createSpreadsheet('sheet', 'Parameters')
  sheet.setCell('A1', '6 mm', { alias: 'Width' })
  sheet.setCell('A2', '=Width * 2', { alias: 'Length' })
  sheet.setCell('B1', '=max(A1, 4 mm)')
  const evaluation = sheet.evaluate()
  assert.equal(evaluation.errors.length, 0)
  assert.deepEqual(evaluation.cells.A1.value, { value: 6, dimension: 'length' })
  assert.deepEqual(evaluation.cells.A2.value, { value: 12, dimension: 'length' })
  assert.deepEqual(evaluation.cells.B1.value, { value: 6, dimension: 'length' })
  assert.deepEqual(evaluation.dependencies.map(({ source, target, reference }) => `${source}:${target}:${reference}`).sort(), ['A2:A1:Width', 'B1:A1:A1'])
})

test('Spreadsheet rejects duplicate aliases and reports dependency cycles', () => {
  const sheet = createSpreadsheet()
  sheet.setCell('A1', 1, { alias: 'Count' })
  assert.throws(() => sheet.setCell('A2', 2, { alias: 'Count' }), /already used/)
  sheet.setCell('B1', '=C1 + 1')
  sheet.setCell('C1', '=B1 + 1')
  const evaluation = sheet.evaluate()
  assert.equal(evaluation.cycles.length > 0, true)
  assert.equal(evaluation.cells.B1.error?.code, 'DEPENDENCY_CYCLE')
  assert.equal(evaluation.cells.C1.error?.code, 'DEPENDENCY_CYCLE')
})

test('Spreadsheet preserves binding metadata and exports escaped CSV', () => {
  const sheet = createSpreadsheet()
  sheet.setCell('$A$1', '6 mm')
  sheet.setAlias('A1', 'Width')
  sheet.setCell('B1', '=Width * 2')
  const binding = sheet.bindProperty('Width', 'pad', 'Length')
  assert.deepEqual(binding, { alias: 'Width', objectId: 'pad', propertyName: 'Length' })
  assert.deepEqual(sheet.snapshot().bindings, [binding])
  assert.match(sheet.exportCsv(), /^6 length,12 length$/)
  assert.throws(() => sheet.setCell('1A', 1), /Invalid spreadsheet cell address/)
  assert.throws(() => sheet.bindProperty('Missing', 'pad', 'Length'), /does not exist/)
})

test('Spreadsheet preserves styles, merged ranges, named ranges and address shifts', () => {
  const sheet = createSpreadsheet()
  sheet.setCell('A1', 1)
  sheet.setCell('A2', '=A1 + 1')
  sheet.setStyle('A1:B2', { background: '#e6f4f1', bold: true, numberFormat: '0.00' })
  sheet.mergeCells('B1:A2')
  sheet.setNamedRange('Inputs', 'A1:A2')
  sheet.insertRows(2)
  sheet.insertColumns(2)
  const snapshot = sheet.snapshot()
  assert.equal(snapshot.cells.find((cell) => cell.address === 'A1')?.style?.bold, true)
  assert.deepEqual(snapshot.namedRanges, [{ name: 'Inputs', range: 'A1:A3' }])
  assert.deepEqual(snapshot.mergedRanges, ['A1:C3'])
  assert.equal(snapshot.cells.find((cell) => cell.address === 'A3')?.input, '=A1 + 1')
  assert.throws(() => sheet.setStyle('A1', { color: 'red' }), /invalid color/)
})
