import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useTheme, THEMES } from '@/composables/useTheme'
import { useAuthStore } from '@/stores/auth'

describe('useTheme', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
    document.documentElement.removeAttribute('data-theme')
    document.head.querySelectorAll('meta[name="theme-color"]').forEach(n => n.remove())
    const meta = document.createElement('meta')
    meta.setAttribute('name', 'theme-color')
    meta.setAttribute('content', '#000000')
    document.head.appendChild(meta)
  })

  afterEach(() => {
    document.head.querySelectorAll('meta[name="theme-color"]').forEach(n => n.remove())
    document.documentElement.removeAttribute('data-theme')
  })

  it('exports six themes with id, label, primary, bg, text', () => {
    expect(THEMES).toHaveLength(6)
    for (const t of THEMES) {
      expect(t.id).toBeTypeOf('string')
      expect(t.label).toBeTypeOf('string')
      expect(t.primary).toMatch(/^#[0-9a-f]{6}$/i)
      expect(t.bg).toMatch(/^#[0-9a-f]{6}$/i)
      expect(t.text).toMatch(/^#[0-9a-f]{6}$/i)
    }
  })

  it('applyTheme writes data-theme on <html>', () => {
    const { applyTheme } = useTheme()
    applyTheme('ocean-dusk')
    expect(document.documentElement.dataset.theme).toBe('ocean-dusk')
  })

  it('applyTheme syncs auth.user.theme when a user is signed in', () => {
    const auth = useAuthStore()
    auth.setUser({ id: 1, email: 'a@b', roles: [], theme: 'warm-craft', designVersion: 'v1', timezone: 'UTC' })
    const { applyTheme } = useTheme()
    applyTheme('sage-cream')
    expect(auth.user?.theme).toBe('sage-cream')
  })

  it('applyTheme updates the theme-color meta tag with the theme bg', () => {
    const { applyTheme } = useTheme()
    applyTheme('playful-pop')
    const meta = document.querySelector('meta[name="theme-color"]')
    const bg = THEMES.find(t => t.id === 'playful-pop')!.bg
    expect(meta?.getAttribute('content')).toBe(bg)
  })

  it('applyTheme is a no-op for unknown theme ids on the meta tag', () => {
    const { applyTheme } = useTheme()
    applyTheme('does-not-exist')
    // data-theme still updates (we mirror the input as-is)
    expect(document.documentElement.dataset.theme).toBe('does-not-exist')
    // meta tag is left at its previous value
    const meta = document.querySelector('meta[name="theme-color"]')
    expect(meta?.getAttribute('content')).toBe('#000000')
  })

  it('applyTheme silently skips meta sync when no theme-color meta exists', () => {
    document.head.querySelectorAll('meta[name="theme-color"]').forEach(n => n.remove())
    const { applyTheme } = useTheme()
    expect(() => applyTheme('warm-craft')).not.toThrow()
    expect(document.documentElement.dataset.theme).toBe('warm-craft')
  })

  it('saveTheme applies and PATCHes /api/user/theme on success', async () => {
    const fetchMock = vi.fn().mockResolvedValue({ ok: true })
    vi.stubGlobal('fetch', fetchMock)

    const { saveTheme } = useTheme()
    await saveTheme('honey-slate')

    expect(document.documentElement.dataset.theme).toBe('honey-slate')
    expect(fetchMock).toHaveBeenCalledWith(
      '/api/user/theme',
      expect.objectContaining({
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ theme: 'honey-slate' }),
      }),
    )
    vi.unstubAllGlobals()
  })

  it('saveTheme shows a toast on non-ok response', async () => {
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }))
    const { saveTheme } = useTheme()
    // Spy on the toast store via the module
    const { useToastStore } = await import('@/stores/toast')
    const toast = useToastStore()
    const showSpy = vi.spyOn(toast, 'show')
    await saveTheme('dusty-mauve')
    expect(showSpy).toHaveBeenCalledWith(expect.stringContaining('Could not save'), 'error')
    vi.unstubAllGlobals()
  })

  it('saveTheme shows a toast when fetch rejects', async () => {
    vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('network')))
    const { saveTheme } = useTheme()
    const { useToastStore } = await import('@/stores/toast')
    const toast = useToastStore()
    const showSpy = vi.spyOn(toast, 'show')
    await saveTheme('warm-craft')
    expect(showSpy).toHaveBeenCalled()
    vi.unstubAllGlobals()
  })
})
