/**
 * 应用全局状态管理
 *
 * 基于 Pinia 的应用级 Store，管理全局配置、侧边栏、
 * 暗黑模式、缓存页面等应用维度的状态。
 *
 * @module stores/app
 */

import { defineStore } from 'pinia'
import { ref, computed, watch } from 'vue'
import type { GlobalConfig } from '@/config/types'

// ══════════════════════════════════════════════
// 常量
// ══════════════════════════════════════════════

/** 暗黑模式 localStorage 键名 */
const DARK_MODE_KEY = 'app-dark-mode'

/** 侧边栏状态 localStorage 键名 */
const SIDEBAR_KEY = 'app-sidebar-collapsed'

// ══════════════════════════════════════════════
// 内部工具函数
// ══════════════════════════════════════════════

/** 从 localStorage 读取布尔值 */
function readBoolFromStorage(key: string, fallback: boolean): boolean {
  try {
    const stored = localStorage.getItem(key)
    if (stored === null) return fallback
    return stored === 'true'
  } catch {
    return fallback
  }
}

/** 将布尔值写入 localStorage */
function writeBoolToStorage(key: string, value: boolean): void {
  try {
    localStorage.setItem(key, String(value))
  } catch {
    // localStorage 不可用时静默失败
  }
}

// ══════════════════════════════════════════════
// 类型定义
// ══════════════════════════════════════════════

/** 侧边栏状态 */
export interface SidebarState {
  /** 是否折叠 */
  collapsed: boolean
  /** 是否在移动端展开（不考虑折叠状态） */
  opened: boolean
}

/** 应用尺寸类型 */
export type DeviceSize = 'mobile' | 'tablet' | 'desktop'

/**
 * 应用全局 Store
 *
 * 管理应用的全局状态，包括：
 * - globalConfig：运行时全局配置
 * - sidebar：侧边栏折叠/展开状态（持久化）
 * - darkMode：暗黑模式开关（持久化）
 * - cachedViews：需要缓存的页面视图名称列表
 * - device：当前设备类型
 */
