import { colorToRgbChannels } from './color'
import { cloneThemeSettings, themeSettings, THEME_SETTINGS_VERSION } from './settings'
import { KRA_MAIN_VERSION, PRESET_FALLBACK_MIN_VERSION } from './version'
import { themeVars } from './vars'
import { defu, isObject, liteClone } from 'arcdash'

export const THEME_SETTINGS_STORAGE_KEY = 'kra-theme-settings'

// 颜色统一小写：项目内部所有颜色字符串以小写为单一形态，避免默认值「#2264f2」
// 与预设「#2264f2」因大小写不等而出现选中态丢失。任何外部数据进入运行态前都过一遍。
export const normalizeColor = (value) =>
  typeof value === 'string' ? value.toLowerCase() : value

// 把一份 settings 里的主题色字段统一小写（就地改写后返回同一对象）。
const normalizeThemeColors = (settings) => {
  settings.themeColor = normalizeColor(settings.themeColor)
  if (isObject(settings.otherColor)) {
    Object.keys(settings.otherColor).forEach((key) => {
      settings.otherColor[key] = normalizeColor(settings.otherColor[key])
    })
  }
  return settings
}

// 把 source 深合并进 target（就地，供 themeStore 直接改写响应式 settings）。
// target / source 都来自完整 schema（默认值或 normalizeThemeSettings 输出），
// 未知字段的过滤已在 normalize 阶段由 pickKnownKeys 完成，这里无需再做白名单。
export const mergeThemeSettings = (target, source) => {
  Object.entries(source || {}).forEach(([key, value]) => {
    if (isObject(value) && isObject(target[key])) {
      mergeThemeSettings(target[key], value)
      return
    }
    target[key] = value
  })
}

// 递归按默认 schema 过滤未知字段：schema 中不存在的键一律丢弃，
// 避免外部数据污染运行态、拖累 adapter 扩展。
const pickKnownKeys = (source, schema) => {
  if (!isObject(source) || !isObject(schema)) return source

  const result = {}
  Object.keys(schema).forEach((key) => {
    if (key in source) result[key] = pickKnownKeys(source[key], schema[key])
  })
  return result
}

const isThemeSettingsLike = (source) =>
  isObject(source) && Boolean(
    source.themeScheme ||
    source.themeColor ||
    source.otherColor ||
    source.layout ||
    source.page ||
    source.header ||
    source.tab ||
    source.menu ||
    source.card ||
    source.watermark ||
    source.tokens
  )

/**
 * 把任意外部主题数据（后端 originSetting、导入预设的内层 theme、本地缓存）规整成
 * 一份完整、可信的 settings。兼容策略（大版本，不迁移旧值，只做防御式回落）：
 * - `{ version, settings }` 信封：版本不等于当前版本 → 回落默认值；否则取 settings 继续。
 * - 旧的扁平主题数据（不含新结构字段）→ isThemeSettingsLike 为 false → 回落默认值。
 * - 合法的新结构 → 先按 schema 过滤未知键（pickKnownKeys），再用 defu 覆盖到默认值之上
 *   （source 优先，缺失 / undefined 自动回落默认；未知键已被剔除，不会进入运行态）。
 */
// 值级迁移：老版本把「深色」当成一种菜单风格（menu.theme==='dark'）。新版本里深色是与风格
// 正交的独立开关（menu.darkSider），这里把老值翻译过去，保住老用户外观。
const migrateLegacyDarkMenu = (settings) => {
  if (settings?.menu?.theme === 'dark') {
    settings.menu.theme = 'design'
    settings.menu.darkSider = true
  }
  return settings
}

export const normalizeThemeSettings = (input) => {
  // 带 version 字段的信封：只接受当前大版本，否则视为不兼容数据，回落默认。
  if (isObject(input) && 'version' in input && input.version !== THEME_SETTINGS_VERSION) {
    return cloneThemeSettings()
  }

  const source = input?.theme || input?.settings || input

  if (!isThemeSettingsLike(source)) return cloneThemeSettings()

  return migrateLegacyDarkMenu(
    normalizeThemeColors(defu(pickKnownKeys(source, themeSettings), cloneThemeSettings()))
  )
}

