import { beforeEach, describe, expect, it, vi } from 'vitest'

import { applyClientAnimationMode,
	 applyThemeTokens,
	 buildThemeTokens,
	 CLIENT_SETTINGS_STORAGE_KEY,
	 DARK_THEME_TOKENS,
	 DEFAULT_POST_LIST_ORDER,
	 getClientListLimit,
	 getClientListOrder,
	 getClientThemeMode,
	 LIGHT_THEME_TOKENS,
	 loadClientSettings,
	 setClientListSettings,
	 setClientThemeAppearance } from '@/lib/settings'

import type { FetchPostsOrder } from '@/types'

const installMatchMedia = (matches: boolean) => {
  Object.defineProperty (window, 'matchMedia', {
      configurable: true,
      value: vi.fn ().mockImplementation (() => ({
	  matches,
	  addEventListener: vi.fn (),
	  removeEventListener: vi.fn () })) })
}

describe ('settings', () => {
  beforeEach (() => {
    localStorage.clear ()
    document.documentElement.removeAttribute ('data-animation')
    document.documentElement.removeAttribute ('style')
    document.documentElement.className = ''
    installMatchMedia (false)
  })

  it ('stores list settings under the shared client settings key', () => {
    setClientListSettings ('postSearch', {
	limit: 50,
	order: 'title:asc' })

    expect (getClientListLimit ('postSearch')).toBe (50)
    expect (getClientListOrder<FetchPostsOrder> ('postSearch')).toBe ('title:asc')
    expect (JSON.parse (localStorage.getItem (CLIENT_SETTINGS_STORAGE_KEY) ?? '{}'))
	.toMatchObject ({ lists: { postSearch: { limit: 50, order: 'title:asc' } } })
  })

  it ('ignores invalid list limits and keeps stored order strings', () => {
    localStorage.setItem (
	CLIENT_SETTINGS_STORAGE_KEY,
	JSON.stringify ({ lists: { postList: { limit: 30, order: DEFAULT_POST_LIST_ORDER } } }))

    expect (getClientListLimit ('postList')).toBeNull ()
    expect (getClientListOrder<FetchPostsOrder> ('postList')).toBe (
	DEFAULT_POST_LIST_ORDER)
  })

  it ('stores theme mode and light/dark slot selections in client settings', () => {
    setClientThemeAppearance ({
	themeMode: 'dark',
	activeLightThemeSlotNo: 2,
	activeDarkThemeSlotNo: 3 })

    expect (getClientThemeMode ()).toBe ('dark')
    expect (loadClientSettings ().appearance).toMatchObject ({
	themeMode: 'dark',
	activeLightThemeSlotNo: 2,
	activeDarkThemeSlotNo: 3 })
  })

  it ('applies animation mode to the document dataset', () => {
    applyClientAnimationMode ('off')
    expect (document.documentElement.dataset.animation).toBe ('off')

    applyClientAnimationMode ('reduced')
    expect (document.documentElement.dataset.animation).toBe ('reduced')

    applyClientAnimationMode ('normal')
    expect (document.documentElement.dataset.animation).toBeUndefined ()
  })

  it ('normalises TopNav menu link tokens to complete CSS colours', () => {
    const tokens = buildThemeTokens ('light', {
	link: '221.2 83.2% 53.3%',
	topNavMenuLink: '221.2 83.2% 53.3%' })

    expect (tokens.link).toBe ('221.2 83.2% 53.3%')
    expect (tokens.topNavMenuLink).toBe ('hsl(221.2 83.2% 53.3%)')
  })

  it ('derives TopNav colours without mixing mobile active backgrounds', () => {
    const tokens = buildThemeTokens ('light', { })

    expect (tokens.topNavMobileActiveBackground).toBe (
	LIGHT_THEME_TOKENS.topNavRootBackgroundDesktop)
    expect (tokens.topNavMobileActiveBackground).not.toBe (
	tokens.topNavRootBackgroundMobile)
    expect (buildThemeTokens ('dark', { }).topNavMobileActiveBackground).toBe (
	DARK_THEME_TOKENS.topNavActiveBackground)
  })

  it ('sets shadcn tokens and TopNav tokens with their different value formats', () => {
    const tokens = buildThemeTokens ('dark', { link: '213.1 93.9% 67.8%' })

    applyThemeTokens (tokens)

    expect (document.documentElement.style.getPropertyValue ('--theme-link')).toBe (
	'213.1 93.9% 67.8%')
    expect (document.documentElement.style.getPropertyValue ('--top-nav-menu-link')).toBe (
	'hsl(213.1 93.9% 67.8%)')
  })
})
