/**
 * App Store 单元测试
 *
 * 覆盖 useAppStore 的状态管理逻辑：
 * - 初始状态值
 * - 主题切换与持久化
 * - 侧边栏折叠状态
 * - 远程主题配置加载
 */
import { describe, it, expect, vi, beforeEach } from 'vitest'

// --- Mock localStorage ---
const localStorageStore: Record<string, string> = {}

const mockLocalStorage = {
  getItem: vi.fn((key: string) => localStorageStore[key] ?? null),
  setItem: vi.fn((key: string, value: string) => {
    localStorageStore[key] = value
  }),
  removeItem: vi.fn((key: string) => {
    delete localStorageStore[key]
  }),
  clear: vi.fn(() => {
    Object.keys(localStorageStore).forEach((k) => delete localStorageStore[k])
  }),
}

Object.defineProperty(globalThis, 'localStorage', {
  value: mockLocalStorage,
  writable: true,
})

// --- Mock getTheme API ---
const mockGetTheme = vi.fn()
vi.mock('../api/themes', () => ({
  getTheme: (...args: unknown[]) => mockGetTheme(...args),
}))

// --- Mock zustand 内部不依赖真实存储 ---
// 在 mock 生效后导入被测模块
import { useAppStore, THEME_OPTIONS } from './app'
import type { ThemeName } from './app'
import type { ThemeConfig } from '../api/themes'

beforeEach(() => {
  vi.clearAllMocks()
  mockLocalStorage.clear()
  // zustand store 每次需要重新创建状态，我们通过 setState 重置
  useAppStore.setState({
    theme: 'blue',
    sidebarCollapsed: false,
    themeConfig: null,
  })
})

// ============================================================
// THEME_OPTIONS 常量
// ============================================================
describe('THEME_OPTIONS', () => {
  it('应包含 4 个主题选项', () => {
    expect(THEME_OPTIONS).toHaveLength(4)
  })

  it('所有选项包含 key / label / desc / preview', () => {
    for (const opt of THEME_OPTIONS) {
      expect(opt).toHaveProperty('key')
      expect(opt).toHaveProperty('label')
      expect(opt).toHaveProperty('desc')
      expect(opt).toHaveProperty('preview')
      expect(opt.preview).toHaveProperty('primary')
      expect(opt.preview).toHaveProperty('bg')
      expect(opt.preview).toHaveProperty('surface')
    }
  })

  it('key 值互不重复', () => {
    const keys = THEME_OPTIONS.map((t) => t.key)
    expect(new Set(keys).size).toBe(keys.length)
  })
})

// ============================================================
// 初始状态
// ============================================================
describe('初始状态', () => {
  it('sidebarCollapsed 默认为 false', () => {
    expect(useAppStore.getState().sidebarCollapsed).toBe(false)
  })

  it('themeConfig 默认为 null', () => {
    expect(useAppStore.getState().themeConfig).toBeNull()
  })

  it('localStorage 无记录时 theme 默认为 blue', () => {
    expect(useAppStore.getState().theme).toBe('blue')
  })

  it('localStorage 有有效主题时恢复该主题', () => {
    localStorageStore['hms-theme'] = 'dark'
    // 需要重新触发 loadTheme —— 由于 zustand create 只执行一次，
    // 这里通过手动 setTheme 模拟初始化行为
    // 实际验证 loadTheme 函数逻辑：
    // loadTheme 读取 localStorage 并验证值是否在 THEME_OPTIONS 中
    const saved = localStorageStore['hms-theme']
    const isValid = THEME_OPTIONS.some((t) => t.key === saved)
    expect(isValid).toBe(true)
    expect(saved).toBe('dark')
  })
})

// ============================================================
// setTheme — 主题切换与持久化
// ============================================================
describe('setTheme', () => {
  it('应更新 theme 状态', () => {
    useAppStore.getState().setTheme('warm')
    expect(useAppStore.getState().theme).toBe('warm')
  })

  it('应将主题写入 localStorage', () => {
    useAppStore.getState().setTheme('emerald')
    expect(mockLocalStorage.setItem).toHaveBeenCalledWith('hms-theme', 'emerald')
  })

  it('连续切换应反映最终值', () => {
    const { setTheme } = useAppStore.getState()
    setTheme('dark')
    setTheme('warm')
    expect(useAppStore.getState().theme).toBe('warm')
  })

  it('切换到每个有效主题名都应成功', () => {
    const validThemes: ThemeName[] = ['blue', 'warm', 'dark', 'emerald']
    for (const t of validThemes) {
      useAppStore.getState().setTheme(t)
      expect(useAppStore.getState().theme).toBe(t)
    }
  })

  it('localStorage 写入失败时不应抛出异常', () => {
    mockLocalStorage.setItem.mockImplementationOnce(() => {
      throw new Error('QuotaExceededError')
    })
    expect(() => useAppStore.getState().setTheme('dark')).not.toThrow()
    // 状态仍然更新（内存中生效）
    expect(useAppStore.getState().theme).toBe('dark')
  })
})