export const normalizeThemePreset = (preset) => {
  if (!preset) return null

  const source = preset.theme || preset.settings
  if (!source) return null

  // 丢弃旧预设里的 ElConf/elConf 与原始 settings 键，统一输出规范化的内层 theme。
  const rest = { ...preset }
  delete rest.theme
  delete rest.settings
  delete rest.ElConf
  delete rest.elConf

  return {
    ...rest,
    // version 是预设自身的发布迭代号，沿用作者声明的值，缺省记为 1。
    version: preset.version ?? 1,
    // minMainVersion 才是与 KRA 主题生态的兼容判据；旧预设未声明时兜底为始终兼容。
    minMainVersion: preset.minMainVersion ?? PRESET_FALLBACK_MIN_VERSION,
    theme: normalizeThemeSettings(source)
  }
}

export const buildThemePreset = (settings, name) => ({
  // 用户导出当前配置 = 一个全新预设，发布号从 1 起；
  // minMainVersion 记为导出时的运行版本，即「能用该预设的最低 KRA 版本」。
  version: 1,
  minMainVersion: KRA_MAIN_VERSION,
  name,
  builtin: false,
  theme: liteClone(settings)
})

/** 主题持久化信封：本地缓存与后端 originSetting 共用同一形态，便于版本校验。 */
export const buildThemeEnvelope = (settings) => ({
  version: THEME_SETTINGS_VERSION,
  settings: liteClone(settings)
})

export const readCachedThemeSettings = () => {
  try {
    const raw = localStorage.getItem(THEME_SETTINGS_STORAGE_KEY)
    if (!raw) return null

    const parsed = JSON.parse(raw)
    // 严格按版本信封读取：版本不匹配（含旧的无版本缓存）一律视为失效，交由调用方回落默认。
    if (parsed?.version !== THEME_SETTINGS_VERSION) return null

    return normalizeThemeSettings(parsed.settings)
  } catch {
    return null
  }
}

export const cacheThemeSettings = (settings) => {
  localStorage.setItem(
    THEME_SETTINGS_STORAGE_KEY,
    JSON.stringify(buildThemeEnvelope(settings))
  )
}

export const clearCachedThemeSettings = () => {
  localStorage.removeItem(THEME_SETTINGS_STORAGE_KEY)
}

const getCssVarByTokens = (tokens) => {
  const styles = []

  Object.entries(themeVars).forEach(([group, vars]) => {
    Object.entries(vars).forEach(([tokenKey, tokenValue]) => {
      let cssVarKey = tokenValue.replace('var(', '').replace(')', '')
      let cssValue = tokens[group]?.[tokenKey]

      if (cssValue === undefined) return

      if (group === 'colors') {
        cssVarKey = cssVarKey.replace('rgb(', '').replace(')', '')
        cssValue = colorToRgbChannels(cssValue)
      }

      styles.push(`${cssVarKey}: ${cssValue}`)
    })
  })

  return styles.join(';')
}

export const addThemeVarsToGlobal = (tokens, darkTokens) => {
  const css = `:root { ${getCssVarByTokens(tokens)} }`
  const darkCss = `html.dark { ${getCssVarByTokens(darkTokens)} }`
  // 作用域暗色：浅色主题下给侧栏容器加 .kra-sider-dark，即让其子树的语义 token 翻成暗色。
  // 与 html.dark 共用同一套 darkTokens，保证「浅色 + 深色侧栏」与「全局暗色侧栏」观感一致。
  const scopedDarkCss = `.kra-sider-dark { ${getCssVarByTokens(darkTokens)} }`
  const styleId = 'theme-vars'
  const style = document.querySelector(`#${styleId}`) || document.createElement('style')

  style.id = styleId
  style.textContent = css + darkCss + scopedDarkCss
  document.head.appendChild(style)
}

export const toggleCssDarkMode = (isDark) => {
  document.documentElement.classList.toggle('dark', isDark)
  document.documentElement.classList.toggle('light', !isDark)
}

export const toggleAuxiliaryColorModes = (grayscale, colourWeakness) => {
  document.documentElement.classList.toggle('html-grey', grayscale)
  document.documentElement.classList.toggle('html-weakenss', colourWeakness)
}
