/**
 * 正则脚本执行引擎（Regex Engine）
 *
 * 对应 ST 中 extensions/regex/engine.js 的前端执行逻辑。
 * 从后端加载的 RegexScript 列表，在前端对消息文本执行正则替换。
 *
 * placement 枚举（与后端 model/app/regex_script.go 保持一致）：
 *   0 = USER_INPUT   — 对用户输入执行
 *   1 = AI_OUTPUT    — 对 AI 输出执行
 *   2 = WORLD_INFO   — 世界书注入（前端暂不处理）
 *   3 = DISPLAY      — 仅展示层（前端暂不处理）
 *
 * 注意：本模块是纯函数，不持有任何状态，调用方负责传入脚本列表。
 */

import type { RegexScript } from '../api/regex'

export const REGEX_PLACEMENT = {
  USER_INPUT: 0,
  AI_OUTPUT: 1,
  WORLD_INFO: 2,
  DISPLAY: 3,
} as const

export type RegexPlacement = (typeof REGEX_PLACEMENT)[keyof typeof REGEX_PLACEMENT]

// ============= 核心函数 =============

/**
 * 对文本执行所有符合条件的正则脚本
 *
 * @param text       原始文本
 * @param placement  执行阶段（USER_INPUT | AI_OUTPUT）
 * @param scripts    正则脚本列表（应已过滤出 enabled 的）
 * @param depth      消息深度（用于 minDepth/maxDepth 过滤，可选）
 * @returns 处理后的文本
 */
export function applyRegexScripts(
  text: string,
  placement: RegexPlacement,
  scripts: RegexScript[],
  depth?: number
): string {
  if (!text || scripts.length === 0) return text

  let result = text

  // 按 order 字段排序（升序）
  const sorted = [...scripts].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))

  for (const script of sorted) {
    // 跳过禁用的脚本
    if (script.disabled) continue

    // 检查 placement
    if (!matchesPlacement(script, placement)) continue

    // promptOnly=true 表示只在构建 prompt 时执行（前端显示层不执行）
    if (script.promptOnly) continue

    // 检查深度（仅在 depth 已知时）
    if (typeof depth === 'number') {
      if (
        script.minDepth !== undefined &&
        script.minDepth !== null &&
        script.minDepth >= 0 &&
        depth < script.minDepth
      ) {
        continue
      }
      if (
        script.maxDepth !== undefined &&
        script.maxDepth !== null &&
        script.maxDepth >= 0 &&
        depth > script.maxDepth
      ) {
        continue
      }
    }

    result = runSingleScript(script, result)
  }

  return result
}

/**
 * 执行单条正则脚本
 */
export function runSingleScript(script: RegexScript, text: string): string {
  if (!script.findRegex || !text) return text

  let findPattern = script.findRegex

  // substituteRegex: 1=RAW 替换宏，2=ESCAPED 转义后替换（简化处理：暂跳过宏替换）
  // TODO: 若需要支持宏变量替换可在此扩展

  const regex = buildRegex(findPattern)
  if (!regex) return text

  const replaceWith = script.replaceWith ?? ''

  try {
    let result = text.replace(regex, (match, ...args) => {
      // 支持 $0 / {{match}} / $1 $2... 捕获组
      // 注意：必须用 function 替换（而非 string），防止 match 或 group 中含有 $n/$& 等被
      // String.replace() 解读为反向引用，导致替换内容污染。
      let replacement = replaceWith
        // ST 兼容：{{match}} / $0 → 整体匹配
        .replace(/\{\{match\}\}/gi, () => match)
        .replace(/\$0/g, () => match)

      // 替换捕获组 $1 $2...
      const groups = args.slice(0, args.length - 3) // 去掉 offset、input 和 namedGroups（ES2018+）
      groups.forEach((group, i) => {
        const safeGroup = group ?? ''
        replacement = replacement.replace(
          new RegExp(`\\$${i + 1}`, 'g'),
          () => safeGroup  // 用 function 防止 safeGroup 中的 $n 被二次解释
        )
      })

      // trimStrings 处理
      if (script.trimStrings && script.trimStrings.length > 0) {
        for (const trim of script.trimStrings) {
          if (trim) replacement = replacement.split(trim).join('')
        }
      }

      return replacement
    })

    return result
  } catch (err) {
    console.warn(`[regexEngine] 脚本 "${script.name}" 执行失败:`, err)
    return text
  }
}

// ============= 辅助函数 =============

/**
 * 解析正则字符串（支持 /pattern/flags 格式和普通字符串）
 */
function buildRegex(pattern: string): RegExp | null {
  try {
    // 检测 /pattern/flags 格式
    const slashMatch = pattern.match(/^\/(.+)\/([gimsuy]*)$/)
    if (slashMatch) {
      return new RegExp(slashMatch[1], slashMatch[2])
    }
    // 普通字符串，默认 global
    return new RegExp(pattern, 'g')
  } catch (err) {
    console.warn(`[regexEngine] 无效正则: "${pattern}"`, err)
    return null
  }
}

/**
 * 判断脚本是否应该在指定 placement 执行
 * 后端脚本：placement 是单个数字
 * 角色卡内置脚本（经过 ChatArea 转换后）：也是单个数字（已做 ST→我们的映射）
 */
function matchesPlacement(script: RegexScript, placement: RegexPlacement): boolean {
  // 如果 placement 存储的是数组（ST 原始格式），检查是否包含目标值
  const p = script.placement as unknown
  if (Array.isArray(p)) {
    // ST placement 数组 [2] → 我们的 1 (ai_output)
    // 但经过 ChatArea.cardRegexScripts 转换后应该已经是单数字了；
    // 这里保留作为防御性处理
    return p.includes(placement === 1 ? 2 : placement === 0 ? 1 : placement)
  }
  return script.placement === placement
}

// ============= 便捷包装 =============

/** 对 AI 输出文本执行正则脚本（最常用） */
export function processAIOutput(text: string, scripts: RegexScript[], depth?: number): string {
  return applyRegexScripts(text, REGEX_PLACEMENT.AI_OUTPUT, scripts, depth)
}

/** 对用户输入文本执行正则脚本 */
export function processUserInput(text: string, scripts: RegexScript[], depth?: number): string {
  return applyRegexScripts(text, REGEX_PLACEMENT.USER_INPUT, scripts, depth)
}
