/**
 * A11y 审计工具（P6 硬化）
 *
 * - runA11yAudit() - 运行 axe-core 审计（条件加载，动态 import）
 * - checkContrast() - 对比度检查工具
 * - generateA11yReport() - 生成审计报告
 *
 * 注意：axe-core 未安装，使用动态 import 在运行时按需加载。
 * 包将在统一安装阶段补充到 package.json。
 *
 * 关联：02-architecture-design.md §12 可观测性 / WCAG 2.2 AA 规范
 */

/**
 * WCAG 2.2 AA 对比度阈值。
 *
 * - 普通文本：4.5:1
 * - 大文本（18pt+ 或 14pt 粗体）：3.0:1
 * - 非文本组件（UI 边框/图标）：3.0:1
 */
export const CONTRAST_THRESHOLDS = {
  normalText: 4.5,
  largeText: 3.0,
  nonTextComponents: 3.0,
} as const;

/** A11y 审计结果级别 */
export type A11yIssueLevel = "minor" | "moderate" | "serious" | "critical";

/** A11y 审计单个问题 */
export interface A11yIssue {
  /** 规则 ID（如 "color-contrast"） */
  id: string;
  /** 问题级别 */
  level: A11yIssueLevel;
  /** 问题描述 */
  description: string;
  /** 受影响元素的选择器 */
  selector: string;
  /** 修复建议 */
  help: string;
  /** 帮助文档 URL */
  helpUrl: string;
}

/** A11y 审计报告 */
export interface A11yAuditReport {
  /** 审计时间戳 */
  timestamp: number;
  /** 页面 URL */
  url: string;
  /** 通过的规则数 */
  passes: number;
  /** 违规规则数 */
  violations: number;
  /** 不完整规则数 */
  incomplete: number;
  /** 问题列表 */
  issues: A11yIssue[];
  /** 是否通过 WCAG 2.2 AA */
  passed: boolean;
}

/**
 * axe-core 的最小类型声明（仅声明使用的 API 子集）。
 */
interface AxeResult {
  passes: unknown[];
  violations: Array<{
    id: string;
    impact: A11yIssueLevel;
    description: string;
    help: string;
    helpUrl: string;
    nodes: Array<{ target: string[] }>;
  }>;
  incomplete: unknown[];
}

interface AxeModule {
  default: (options: {
    runOnly?: { type: string; values: string[] };
  }) => Promise<AxeResult>;
}

/** axe-core 的最小配置（启用 WCAG 2.2 AA 规则集） */
const AXE_RUN_OPTIONS = {
  runOnly: {
    type: "tag" as const,
    values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"],
  },
};

/**
 * 运行 axe-core A11y 审计。
 *
 * 动态 import axe-core，避免未安装包导致构建失败。
 * 仅在浏览器环境运行（需要 DOM）。
 *
 * @returns 审计结果数组（违规项），加载失败返回空数组
 */
export async function runA11yAudit(): Promise<A11yIssue[]> {
  if (typeof window === "undefined") return [];
  if (typeof document === "undefined") return [];

  try {
    const axeMod = (await import("axe-core")) as unknown as AxeModule;
    const axe = axeMod.default ?? (axeMod as unknown as AxeModule["default"]);
    const result = await axe(AXE_RUN_OPTIONS);

    return result.violations.map((violation) => {
      const node = violation.nodes[0];
      return {
        id: violation.id,
        level: violation.impact,
        description: violation.description,
        selector: node ? node.target.join(", ") : "",
        help: violation.help,
        helpUrl: violation.helpUrl,
      };
    });
  } catch (err) {
    if (typeof console !== "undefined") {
      console.warn("[teacher-portal] axe-core 加载失败，跳过 A11y 审计", err);
    }
    return [];
  }
}

/**
 * 将 hex 颜色转换为相对亮度值（0~1）。
 *
 * 依据 WCAG 2.x 相对亮度公式：
 * https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
 */
