// @vitest-environment jsdom
import { describe, it, expect, vi, afterEach } from 'vitest'
import { applyTheme, subscribeToSystemMode } from './apply'
import type { EffectiveTokens } from './types'

const tokens = { fg: '#111111', bg: '#fefefe', 'radius-sm': '0.125rem' } as unknown as EffectiveTokens

afterEach(() => { vi.restoreAllMocks(); delete (window as { matchMedia?: unknown }).matchMedia })

describe('applyTheme', () => {
  it('sets data-theme/data-mode + each token as a custom property', () => {
    const el = document.createElement('div')
    applyTheme(el, tokens, 'my-theme', 'dark')
    expect(el.getAttribute('data-theme')).toBe('my-theme')
    expect(el.getAttribute('data-mode')).toBe('dark')
    expect(el.style.colorScheme).toBe('dark')
    expect(el.style.getPropertyValue('--mod-color-fg')).toBe('#111111')
    expect(el.style.getPropertyValue('--mod-color-bg')).toBe('#fefefe')
    expect(el.style.getPropertyValue('--mod-radius-sm')).toBe('0.125rem')
  })
  it('is idempotent (re-apply overwrites, does not duplicate)', () => {
    const el = document.createElement('div')
    applyTheme(el, tokens, 'a', 'light')
    applyTheme(el, { ...tokens, fg: '#222222' } as EffectiveTokens, 'a', 'light')
    expect(el.style.colorScheme).toBe('light')
    expect(el.style.getPropertyValue('--mod-color-fg')).toBe('#222222')
  })
})

describe('subscribeToSystemMode', () => {
  it('subscribes to matchMedia and reports OS-preference changes; unsubscribe detaches', () => {
    const listeners: Array<(e: { matches: boolean }) => void> = []
    const addEventListener = vi.fn((_: string, cb: (e: { matches: boolean }) => void) => listeners.push(cb))
    const removeEventListener = vi.fn()
    ;(window as { matchMedia?: unknown }).matchMedia = vi.fn(() => ({
      matches: false, media: '(prefers-color-scheme: dark)',
      addEventListener, removeEventListener,
    }))
    const cb = vi.fn()
    const unsub = subscribeToSystemMode(cb)
    expect(addEventListener).toHaveBeenCalledTimes(1)
    listeners[0]!({ matches: true })
    expect(cb).toHaveBeenCalledWith(true)
    unsub()
    expect(removeEventListener).toHaveBeenCalledTimes(1)
  })
  it('no-ops + returns a noop unsubscribe when matchMedia is absent (SSR)', () => {
    delete (window as { matchMedia?: unknown }).matchMedia
    const cb = vi.fn()
    const unsub = subscribeToSystemMode(cb)
    expect(typeof unsub).toBe('function')
    expect(() => unsub()).not.toThrow()
    expect(cb).not.toHaveBeenCalled()
  })
})
