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

import {
  fontManager,
  SceneGraph,
  buildFontDigestMap,
  buildDerivedTextDataV4,
  initCodec
} from '@open-pencil/core'

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

describe('clipboard derived text export', () => {
  test('builds richer v4 derivedTextData from shaped text + glyph outlines', async () => {
    await initCodec()

    const font = expectDefined(
      await fontManager.fetchBundledFont('/Inter-Regular.ttf'),
      'bundled Inter font'
    )
    fontManager.markLoaded('Inter', 'Regular', font)

    const graph = new SceneGraph()
    const page = graph.getPages()[0]
    const text = graph.createNode('TEXT', page.id, {
      name: 'Hello',
      text: 'Hello',
      width: 120,
      height: 24,
      fontFamily: 'Inter',
      fontSize: 16,
      fontWeight: 400
    })

    const fontDigestMap = await buildFontDigestMap(graph)
    const blobs: Uint8Array[] = []
    const derived = await buildDerivedTextDataV4(
      text,
      fontDigestMap,
      {
        lineHeight: 20,
        lineAscent: 15,
        lineWidth: 42,
        baseline: 16,
        glyphs: [
          { firstCharacter: 0, x: 0, y: 16, advance: 8 },
          { firstCharacter: 1, x: 8, y: 16, advance: 8 },
          { firstCharacter: 2, x: 16, y: 16, advance: 8 },
          { firstCharacter: 3, x: 24, y: 16, advance: 8 },
          { firstCharacter: 4, x: 32, y: 16, advance: 10 }
        ],
        logicalIndexToCharacterOffsetMap: [0, 8, 16, 24, 32, 42]
      },
      blobs
    )

    const derivedTextData = expectDefined(derived, 'derived text data')
    const firstGlyph = expectDefined(derivedTextData.glyphs[0], 'first glyph')
    const lastGlyph = expectDefined(derivedTextData.glyphs[4], 'last glyph')
    const baseline = expectDefined(derivedTextData.baselines[0], 'first baseline')
    const line = expectDefined(derivedTextData.derivedLines[0], 'first derived line')

    expect(derivedTextData.fontMetaData.length).toBeGreaterThan(0)
    expect(derivedTextData.fontMetaData[0].key.style).toBe('Regular')
    expect(derivedTextData.glyphs.length).toBeGreaterThan(0)
    expect(derivedTextData.baselines.length).toBeGreaterThan(0)
    expect(derivedTextData.logicalIndexToCharacterOffsetMap.length).toBe(text.text.length + 1)
    expect(derivedTextData.logicalIndexToCharacterOffsetMap[5]).toBe(42)
    expect(line.directionality).toBe('LTR')
    expect(derivedTextData.truncationStartIndex).toBe(-1)
    expect(derivedTextData.truncatedHeight).toBe(-1)
    expect(firstGlyph.commandsBlob).toBe(0)
    expect(blobs[0].length).toBeGreaterThan(0)
    expect(firstGlyph.position.x).toBe(0)
    expect(lastGlyph.position.x).toBe(32)
    expect(baseline.lineHeight).toBe(20)
    expect(baseline.lineAscent).toBe(15)
    expect(baseline.width).toBe(42)
  })

  test('preserves shaped multiline baselines for Figma text editing', async () => {
    await initCodec()

    const graph = new SceneGraph()
    const page = graph.getPages()[0]
    const text = graph.createNode('TEXT', page.id, {
      name: 'Wrapped',
      text: 'Analytics Overview',
      width: 360,
      height: 136,
      fontFamily: 'Missing Preview Font',
      fontSize: 56,
      fontWeight: 700
    })

    const derived = expectDefined(
      await buildDerivedTextDataV4(
        text,
        await buildFontDigestMap(graph),
        {
          lineHeight: 68,
          lineAscent: 54,
          lineWidth: 272,
          baseline: 54.36,
          baselines: [
            {
              firstCharacter: 0,
              endCharacter: 10,
              position: { x: 0, y: 54.36 },
              width: 271.58,
              lineY: 0,
              lineHeight: 68,
              lineAscent: 54
            },
            {
              firstCharacter: 10,
              endCharacter: 18,
              position: { x: 0, y: 122.36 },
              width: 261.9,
              lineY: 68,
              lineHeight: 68,
              lineAscent: 54
            }
          ],
          glyphs: [
            { firstCharacter: 0, x: 0, y: 54.36, advance: 42 },
            { firstCharacter: 10, x: 0, y: 122.36, advance: 44 }
          ],
          logicalIndexToCharacterOffsetMap: Array.from({ length: 19 }, () => 0)
        },
        null
      ),
      'derived text'
    )

    expect(derived.baselines).toHaveLength(2)
    expect(derived.baselines[0].endCharacter).toBe(10)
    expect(derived.baselines[1].firstCharacter).toBe(10)
    expect(derived.baselines[1].position.y).toBe(122.36)
    expect(derived.layoutSize).toEqual({ x: 360, y: 136 })
  })

  test('uses Figma font style names in metadata', async () => {
    await initCodec()

    const font = expectDefined(
      await fontManager.fetchBundledFont('/Inter-SemiBold.ttf'),
      'bundled Inter font'
    )
    fontManager.markLoaded('Inter', 'SemiBold', font)

    const graph = new SceneGraph()
    const page = graph.getPages()[0]
    const text = graph.createNode('TEXT', page.id, {
      text: 'Title',
      fontFamily: 'Inter',
      fontSize: 16,
      fontWeight: 600
    })

    const fontDigestMap = await buildFontDigestMap(graph)
    const blobs: Uint8Array[] = []
    const derived = expectDefined(
      await buildDerivedTextDataV4(text, fontDigestMap, null, blobs),
      'derived text'
    )

    expect(derived.fontMetaData[0].key.style).toBe('Semi Bold')
    expect(derived.glyphs[0].commandsBlob).toBe(0)
    expect(blobs[0].length).toBeGreaterThan(0)
    expect(derived.glyphs[1].position.x).toBeGreaterThan(0)
    expect(derived.glyphs[1].advance).toBeGreaterThan(0)
    expect(derived.logicalIndexToCharacterOffsetMap[1]).toBeGreaterThan(0)
  })
})