export const useAppStore = defineStore('app', () => {
  // ══════════════════════════════════════════════
  // State
  // ══════════════════════════════════════════════

  /**
   * 运行时全局配置
   *
   * 应用启动时从 window.globalConfig 读取初始值，
   * 后续可通过 loadGlobalConfig 从远程接口更新。
   */
  const globalConfig = ref<GlobalConfig | null>(
    typeof window !== 'undefined' ? window.globalConfig ?? null : null,
  )

  /**
   * 侧边栏状态
   *
   * collapsed 会持久化到 localStorage，刷新后保持。
   */
  const sidebar = ref<SidebarState>({
    collapsed: readBoolFromStorage(SIDEBAR_KEY, false),
    opened: false,
  })

  /**
   * 暗黑模式
   *
   * 切换时自动在 <html> 元素上添加/移除 'dark' 类名，
   * 并持久化到 localStorage。
   */
  const darkMode = ref<boolean>(readBoolFromStorage(DARK_MODE_KEY, false))

  /**
   * 缓存的视图组件名称列表
   *
   * 用于 <keep-alive> 的 include 属性，
   * 存储需要保持状态不销毁的路由组件 name。
   */
  const cachedViews = ref<string[]>([])

  /**
   * 当前设备类型
   *
   * 根据窗口宽度自动判断。
   */
  const device = ref<DeviceSize>(getDeviceSize())

  // ══════════════════════════════════════════════
  // 暗黑模式初始化
  // ══════════════════════════════════════════════

  // 监听 darkMode 变化，同步到 DOM 和 localStorage
  watch(darkMode, (val: boolean) => {
    writeBoolToStorage(DARK_MODE_KEY, val)
    if (typeof document !== 'undefined') {
      document.documentElement.classList.toggle('dark', val)
    }
  }, { immediate: true })

  // ══════════════════════════════════════════════
  // Getters
  // ══════════════════════════════════════════════

  /**
   * 当前是否为移动端设备
   * @returns 设备宽度 < 768px 返回 true
   */
  const isMobile = computed<boolean>(() => device.value === 'mobile')

  /**
   * 当前是否为桌面端设备
   * @returns 设备宽度 >= 992px 返回 true
   */
  const isDesktop = computed<boolean>(() => device.value === 'desktop')

  /**
   * 当前是否为平板设备
   * @returns 设备宽度在 768px ~ 991px 返回 true
   */
  const isTablet = computed<boolean>(() => device.value === 'tablet')

  // ══════════════════════════════════════════════
  // Actions
  // ══════════════════════════════════════════════

  /**
   * 设置全局配置
   *
   * 可用于应用启动时从远程接口加载配置后合并更新。
   *
   * @param config - 完整的全局配置对象或部分配置
   */
  function setGlobalConfig(config: Partial<GlobalConfig>): void {
    if (globalConfig.value) {
      Object.assign(globalConfig.value, config)
    } else {
      globalConfig.value = config as GlobalConfig
    }
  }

  /**
   * 切换侧边栏折叠状态
   *
   * 桌面端切换 collapsed 状态，移动端切换 opened 状态。
   */
  function toggleSidebar(): void {
    if (isMobile.value) {
      sidebar.value.opened = !sidebar.value.opened
    } else {
      sidebar.value.collapsed = !sidebar.value.collapsed
      writeBoolToStorage(SIDEBAR_KEY, sidebar.value.collapsed)
    }
  }

  /**
   * 关闭侧边栏
   *
   * 通常用于移动端点击遮罩层或路由跳转后自动关闭。
   */
  function closeSidebar(): void {
    sidebar.value.opened = false
  }

  /**
   * 切换暗黑模式
   *
   * 反转当前的暗黑模式状态，变化会自动同步到
   * localStorage 和 DOM。
   */
  function toggleDarkMode(): void {
    darkMode.value = !darkMode.value
  }

  /**
   * 设置暗黑模式
   *
   * @param value - true 开启暗黑模式，false 关闭
   */
  function setDarkMode(value: boolean): void {
    darkMode.value = value
  }

  /**
   * 添加缓存视图
   *
   * 将指定视图名称加入缓存列表，用于 keep-alive。
   * 重复添加同名视图不会产生副作用。
   *
   * @param viewName - 要缓存的视图组件名称
   */
  function addCachedView(viewName: string): void {
    if (!cachedViews.value.includes(viewName)) {
      cachedViews.value.push(viewName)
    }
  }

  /**
   * 移除缓存视图
   *
   * 将指定视图名称从缓存列表中移除。
   *
   * @param viewName - 要移除缓存的视图组件名称
   */
  function removeCachedView(viewName: string): void {
    const index = cachedViews.value.indexOf(viewName)
    if (index !== -1) {
      cachedViews.value.splice(index, 1)
    }
  }

  /**
   * 清空所有缓存视图
   */
  function clearCachedViews(): void {
    cachedViews.value = []
  }

  /**
   * 更新设备类型
   *
   * 根据当前窗口宽度重新计算设备类型。
   * 通常在窗口 resize 事件中调用。
   */
  function updateDevice(): void {
    device.value = getDeviceSize()
  }

  return {
    // State
    globalConfig,
    sidebar,
    darkMode,
    cachedViews,
    device,
    // Getters
    isMobile,
    isDesktop,
    isTablet,
    // Actions
    setGlobalConfig,
    toggleSidebar,
    closeSidebar,
    toggleDarkMode,
    setDarkMode,
    addCachedView,
    removeCachedView,
    clearCachedViews,
    updateDevice,
  }
})

// ══════════════════════════════════════════════
// 辅助函数
// ══════════════════════════════════════════════

/**
 * 根据窗口宽度判断当前设备类型
 * @returns 设备类型标识
 */
function getDeviceSize(): DeviceSize {
  if (typeof window === 'undefined') return 'desktop'
  const width = window.innerWidth
  if (width < 768) return 'mobile'
  if (width < 992) return 'tablet'
  return 'desktop'
}
