/**
 * 视觉测试通用辅助
 *
 * 提供视口切换、主题切换、页面就绪等待以及动态元素遮罩能力,
 * 用于消除视觉快照中的误报。
 */
import type { Locator, Page } from "@playwright/test"
import { VIEWPORTS, type ThemeName, type ViewportSize } from "../visual.config"

/**
 * 设置视口尺寸
 * @param page Playwright Page 实例
 * @param size 视口标识 desktop | tablet | mobile
 */
export async function setViewport(page: Page, size: ViewportSize): Promise<void> {
  const viewport = VIEWPORTS[size]
  await page.setViewportSize({ width: viewport.width, height: viewport.height })
}

/**
 * 设置主题
 *
 * 项目使用 next-themes(attribute="class"),通过在 <html> 上切换 class 实现。
 * 这里直接操作 localStorage 与 DOM,避免依赖主题切换 UI。
 *
 * @param page Playwright Page 实例
 * @param theme 主题 light | dark
 */
export async function setTheme(page: Page, theme: ThemeName): Promise<void> {
  // next-themes 默认将主题持久化在 localStorage 的 "theme" key
  await page.addInitScript((themeValue) => {
    try {
      window.localStorage.setItem("theme", themeValue)
    } catch {
      // 忽略 localStorage 不可用的情况
    }
  }, theme)
  // 若页面已加载,同步切换 DOM class
  const htmlClass = await page.evaluate(() => document.documentElement.className)
  const nextClass = theme === "dark" ? `${htmlClass} dark` : htmlClass.replace(/\bdark\b/g, "").trim()
  await page.evaluate((cls) => {
    document.documentElement.className = cls
  }, nextClass)
}

/**
 * 等待页面就绪
 *
 * 等待 networkidle、字体加载以及主内容渲染完成,
 * 确保快照稳定。
 */
export async function waitForPageReady(page: Page): Promise<void> {
  await page.waitForLoadState("networkidle")
  // 等待字体加载完成,避免文字位移
  await page.evaluate(() => document.fonts.ready)
  // 给 React hydration 一点缓冲
  await page.waitForTimeout(300)
}

/**
 * 默认需要遮罩的动态元素选择器
 * - 时间戳
 * - 用户头像/用户名
 * - 实时数据
 */
const DEFAULT_DYNAMIC_SELECTORS = [
  "[data-testid='timestamp']",
  "[data-testid='current-time']",
  "[data-testid='user-avatar']",
  "[data-testid='user-name']",
  "time",
  "[data-visual-dynamic]",
]

/**
 * 遮罩动态元素
 *
 * 将指定选择器匹配的元素用纯色块覆盖,
 * 避免时间戳、用户名等动态内容导致快照误报。
 *
 * @param page Playwright Page 实例
 * @param selectors 额外需要遮罩的选择器(会与默认列表合并)
 */
export async function maskDynamicElements(page: Page, selectors: string[] = []): Promise<Locator[]> {
  const allSelectors = [...new Set([...DEFAULT_DYNAMIC_SELECTORS, ...selectors])]
  const masks: Locator[] = []
  for (const selector of allSelectors) {
    const locator = page.locator(selector)
    const count = await locator.count()
    if (count > 0) {
      masks.push(locator)
    }
  }
  return masks
}

/**
 * 生成 toHaveScreenshot 的 mask 选项
 *
 * 配合 maskDynamicElements 使用,将动态元素从对比中遮罩。
 */
export function buildMaskOption(masks: Locator[]) {
  return masks.length > 0 ? { mask: masks } : {}
}