// ============================================================
// toggleSidebar — 侧边栏折叠切换
// ============================================================
describe('toggleSidebar', () => {
  it('应从 false 切换到 true', () => {
    expect(useAppStore.getState().sidebarCollapsed).toBe(false)
    useAppStore.getState().toggleSidebar()
    expect(useAppStore.getState().sidebarCollapsed).toBe(true)
  })

  it('应从 true 切换回 false', () => {
    useAppStore.setState({ sidebarCollapsed: true })
    useAppStore.getState().toggleSidebar()
    expect(useAppStore.getState().sidebarCollapsed).toBe(false)
  })

  it('连续切换应交替变化', () => {
    const { toggleSidebar } = useAppStore.getState()
    expect(useAppStore.getState().sidebarCollapsed).toBe(false)

    toggleSidebar()
    expect(useAppStore.getState().sidebarCollapsed).toBe(true)

    toggleSidebar()
    expect(useAppStore.getState().sidebarCollapsed).toBe(false)

    toggleSidebar()
    expect(useAppStore.getState().sidebarCollapsed).toBe(true)
  })

  it('不应影响其他状态字段', () => {
    useAppStore.getState().setTheme('warm')
    const themeBefore = useAppStore.getState().theme
    const configBefore = useAppStore.getState().themeConfig

    useAppStore.getState().toggleSidebar()

    expect(useAppStore.getState().theme).toBe(themeBefore)
    expect(useAppStore.getState().themeConfig).toBe(configBefore)
  })
})

// ============================================================
// loadThemeConfig — 远程主题配置加载
// ============================================================
describe('loadThemeConfig', () => {
  const fakeConfig: ThemeConfig = {
    primary_color: '#C4623A',
    logo_url: '/logo.png',
    sidebar_style: 'dark',
    brand_name: '测试机构',
    brand_slogan: '测试标语',
  }

  it('成功时应设置 themeConfig', async () => {
    mockGetTheme.mockResolvedValueOnce(fakeConfig)
    await useAppStore.getState().loadThemeConfig()
    expect(useAppStore.getState().themeConfig).toEqual(fakeConfig)
  })

  it('调用 getTheme API 一次', async () => {
    mockGetTheme.mockResolvedValueOnce(fakeConfig)
    await useAppStore.getState().loadThemeConfig()
    expect(mockGetTheme).toHaveBeenCalledTimes(1)
  })

  it('API 失败时不应修改 themeConfig（保持 null）', async () => {
    mockGetTheme.mockRejectedValueOnce(new Error('Network error'))
    await useAppStore.getState().loadThemeConfig()
    expect(useAppStore.getState().themeConfig).toBeNull()
  })

  it('API 失败时不应抛出异常', async () => {
    mockGetTheme.mockRejectedValueOnce(new Error('Server error'))
    await expect(useAppStore.getState().loadThemeConfig()).resolves.toBeUndefined()
  })

  it('不应影响 theme 和 sidebarCollapsed', async () => {
    useAppStore.getState().setTheme('emerald')
    useAppStore.getState().toggleSidebar()

    mockGetTheme.mockResolvedValueOnce(fakeConfig)
    await useAppStore.getState().loadThemeConfig()

    expect(useAppStore.getState().theme).toBe('emerald')
    expect(useAppStore.getState().sidebarCollapsed).toBe(true)
  })
})

// ============================================================
// 状态隔离 — 多次操作互不干扰
// ============================================================
describe('状态隔离', () => {
  it('主题切换不应影响侧边栏', () => {
    useAppStore.setState({ sidebarCollapsed: true })
    useAppStore.getState().setTheme('dark')
    expect(useAppStore.getState().sidebarCollapsed).toBe(true)
  })

  it('侧边栏切换不应影响主题', () => {
    useAppStore.getState().setTheme('warm')
    useAppStore.getState().toggleSidebar()
    expect(useAppStore.getState().theme).toBe('warm')
  })

  it('加载配置不应重置已有主题', async () => {
    useAppStore.getState().setTheme('emerald')
    mockGetTheme.mockResolvedValueOnce({ primary_color: '#000' })
    await useAppStore.getState().loadThemeConfig()
    expect(useAppStore.getState().theme).toBe('emerald')
  })
})