function relativeLuminance(hex: string): number | null {
  const cleaned = hex.replace("#", "");
  if (cleaned.length !== 6 && cleaned.length !== 3) return null;

  const fullHex =
    cleaned.length === 3
      ? cleaned
          .split("")
          .map((c) => c + c)
          .join("")
      : cleaned;

  const r = parseInt(fullHex.slice(0, 2), 16) / 255;
  const g = parseInt(fullHex.slice(2, 4), 16) / 255;
  const b = parseInt(fullHex.slice(4, 6), 16) / 255;

  if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null;

  const toLinear = (c: number): number =>
    c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);

  const R = toLinear(r);
  const G = toLinear(g);
  const B = toLinear(b);

  return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}

/**
 * 对比度检查工具。
 *
 * 计算两个 hex 颜色之间的对比度比值（1~21）。
 *
 * @param foreground 前景色（hex，如 "#000000"）
 * @param background 背景色（hex，如 "#ffffff"）
 * @param isLargeText 是否大文本（默认 false，使用 4.5:1 阈值）
 * @returns 是否通过 WCAG 2.2 AA 对比度要求（无效颜色返回 false）
 */
export function checkContrast(
  foreground: string,
  background: string,
  isLargeText = false,
): boolean {
  const fg = relativeLuminance(foreground);
  const bg = relativeLuminance(background);
  if (fg === null || bg === null) return false;

  const lighter = Math.max(fg, bg);
  const darker = Math.min(fg, bg);
  const ratio = (lighter + 0.05) / (darker + 0.05);

  const threshold = isLargeText
    ? CONTRAST_THRESHOLDS.largeText
    : CONTRAST_THRESHOLDS.normalText;

  return ratio >= threshold;
}

/**
 * 获取两个颜色之间的对比度比值。
 *
 * @returns 对比度比值（1~21），无效颜色返回 null
 */
export function getContrastRatio(
  foreground: string,
  background: string,
): number | null {
  const fg = relativeLuminance(foreground);
  const bg = relativeLuminance(background);
  if (fg === null || bg === null) return null;

  const lighter = Math.max(fg, bg);
  const darker = Math.min(fg, bg);
  return (lighter + 0.05) / (darker + 0.05);
}

/**
 * 生成 A11y 审计报告。
 *
 * 运行 axe-core 审计并汇总为结构化报告。
 *
 * @returns 审计报告（加载失败返回空报告）
 */
export async function generateA11yReport(): Promise<A11yAuditReport> {
  if (typeof window === "undefined") {
    return {
      timestamp: Date.now(),
      url: "",
      passes: 0,
      violations: 0,
      incomplete: 0,
      issues: [],
      passed: false,
    };
  }

  try {
    const axeMod = (await import("axe-core")) as unknown as AxeModule;
    const axe = axeMod.default ?? (axeMod as unknown as AxeModule["default"]);
    const result = await axe(AXE_RUN_OPTIONS);

    const issues: A11yIssue[] = result.violations.map((violation) => {
      const node = violation.nodes[0];
      return {
        id: violation.id,
        level: violation.impact,
        description: violation.description,
        selector: node ? node.target.join(", ") : "",
        help: violation.help,
        helpUrl: violation.helpUrl,
      };
    });

    return {
      timestamp: Date.now(),
      url: window.location.href,
      passes: result.passes.length,
      violations: result.violations.length,
      incomplete: result.incomplete.length,
      issues,
      passed: result.violations.length === 0,
    };
  } catch (err) {
    if (typeof console !== "undefined") {
      console.warn("[teacher-portal] A11y 审计失败", err);
    }
    return {
      timestamp: Date.now(),
      url: typeof window !== "undefined" ? window.location.href : "",
      passes: 0,
      violations: 0,
      incomplete: 0,
      issues: [],
      passed: false,
    };
  }
}
