import { describe, expect, it, vi } from 'vitest'
import { compositePaintsForTheme, hexToRgb, rgbToHex } from '../scripts/lib/composite-paints.mjs'

const tokenValues: Record<string, Record<string, string>> = {
  dark: {
    '--brand-primary': '#299f45',
  },
  light: {
    '--brand-primary': '#299f45',
  },
}

function paintHexForTheme(paint: { hex?: string; token?: { cssVar?: string } }, theme: string) {
  const cssVar = paint.token?.cssVar
  return (cssVar ? tokenValues[theme]?.[cssVar] : null) ?? paint.hex?.toLowerCase() ?? null
}

describe('compositePaintsForTheme', () => {
  it('returns null for an empty paint stack', () => {
    expect(compositePaintsForTheme([], 'dark', { paintHexForTheme })).toBeNull()
  })

  it('resolves a single token-bound solid paint through the injected theme resolver', () => {
    expect(
      compositePaintsForTheme(
        [{ type: 'SOLID', visible: true, opacity: 1, token: { cssVar: '--brand-primary' } }],
        'dark',
        { paintHexForTheme },
      ),
    ).toBe('#299f45')
  })

  it('returns a single raw solid hex paint', () => {
    expect(
      compositePaintsForTheme([{ type: 'SOLID', visible: true, opacity: 1, hex: '#299F45' }], 'dark', {
        paintHexForTheme,
      }),
    ).toBe('#299f45')
  })

  it('composites brand plus white at 70 percent opacity bottom-up', () => {
    expect(
      compositePaintsForTheme(
        [
          { type: 'SOLID', visible: true, opacity: 1, hex: '#299f45' },
          { type: 'SOLID', visible: true, opacity: 0.7, hex: '#ffffff' },
        ],
        'dark',
        { paintHexForTheme },
      ),
    ).toBe('#bfe2c7')
  })

  it('returns null when all paints are hidden', () => {
    expect(
      compositePaintsForTheme(
        [
          { type: 'SOLID', visible: false, opacity: 1, hex: '#299f45' },
          { type: 'SOLID', visible: false, opacity: 1, hex: '#ffffff' },
        ],
        'dark',
        { paintHexForTheme },
      ),
    ).toBeNull()
  })

  it('composites three layers in order from bottom to top', () => {
    expect(
      compositePaintsForTheme(
        [
          { type: 'SOLID', visible: true, opacity: 1, hex: '#000000' },
          { type: 'SOLID', visible: true, opacity: 0.5, hex: '#ffffff' },
          { type: 'SOLID', visible: true, opacity: 0.25, hex: '#ff0000' },
        ],
        'dark',
        { paintHexForTheme },
      ),
    ).toBe('#9f6060')
  })

  it('warns and skips non-solid paints, falling back when no solid layer remains', () => {
    const warn = vi.fn()

    expect(
      compositePaintsForTheme(
        [
          { type: 'GRADIENT_LINEAR', visible: true, opacity: 1, hex: '#123456' },
          { type: 'SOLID', visible: true, opacity: 1, hex: '#299f45' },
        ],
        'dark',
        { paintHexForTheme, warn },
      ),
    ).toBe('#299f45')
    expect(warn).toHaveBeenCalledWith(expect.stringContaining('unsupported paint type "GRADIENT_LINEAR"'))

    warn.mockClear()
    expect(
      compositePaintsForTheme([{ type: 'GRADIENT_LINEAR', visible: true, opacity: 1, hex: '#123456' }], 'dark', {
        paintHexForTheme,
        warn,
      }),
    ).toBe('#123456')
    expect(warn).toHaveBeenCalledWith(expect.stringContaining('unsupported paint type "GRADIENT_LINEAR"'))
  })

  it('warns and falls back to normal alpha compositing for unsupported blend modes', () => {
    const warn = vi.fn()

    expect(
      compositePaintsForTheme([{ type: 'SOLID', visible: true, opacity: 1, blendMode: 'MULTIPLY', hex: '#299f45' }], 'dark', {
        paintHexForTheme,
        warn,
      }),
    ).toBe('#299f45')
    expect(warn).toHaveBeenCalledWith(expect.stringContaining('unsupported blendMode "MULTIPLY"'))
  })
})

describe('paint color helpers', () => {
  it('converts hex and rgb values with clamping', () => {
    expect(hexToRgb('#bfe2c7')).toEqual({ r: 191, g: 226, b: 199 })
    expect(rgbToHex({ r: 190.8, g: 226.2, b: 199.2 })).toBe('#bfe2c7')
  })
})
