import { describe, it, expect, vi } from 'vitest'
import { buildTree } from '../figma-sync/generate-token-exports.mjs'

const toks = [
  { name: 'color-grey-8', value: '#595959', scope: 'dark', section: 'Neutrals' },
  { name: 'input-line-border', value: 'var(--color-grey-8)', scope: 'dark', section: 'Input' },
  { name: 'bg-layer1', value: '#141414', scope: 'dark', section: 'Surface' },
  { name: 'bg-layer1', value: '#ffffff', scope: 'light', section: 'Surface' },
]

describe('buildTree', () => {
  it('keeps whole-value var as reference and resolves path', () => {
    const { dtcg } = buildTree(toks)
    expect(dtcg.semantic['input-line-border']).toEqual({ $type: 'color', $value: '{palette.color-grey-8}' })
  })

  it('attaches $extensions modes for light overrides', () => {
    const { dtcg } = buildTree(toks)
    expect(dtcg.semantic['bg-layer1']).toEqual({
      $type: 'color', $value: '#141414',
      $extensions: { 'tvu.mode': { dark: '#141414', light: '#ffffff' } },
    })
  })

  it('throws on unknown reference target', () => {
    expect(() => buildTree([{ name: 'x', value: 'var(--nope)', scope: 'dark', section: 'Input' }])).toThrow(/nope/)
  })

  it('resolves a reference node $type from the TARGET token, not the alias prefix guess', () => {
    const withDimensionAlias = [
      { name: 'sp-xs', value: '12px', scope: 'dark', section: 'Spacing' },
      { name: 'slider-track-h-m', value: 'var(--sp-xs)', scope: 'dark', section: 'Slider' },
    ]
    const { dtcg } = buildTree(withDimensionAlias)
    expect(dtcg.nonColor['slider-track-h-m']).toEqual({ $type: 'dimension', $value: '{nonColor.sp-xs}' })
  })

  it('resolves light-override references (not just literals) into $extensions', () => {
    const withRefOverride = [
      { name: 'color-grey-8', value: '#595959', scope: 'dark', section: 'Neutrals' },
      { name: 'color-grey-2', value: '#f0f0f0', scope: 'dark', section: 'Neutrals' },
      { name: 'bg-panel', value: 'var(--color-grey-8)', scope: 'dark', section: 'Surface' },
      { name: 'bg-panel', value: 'var(--color-grey-2)', scope: 'light', section: 'Surface' },
    ]
    const { dtcg } = buildTree(withRefOverride)
    expect(dtcg.semantic['bg-panel']).toEqual({
      $type: 'color',
      $value: '{palette.color-grey-8}',
      $extensions: { 'tvu.mode': { dark: '{palette.color-grey-8}', light: '{palette.color-grey-2}' } },
    })
  })

  it('throws on unknown reference target in a light override too', () => {
    expect(() => buildTree([
      { name: 'x', value: '#fff', scope: 'dark', section: 'Surface' },
      { name: 'x', value: 'var(--nope)', scope: 'light', section: 'Surface' },
    ])).toThrow(/nope/)
  })

  it('resolves a composite value (multiple vars) to a final literal with no var( left', () => {
    const { dtcg } = buildTree([
      { name: 'font-family-en', value: '-apple-system, sans-serif', scope: 'dark', section: 'Type' },
      { name: 'font-family-zh', value: "'PingFang SC', sans-serif", scope: 'dark', section: 'Type' },
      { name: 'f', value: 'var(--font-family-en), var(--font-family-zh)', scope: 'dark', section: 'Type' },
    ])
    expect(dtcg.nonColor['f'].$value).toBe("-apple-system, sans-serif, 'PingFang SC', sans-serif")
    expect(dtcg.nonColor['f'].$value).not.toContain('var(')
  })

  it('resolves a nested composite (literal + var pointing at a composite) fully', () => {
    const { dtcg } = buildTree([
      { name: 'font-family-en', value: '-apple-system, sans-serif', scope: 'dark', section: 'Type' },
      { name: 'font-family-zh', value: "'PingFang SC', sans-serif", scope: 'dark', section: 'Type' },
      { name: 'font-family-base', value: 'var(--font-family-en), var(--font-family-zh)', scope: 'dark', section: 'Type' },
      { name: 't', value: '400 14px/21px var(--font-family-base)', scope: 'dark', section: 'Type' },
    ])
    expect(dtcg.nonColor['t'].$value).toBe("400 14px/21px -apple-system, sans-serif, 'PingFang SC', sans-serif")
    expect(dtcg.nonColor['t'].$value).not.toContain('var(')
  })

  it('throws on an unknown inner var target inside a composite value', () => {
    expect(() => buildTree([
      { name: 't', value: '400 14px var(--nope)', scope: 'dark', section: 'Type' },
    ])).toThrow(/nope/)
  })

  it('throws (does not infinite-recurse) on a cyclic var chain in a composite value', () => {
    expect(() => buildTree([
      { name: 'a', value: '1px var(--b)', scope: 'dark', section: 'Type' },
      { name: 'b', value: '2px var(--a)', scope: 'dark', section: 'Type' },
    ])).toThrow(/cyclic/)
  })

  it('warns (but does not throw or add a node) for a light-only token with no dark counterpart', () => {
    const spy = vi.spyOn(console, 'warn').mockImplementation(() => {})
    try {
      const { dtcg } = buildTree([
        { name: 'bg-layer1', value: '#141414', scope: 'dark', section: 'Surface' },
        { name: 'bg-light-only', value: '#ffffff', scope: 'light', section: 'Surface' },
      ])
      expect(dtcg.semantic['bg-light-only']).toBeUndefined()
      expect(dtcg.palette['bg-light-only']).toBeUndefined()
      expect(dtcg.nonColor['bg-light-only']).toBeUndefined()
      expect(spy).toHaveBeenCalledWith(
        expect.stringContaining('light-only token --bg-light-only has no dark counterpart'),
      )
    } finally {
      spy.mockRestore()
    }
  })
})
