import { describe, test, expect, beforeAll } from 'bun:test'

import { exportFigFile, parseFigFile, initCodec, SceneGraph } from '@open-pencil/core'

import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'

beforeAll(async () => {
  await initCodec()
})

function pageId(graph: SceneGraph) {
  return graph.getPages()[0].id
}

describe('Font family normalization on .fig export', () => {
  test('strips optical size suffix from font family', async () => {
    const graph = new SceneGraph()
    graph.createNode('TEXT', pageId(graph), {
      name: 'Test',
      x: 0,
      y: 0,
      width: 100,
      height: 20,
      text: 'Hello',
      fontFamily: 'DM Sans 9pt',
      fontWeight: 400,
      fontSize: 14
    })

    const exported = await exportFigFile(graph)
    const reimported = await parseFigFile(exported.buffer as ArrayBuffer)

    const nodes = [...reimported.nodes.values()]
    const textNode = expectDefined(
      nodes.find((n) => n.type === 'TEXT'),
      'text node'
    )
    expect(textNode.fontFamily).toBe('DM Sans')
  })

  test('preserves normal font family names', async () => {
    const graph = new SceneGraph()
    graph.createNode('TEXT', pageId(graph), {
      name: 'Test',
      x: 0,
      y: 0,
      width: 100,
      height: 20,
      text: 'Hello',
      fontFamily: 'Inter',
      fontWeight: 400,
      fontSize: 14
    })

    const exported = await exportFigFile(graph)
    const reimported = await parseFigFile(exported.buffer as ArrayBuffer)

    const nodes = [...reimported.nodes.values()]
    const textNode = expectDefined(
      nodes.find((n) => n.type === 'TEXT'),
      'text node'
    )
    expect(textNode.fontFamily).toBe('Inter')
  })

  test('strips Variable suffix from font family', async () => {
    const graph = new SceneGraph()
    graph.createNode('TEXT', pageId(graph), {
      name: 'Test',
      x: 0,
      y: 0,
      width: 100,
      height: 20,
      text: 'Hello',
      fontFamily: 'Roboto Variable',
      fontWeight: 400,
      fontSize: 14
    })

    const exported = await exportFigFile(graph)
    const reimported = await parseFigFile(exported.buffer as ArrayBuffer)

    const nodes = [...reimported.nodes.values()]
    const textNode = expectDefined(
      nodes.find((n) => n.type === 'TEXT'),
      'text node'
    )
    expect(textNode.fontFamily).toBe('Roboto')
  })
})

describe('TEXT node default fill', () => {
  test('TEXT node without explicit fills exports with solid black fill', async () => {
    const graph = new SceneGraph()
    graph.createNode('TEXT', pageId(graph), {
      name: 'No Fill Specified',
      x: 0,
      y: 0,
      width: 100,
      height: 20,
      text: 'Hello',
      fontFamily: 'Inter',
      fontWeight: 400,
      fontSize: 14
    })

    const exported = await exportFigFile(graph)
    const reimported = await parseFigFile(exported.buffer as ArrayBuffer)

    const nodes = [...reimported.nodes.values()]
    const textNode = expectDefined(
      nodes.find((n) => n.type === 'TEXT'),
      'text node'
    )
    expect(textNode.fills.length).toBe(1)
    expect(textNode.fills[0].type).toBe('SOLID')
    expect(textNode.fills[0].color.r).toBe(0)
    expect(textNode.fills[0].color.g).toBe(0)
    expect(textNode.fills[0].color.b).toBe(0)
    expect(textNode.fills[0].color.a).toBe(1)
  })

  test('TEXT node with explicit fill preserves it', async () => {
    const graph = new SceneGraph()
    graph.createNode('TEXT', pageId(graph), {
      name: 'Gold Text',
      x: 0,
      y: 0,
      width: 100,
      height: 20,
      text: 'Hello',
      fontFamily: 'Inter',
      fontWeight: 400,
      fontSize: 14,
      fills: [{ type: 'SOLID', color: { r: 0.96, g: 0.72, b: 0, a: 1 }, opacity: 1, visible: true }]
    })

    const exported = await exportFigFile(graph)
    const reimported = await parseFigFile(exported.buffer as ArrayBuffer)

    const nodes = [...reimported.nodes.values()]
    const textNode = expectDefined(
      nodes.find((n) => n.type === 'TEXT'),
      'text node'
    )
    expect(textNode.fills.length).toBe(1)
    expect(textNode.fills[0].color.r).toBeCloseTo(0.96, 1)
    expect(textNode.fills[0].color.g).toBeCloseTo(0.72, 1)
    expect(textNode.fills[0].color.b).toBeCloseTo(0, 1)
  })

  test('non-TEXT node still defaults to empty fills', async () => {
    const graph = new SceneGraph()
    const node = graph.createNode('RECTANGLE', pageId(graph), {
      name: 'Rect',
      x: 0,
      y: 0,
      width: 100,
      height: 100
    })
    expect(getNodeOrThrow(graph, node.id).fills.length).toBe(0)
  })
})
