import {
  APP_UPNAME,
  APP_LCNAME,
  APP_CONSTS,
  MSG_INJECT_JS,
  MSG_INJECT_CSS,
  OPT_STYLE_FUZZY,
  GLOBLA_RULE,
  DEFAULT_SETTING,
  // DEFAULT_MOUSEHOVER_KEY,
  OPT_STYLE_NONE,
  DEFAULT_API_SETTING,
  OPT_HIGHLIGHT_WORDS_BEFORETRANS,
  OPT_HIGHLIGHT_WORDS_AFTERTRANS,
  OPT_SPLIT_PARAGRAPH_PUNCTUATION,
  OPT_SPLIT_PARAGRAPH_DISABLE,
  OPT_SPLIT_PARAGRAPH_TEXTLENGTH,
} from "../config";
import interpreter from "./interpreter";
import ShadowRootMonitor from "./shadowRootMonitor";
import { clearFetchPool } from "./pool";
import { debounce, scheduleIdle, genEventName, truncateWords } from "./utils";
import { apiTranslate } from "../apis";
import { sendBgMsg } from "./msg";
import { isExt } from "./client";
import { injectInlineJs, injectInternalCss } from "./injector";
import { kissLog } from "./log";
import { clearAllBatchQueue } from "./batchQueue";
import { genTextClass } from "./style";
import { createLoadingSVG } from "./svg";
import { shortcutRegister } from "./shortcut";
import { tryDetectLang } from "./detect";
import { trustedTypesHelper } from "./trustedTypes";

/**
 * @class Translator
 * @description 翻译核心逻辑封装
 */
export class Translator {
  static displayCache = new WeakMap();
  static TAGS = {
    BREAK_LINE: new Set(["BR", "WBR"]),
    BLOCK: new Set([
      "ADDRESS",
      "ARTICLE",
      "ASIDE",
      "BLOCKQUOTE",
      "CANVAS",
      "DD",
      "DIV",
      "DL",
      "DT",
      "FIELDSET",
      "FIGCAPTION",
      "FIGURE",
      "FOOTER",
      "FORM",
      "H1",
      "H2",
      "H3",
      "H4",
      "H5",
      "H6",
      "HEADER",
      "HR",
      "LI",
      "MAIN",
      "NAV",
      "NOSCRIPT",
      "OL",
      "P",
      "PRE",
      "SECTION",
      "TABLE",
      "TFOOT",
      "UL",
      "VIDEO",
    ]),
    INLINE: new Set([
      // "A",
      "ABBR",
      "ACRONYM",
      "B",
      "BDO",
      "BIG",
      "BR",
      "BUTTON",
      "CITE",
      "CODE",
      "DFN",
      "DEL",
      "FONT",
      "EM",
      "I",
      "IMG",
      "INPUT",
      "INS",
      "KBD",
      "LABEL",
      "MAP",
      "MARK",
      "OBJECT",
      "OUTPUT",
      "Q",
      "SAMP",
      "SCRIPT",
      "SELECT",
      "SMALL",
      // "SPAN",
      "STRONG",
      "SUB",
      "SUP",
      "TEXTAREA",
      "TIME",
      "TT",
      "U",
      "VAR",
    ]),
    REPLACE: new Set([
      "ABBR",
      "CODE",
      "DFN",
      "IMG",
      "KBD",
      "OUTPUT",
      "SAMP",
      "SUB",
      "SUP",
      "SVG",
      "TIME",
      "VAR",
    ]),
    WARP: new Set([
      "A",
      "B",
      "BDO",
      "BDI",
      "BIG",
      "CITE",
      "DEL",
      "EM",
      "FONT",
      "I",
      "INS",
      "MARK",
      "Q",
      "S",
      "SMALL",
      "SPAN",
      "STRONG",
      "U",
    ]),
  };

  // 译文相关class
  static KISS_CLASS = {
    warpper: `${APP_LCNAME}-wrapper notranslate`,
    inner: `${APP_LCNAME}-inner`,
    term: `${APP_LCNAME}-term`,
    br: `${APP_LCNAME}-br`,
    highlight: `${APP_LCNAME}-highlight`,
  };

  // 内置跳过翻译文本
  // todo: 验证有效性
  static BUILTIN_SKIP_PATTERNS = [
    // 1. URL (覆盖 http, https, ftp, file 协议)
    /^(?:(?:https?|ftp|file):\/\/|www\.)[^\s/$.?#].[^\s]*$/i,

    // 2. 邮箱地址
    /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,

    // 3. 文件路径 (为 Unix 和 Windows 做了简化)
    /^(?:[a-zA-Z]:\\|\/|\\)(?:[\w\-. ]+\/|[\w\-. ]+\\)*[\w\-. ]*\.?[\w\-. ]*$/,

    // 4. UUID (通用唯一标识符)
    /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/,

    // 5. 纯数字字符串 (整数, 浮点数, 包含常见分隔符)
    // 同时也处理单位 (如 px, %, em, rem 等) 和货币符号。
    /^[$\u00A2-\u00A5\u20A0-\u20CF]?\s?-?\d{1,3}(?:[.,]\d{3})*(?:[.,]\d+)?\s?(?:px|%|em|rem|pt|vw|vh|deg|s|ms)?$/,

    // 6. 版本号 (例如 v1.2.3, 10.0.1)
    /^v?\d+(\.\d+){1,3}$/,

    // 7. ISO 8601 日期/时间格式
    /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/,

    // 8. 模板占位符 (例如 {{var}}, ${var}, __VAR__)
    /^({{[^}]+}}|\${[^}]+}|__\w+__|%\w+)$/,

    // 9. CSS 选择器 (简单的 class/ID) 和十六进制颜色值
    /^(?:\.|#)[\w-]+$|^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,

    // 10. 用户名 (例如 @username, @user.name, @user-name) - [已修改]
    /^@[\w.-]+$/,

    // 11. HTML 实体
    /^&\w+;$/,

    // 12. 中括号包裹的序号 (例如 [1], [99])
    /^\[\d+\]$/,

    // 13. 简单时间格式 (例如 12:30, 9:45:30) - [新增]
    /^\d{1,2}:\d{2}(:\d{2})?$/,

    // 14. 包含常见扩展名的文件名 (例如: document.pdf, image.jpeg)
    /^[^\s\\/:]+?\.[a-zA-Z0-9]{2,5}$/,

    // todo: 数字和特殊字符组成的字符串
  ];

  static DEFAULT_OPTIONS = DEFAULT_SETTING; // 默认配置
  static DEFAULT_RULE = GLOBLA_RULE; // 默认规则

  static isElementOrFragment(el) {
    return el instanceof Element || el instanceof DocumentFragment;
  }

  // 判断是否块级元素
  static isBlockNode(el) {
    if (!Translator.isElementOrFragment(el)) return false;

    if (Translator.TAGS.INLINE.has(el.nodeName)) return false;
    if (Translator.TAGS.BLOCK.has(el.nodeName)) return true;
    if (el.attributes?.display?.value?.includes("inline")) return false;

    if (Translator.displayCache.has(el)) {
      return Translator.displayCache.get(el);
    }

    const isBlock = !window.getComputedStyle(el).display.startsWith("inline");
    Translator.displayCache.set(el, isBlock);
    return isBlock;
  }

  // 判断是否包含块级子元素
  static hasBlockNode(el) {
    if (!Translator.isElementOrFragment(el)) return false;
    for (const child of el.childNodes) {
      if (Translator.isBlockNode(child)) {
        return true;
      }
    }
    return false;
  }

  // 判断是否直接包含非空文本节点
  static hasTextNode(el) {
    if (!Translator.isElementOrFragment(el)) return false;
    for (const child of el.childNodes) {
      if (child.nodeType === Node.TEXT_NODE && /\S/.test(child.nodeValue)) {
        return true;
      }
    }
    return false;
  }

  // 特殊字符转义
  static escapeRegex(str) {
    return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  }

  // 内置忽略元素
  static BUILTIN_IGNORE_SELECTOR = `address, area, audio, br, canvas, 
  data, datalist, embed, head, iframe, input, noscript, map, 
  object, option, param, picture, progress, 
  select, script, style, track, textarea, template, 
  video, wbr, .notranslate, [contenteditable], [translate='no'], 
  ${APP_LCNAME}, #${APP_CONSTS.fabID}, #${APP_CONSTS.boxID}, 
  .${APP_CONSTS.fabID}_warpper, .${APP_CONSTS.boxID}_warpper`;

  #setting; // 设置选项
  #rule; // 规则
  #isInitialized = false; // 初始化状态
  #isJsInjected = false; // 注入用户JS
  #mouseHoverEnabled = false; // 鼠标悬停翻译
  #enabled = false; // 全局默认状态
  #runId = 0; // 用于中止过期的异步请求
  #termValues = []; // 按顺序存储术语的替换值
  #combinedTermsRegex; // 专业术语正则表达式
  #combinedSkipsRegex; // 跳过文本正则表达式
  #placeholderRegex; // 恢复htnml正则表达式
  #translationTagName = APP_UPNAME; // 翻译容器的标签名
  #eventName = ""; // 通信事件名称
  #docInfo = {}; // 网页信息
  #glossary = {}; // AI词典
  #textClass = {}; // 译文样式class
  #textSheet = ""; // 译文样式字典
  #apisMap = new Map(); // 用于接口快速查找
  #favWords = []; // 收藏词汇

  #observedNodes = new WeakSet(); // 存储所有被识别出的、可翻译的 DOM 节点单元
  #translationNodes = new WeakMap(); // 存储所有插入到页面的译文节点
  #viewNodes = new Set(); // 当前在可视范围内的单元
  #processedNodes = new WeakMap(); // 已处理（已执行翻译DOM操作）的单元
  #rootNodes = new Set(); // 已监控的根节点
  #skipMoNodes = new WeakSet(); // 忽略变化的节点

  #removeKeydownHandler; // 快捷键清理函数
  #hoveredNode = null; // 存储当前悬停的可翻译节点
  #boundMouseMoveHandler; // 鼠标事件
  #boundKeyDownHandler; // 键盘事件

  #io; // IntersectionObserver
  #mo; // MutationObserver
  #dmm; // DebounceMouseMover
  #srm; // ShadowRootMonitor

  #rescanQueue = new Set(); // “脏容器”队列
  #isQueueProcessing = false; // 队列处理状态标志

  // 忽略元素
  get #ignoreSelector() {
    return `${Translator.BUILTIN_IGNORE_SELECTOR}, ${this.#rule.ignoreSelector}`;
  }

  // 接口参数
  // todo: 不用频繁查找计算
  get #apiSetting() {
    // return (
    //   this.#setting.transApis.find(
    //     (api) => api.apiSlug === this.#rule.apiSlug
    //   ) || DEFAULT_API_SETTING
    // );
    return this.#apisMap.get(this.#rule.apiSlug) || DEFAULT_API_SETTING;
  }

  // 占位符
  get #placeholder() {
    const [startDelimiter, endDelimiter] =
      this.#apiSetting.placeholder.split(" ");
    return {
      startDelimiter,
      endDelimiter,
      tagName: this.#apiSetting.placetag,
    };
  }

  constructor({ rule = {}, setting = {}, favWords = [] }) {
    this.#setting = { ...Translator.DEFAULT_OPTIONS, ...setting };
    this.#rule = { ...Translator.DEFAULT_RULE, ...rule };
    this.#favWords = favWords;
    this.#apisMap = new Map(
      this.#setting.transApis.map((api) => [api.apiSlug, api])
    );

    this.#eventName = genEventName();
    this.#docInfo = {
      title: document.title,
      description: this.#getDocDescription(),
    };
    this.#combinedSkipsRegex = new RegExp(
      Translator.BUILTIN_SKIP_PATTERNS.map((r) => `(${r.source})`).join("|")
    );
    this.#placeholderRegex = this.#createPlaceholderRegex();
    this.#parseTerms(this.#rule.terms);
    this.#parseAITerms(this.#rule.aiTerms);
    this.#createTextStyles();

    this.#boundMouseMoveHandler = this.#handleMouseMove.bind(this);
    this.#boundKeyDownHandler = this.#handleKeyDown.bind(this);

    this.#io = this.#createIntersectionObserver();
    this.#mo = this.#createMutationObserver();
    this.#dmm = this.#createDebounceMouseMover();
    this.#srm = this.#createShadowRootMonitor();

    // 监控shadowroot
    if (this.#rule.hasShadowroot === "true") {
      this.#srm.start();
    }

    // 鼠标悬停翻译
    if (this.#setting.mouseHoverSetting.useMouseHover) {
      this.#enableMouseHover();
    }

    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", () => this.#run());
    } else {
      this.#run();
    }
  }

  // 启动
  #run() {
    if (this.#rule.transOpen === "true") {
      this.enable();
    } else if (this.#setting.preInit) {
      this.#init();
    }
  }

  // 初始化
  #init() {
    this.#isInitialized = true;

    // 注入JS/CSS
    this.#initInjector();

    // 查找根节点并扫描
    document
      .querySelectorAll(this.#rule.rootsSelector || "body")
      .forEach((root) => {
        this.#startObserveRoot(root);
      });

    // 查找现有的所有shadowroot
    if (this.#rule.hasShadowroot === "true") {
      try {
        this.#findAllShadowRoots().forEach((shadowRoot) => {
          this.#startObserveShadowRoot(shadowRoot);
        });
      } catch (err) {
        kissLog("findAllShadowRoots", err);
      }
    }
  }

  #createPlaceholderRegex() {
    const escapedStart = Translator.escapeRegex(
      this.#placeholder.startDelimiter
    );
    const escapedEnd = Translator.escapeRegex(this.#placeholder.endDelimiter);
    const patternString = `(${escapedStart}\\d+${escapedEnd}|<\\/?\\w+\\d+>)`;
    const flags = "g";
    return new RegExp(patternString, flags);
  }

  // 创建样式
  #createTextStyles() {
    const [textClass, textStyles] = genTextClass({ ...this.#rule });
    const textSheet = new CSSStyleSheet();
    textSheet.replaceSync(textStyles);
    this.#textClass = textClass;
    this.#textSheet = textSheet;
  }

  // 注入样式
  #injectSheet(shadowRoot) {
    if (!shadowRoot.adoptedStyleSheets.includes(this.#textSheet)) {
      shadowRoot.adoptedStyleSheets = [
        ...shadowRoot.adoptedStyleSheets,
        this.#textSheet,
      ];
    }
  }

  // 解析专业术语字符串
  #parseTerms(termsString) {
    this.#termValues = [];
    this.#combinedTermsRegex = null;

    if (!termsString || typeof termsString !== "string") return;

    const termPatterns = [];
    const lines = termsString.split(/\n|;/); // 按换行或分号分割

    for (const line of lines) {
      const trimmedLine = line.trim();
      if (!trimmedLine) continue;

      let lastCommaIndex = trimmedLine.lastIndexOf(",");
      if (lastCommaIndex === -1) {
        lastCommaIndex = trimmedLine.length;
      }
      const key = trimmedLine.substring(0, lastCommaIndex).trim();
      const value = trimmedLine.substring(lastCommaIndex + 1).trim();

      if (key) {
        try {
          new RegExp(key);
          termPatterns.push(`(${key})`);
          this.#termValues.push(value);
        } catch (err) {
          kissLog(`Invalid RegExp for term: "${key}"`, err);
        }
      }
    }

    if (termPatterns.length > 0) {
      this.#combinedTermsRegex = new RegExp(termPatterns.join("|"), "g");
    }
  }

  #parseAITerms(termsString) {
    if (!termsString || typeof termsString !== "string") return;

    try {
      this.#glossary = Object.fromEntries(
        termsString
          .split(/\n|;/)
          .map((line) => {
            const [k = "", v = ""] = line.split(",").map((s) => s.trim());
            return [k, v];
          })
          .filter(([k]) => k)
      );
    } catch (err) {
      kissLog("parse aiterms", err);
    }
  }

  // todo: 利用AI总结
  #getDocDescription() {
    try {
      const meta = document.querySelector('meta[name="description"]');
      const description = meta?.getAttribute("content") || "";
      return truncateWords(description);
    } catch (err) {
      kissLog("get description", err);
    }
    return "";
  }

  // 监控翻译单元的可见性
  #createIntersectionObserver() {
    const pending = new Set();
    const flush = debounce(() => {
      pending.forEach((node) => this.#performSyncNode(node));
      pending.clear();
    }, this.#setting.transInterval);

    return new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            this.#viewNodes.add(entry.target);
            pending.add(entry.target);
            flush();
          } else {
            this.#viewNodes.delete(entry.target);
          }
        });
      },
      { threshold: 0.01 }
    );
  }

  // 监控页面动态变化
  #createMutationObserver() {
    return new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        if (
          this.#skipMoNodes.has(mutation.target) ||
          mutation.nextSibling?.tagName === this.#translationTagName
        ) {
          continue;
        }

        if (mutation.type === "characterData") {
          if (
            mutation.oldValue !== mutation.target.nodeValue &&
            !this.#combinedSkipsRegex.test(mutation.target.nodeValue)
          ) {
            this.#queueForRescan(mutation.target.parentElement);
          }
        } else if (mutation.type === "childList") {
          let nodes = new Set();
          let hasText = false;
          mutation.addedNodes.forEach((node) => {
            if (
              this.#skipMoNodes.has(node) ||
              node.nodeName === this.#translationTagName
            ) {
              return;
            }

            if (node.nodeType === Node.TEXT_NODE) {
              hasText = true;
            } else if (Translator.isElementOrFragment(node)) {
              nodes.add(node);
            }
          });
          if (hasText) {
            this.#queueForRescan(mutation.target);
          } else {
            nodes.forEach((node) => this.#queueForRescan(node));
          }
        }
      }
    });
  }

  // 节流的鼠标悬停事件
  #createDebounceMouseMover() {
    return debounce((targetNode) => {
      const startNode = targetNode;
      let foundNode = null;
      while (targetNode && targetNode !== document.body) {
        if (this.#observedNodes.has(targetNode)) {
          foundNode = targetNode;
          break;
        }
        targetNode = targetNode.parentElement;
      }
      this.#hoveredNode = foundNode || startNode;

      const { mouseHoverKey } = this.#setting.mouseHoverSetting;
      if (mouseHoverKey.length === 0 && !this.#isInitialized) {
        this.#init();
      }
      if (mouseHoverKey.length === 0 && foundNode) {
        this.#processNode(foundNode);
      }
    }, 100);
  }

  // 创建shadowroot的回调
  #createShadowRootMonitor() {
    return new ShadowRootMonitor((shadowRoot) => {
      this.#startObserveShadowRoot(shadowRoot);
    });
  }

  // 跟踪鼠标下的可翻译节点
  #handleMouseMove(event) {
    let targetNode = event.composedPath()[0];
    this.#dmm(targetNode);
  }

  // 快捷键按下时的处理器
  #handleKeyDown() {
    if (!this.#isInitialized) {
      this.#init();
    }
    let targetNode = this.#hoveredNode;
    if (!targetNode || !this.#observedNodes.has(targetNode)) return;

    this.#toggleTargetNode(targetNode);
  }

  // 切换节点翻译状态
  #toggleTargetNode(targetNode) {
    if (this.#processedNodes.has(targetNode)) {
      this.#cleanupDirectTranslations(targetNode);
    } else {
      this.#processNode(targetNode);
    }
  }

  // 找页面所有 ShadowRoot
  #findAllShadowRoots(root = document.body, results = new Set()) {
    // const start = performance.now();
    try {
      const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
      while (walker.nextNode()) {
        const node = walker.currentNode;
        if (node.shadowRoot) {
          results.add(node.shadowRoot);
          this.#findAllShadowRoots(node.shadowRoot, results);
        }
      }
    } catch (err) {
      kissLog("无法访问某个 shadowRoot", err);
    }
    // const end = performance.now();
    // const duration = end - start;
    // console.log(`findAllShadowRoots 耗时：${duration} 毫秒`);
    return results;
  }

  // 向上查找发生变化的块级元素
  #findChangeContainer(startNode) {
    if (
      !Translator.isElementOrFragment(startNode) ||
      startNode.closest?.(this.#ignoreSelector)
    ) {
      return null;
    }

    let current = startNode;
    while (current && current !== document.body) {
      if (Translator.isBlockNode(current) || this.#observedNodes.has(current)) {
        // 确保找到的容器在我们监控的根节点内
        for (const root of this.#rootNodes) {
          if (root.contains(current)) {
            return current;
          }
        }
      }
      current = current.parentElement;
    }

    return null;
  }

  // “脏容器”队列
  #queueForRescan(target) {
    this.#rescanQueue.add(target);
    if (!this.#isQueueProcessing) {
      this.#isQueueProcessing = true;
      scheduleIdle(() => {
        this.#rescanQueue.forEach((t) => this.#rescanContainer(t));
        this.#rescanQueue.clear();
        this.#isQueueProcessing = false;
      }, 100);
    }
  }

  // 处理“脏容器”
  #rescanContainer(changedNode) {
    const container = this.#findChangeContainer(changedNode);
    if (!container) return;

    this.#cleanupAllTranslations(container);
    this.#scanNode(container);
  }

  // 重新观察
  #reIO(node) {
    this.#io.unobserve(node);
    this.#io.observe(node);
  }

  // 重新观察可视范围内全部节点
  #reIOViewNodes() {
    this.#viewNodes.forEach((n) => this.#reIO(n));
  }

  // 监控shadowroot
  #startObserveShadowRoot(shadowRoot) {
    if (shadowRoot.host.matches(`#${APP_CONSTS.fabID}, #${APP_CONSTS.boxID}`)) {
      return;
    }
    this.#startObserveRoot(shadowRoot);
    this.#injectSheet(shadowRoot);
  }

  // 监控根节点
  #startObserveRoot(root) {
    if (this.#rootNodes.has(root)) return;
    this.#rootNodes.add(root);
    this.#mo.observe(root, {
      childList: true,
      subtree: true,
      characterData: true,
      characterDataOldValue: true,
    });
    this.#scanNode(root);
  }

  // 开始/重新监控节点
  #startObserveNode(node) {
    if (this.#rule.highlightWords === OPT_HIGHLIGHT_WORDS_BEFORETRANS) {
      this.#highlightWordsDeeply(node);
    }

    if (
      !this.#observedNodes.has(node) &&
      this.#enabled &&
      this.#setting.transAllnow
    ) {
      this.#observedNodes.add(node);
      this.#processNode(node);
      return;
    }

    // 未监控
    if (!this.#observedNodes.has(node)) {
      this.#observedNodes.add(node);
      this.#io.observe(node);
      return;
    }

    // 已监控，但未处理状态，且在可视范围
    if (!this.#processedNodes.has(node) && this.#viewNodes.has(node)) {
      this.#reIO(node);
    }
  }

  // 非自动识别文本模式下，快速查询目标节点
  #queryNode(rootNode) {
    // root 也可能是目标节点
    if (rootNode.matches?.(this.#rule.selector)) {
      this.#startObserveNode(rootNode);
    }

    rootNode.querySelectorAll(this.#rule.selector).forEach((node) => {
      if (!node.closest?.(this.#ignoreSelector)) {
        this.#startObserveNode(node);
      }
    });
  }

  // 寻找需要被监控的文本节点
  #scanNode(rootNode) {
    if (
      !Translator.isElementOrFragment(rootNode) ||
      // rootNode.matches?.(this.#rule.keepSelector) ||
      rootNode.matches?.(this.#ignoreSelector)
    ) {
      return;
    }

    if (this.#rule.autoScan === "false") {
      this.#queryNode(rootNode);
      return;
    }

    const hasText = Translator.hasTextNode(rootNode);

    if (!hasText && rootNode.children.length === 1) {
      this.#scanNode(rootNode.children[0]);
      return;
    }

    const hasBlock = Translator.hasBlockNode(rootNode);

    if (hasText || !hasBlock) {
      this.#startObserveNode(rootNode);
    }

    if (hasBlock) {
      for (const child of rootNode.children) {
        const isBlock = Translator.isBlockNode(child);
        if (!hasText || isBlock) {
          this.#scanNode(child);
        }
      }
    }
  }

  // 处理一个待翻译的节点
  async #processNode(node) {
    if (
      this.#processedNodes.has(node) ||
      !Translator.isElementOrFragment(node)
    ) {
      return;
    }

    this.#processedNodes.set(node, { ...this.#rule });

    // 提前检测文本
    if (this.#isInvalidText(node.textContent)) {
      return;
    }

    // 提前进行语言检测
    let deLang = "";
    const {
      fromLang = "auto",
      toLang,
      splitParagraph = OPT_SPLIT_PARAGRAPH_DISABLE,
      splitLength = 100,
    } = this.#rule;
    const { langDetector, skipLangs = [] } = this.#setting;
    if (fromLang === "auto") {
      deLang = await tryDetectLang(node.textContent, langDetector);
      if (
        deLang &&
        (toLang.slice(0, 2) === deLang.slice(0, 2) ||
          skipLangs.includes(deLang))
      ) {
        // 保留处理状态，不做删除
        // this.#processedNodes.delete(node);
        return;
      }
    }

    // 切分长段落
    if (splitParagraph !== OPT_SPLIT_PARAGRAPH_DISABLE) {
      this.#splitTextNodesBySentence(node, splitParagraph, splitLength);
    }

    let nodeGroup = [];
    [...node.childNodes].forEach((child) => {
      const shouldBreak = this.#shouldBreak(child);
      const shouldGroup =
        child.nodeType === Node.ELEMENT_NODE ||
        child.nodeType === Node.TEXT_NODE;
      if (!shouldBreak && shouldGroup) {
        nodeGroup.push(child);
      } else if (shouldBreak && nodeGroup.length) {
        this.#translateNodeGroup(nodeGroup, node, deLang);
        nodeGroup = [];
      }
    });

    if (nodeGroup.length) {
      this.#translateNodeGroup(nodeGroup, node, deLang);
    }
  }

  // 高亮词汇
  #highlightTextNode(textNode, wordRegex) {
    if (textNode.parentNode?.nodeName.toLowerCase() === "b") {
      return;
    }

    if (!wordRegex.test(textNode.textContent)) {
      return;
    }

    wordRegex.lastIndex = 0;
    const fragments = textNode.textContent.split(wordRegex);
    const newNodes = [];

    fragments.forEach((fragment, i) => {
      if (!fragment) return;

      if (i % 2 === 1) {
        // 奇数索引是匹配到的关键词
        const bTag = document.createElement("b");
        bTag.className = Translator.KISS_CLASS.highlight;
        bTag.style.cssText = this.#rule.highlightStyle || "";
        bTag.textContent = fragment;
        this.#skipMoNodes.add(bTag);
        newNodes.push(bTag);
      } else {
        // 偶数索引是普通文本
        const newTextNode = document.createTextNode(fragment);
        this.#skipMoNodes.add(newTextNode);
        newNodes.push(newTextNode);
      }
    });

    if (newNodes.length > 0) {
      textNode.replaceWith(...newNodes);
    }
  }

  // 高亮词汇
  #highlightWordsDeeply(parentNode) {
    if (!parentNode || this.#favWords.length === 0) {
      return;
    }

    const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const escapedWords = this.#favWords.map(escapeRegex);
    const wordRegex = new RegExp(`\\b(${escapedWords.join("|")})\\b`, "gi");

    if (parentNode.nodeType === Node.ELEMENT_NODE) {
      const walker = document.createTreeWalker(
        parentNode,
        NodeFilter.SHOW_TEXT,
        null,
        false
      );

      const nodesToProcess = [];
      let node;
      while ((node = walker.nextNode())) {
        nodesToProcess.push(node);
      }

      nodesToProcess.forEach((textNode) => {
        this.#highlightTextNode(textNode, wordRegex);
      });
    } else if (parentNode.nodeType === Node.TEXT_NODE) {
      this.#highlightTextNode(parentNode, wordRegex);
    }
  }

  // 切分文本段落
  #splitTextNodesBySentence(parentNode, splitParagraph, splitLength) {
    const sentenceEndRegexForSplit = /[。！？]+|[.?!]+(?=\s+|$)/g;

    [...parentNode.childNodes].forEach((node) => {
      if (node.nodeType !== Node.TEXT_NODE || node.textContent.trim() === "") {
        return;
      }

      const text = node.textContent;
      const parts = [];
      let lastIndex = 0;
      let match;

      while ((match = sentenceEndRegexForSplit.exec(text)) !== null) {
        let realEndIndex = match.index + match[0].length;
        while (realEndIndex < text.length && /\s/.test(text[realEndIndex])) {
          realEndIndex++;
        }
        parts.push(text.substring(lastIndex, realEndIndex));
        lastIndex = realEndIndex;
        sentenceEndRegexForSplit.lastIndex = realEndIndex;
      }
      if (lastIndex < text.length) {
        parts.push(text.substring(lastIndex));
      }

      const validParts = parts.filter((part) => part.trim().length > 0);
      if (validParts.length <= 1) {
        return;
      }

      const newNodes = validParts.map((part) => {
        const newNode = document.createTextNode(part);
        this.#skipMoNodes.add(newNode);
        return newNode;
      });

      node.replaceWith(...newNodes);
    });

    const sentenceEndRegexForTest = /(?:[。！？?!]+|(?<!\d)\.)\s*$/;
    let textLength = 0;

    [...parentNode.childNodes].forEach((node) => {
      textLength += node.textContent.length;

      const isSentenceEnd = sentenceEndRegexForTest.test(node.textContent);
      if (!isSentenceEnd || node.nextSibling?.nodeName === "BR") {
        return;
      }

      if (
        splitParagraph === OPT_SPLIT_PARAGRAPH_PUNCTUATION ||
        (splitParagraph === OPT_SPLIT_PARAGRAPH_TEXTLENGTH &&
          textLength >= splitLength)
      ) {
        textLength = 0;

        const br = document.createElement("br");
        br.className = Translator.KISS_CLASS.br;
        this.#skipMoNodes.add(br);

        node.after(br);
      }
    });
  }

  // 清除高亮
  #removeHighlights(parentNode) {
    if (!parentNode) return;

    const highlightedElements = parentNode.querySelectorAll(
      `.${Translator.KISS_CLASS.highlight}`
    );

    highlightedElements.forEach((element) => {
      const textNode = document.createTextNode(element.textContent);
      element.replaceWith(textNode);
    });

    parentNode.normalize();
  }

  // 移除br
  #removeBrTags(parentNode) {
    if (!parentNode) return;

    parentNode
      .querySelectorAll(`.${Translator.KISS_CLASS.br}`)
      .forEach((br) => br.remove());

    parentNode.normalize();
  }

  // 判断是否需要换行
  #shouldBreak(node) {
    if (!Translator.isElementOrFragment(node)) return false;
    if (node.matches(this.#rule.keepSelector)) return false;

    if (
      Translator.TAGS.BREAK_LINE.has(node.nodeName) ||
      node.matches?.(this.#ignoreSelector) ||
      node.nodeName === this.#translationTagName
    ) {
      return true;
    }

    if (this.#rule.autoScan && Translator.isBlockNode(node)) {
      return true;
    }

    if (
      !this.#rule.autoScan &&
      (node.matches(this.#rule.selector) ||
        node.querySelector(this.#rule.selector))
    ) {
      return true;
    }

    return false;
  }

  // 过滤文本
  #isInvalidText(text) {
    if (typeof text !== "string") {
      return true;
    }

    const trimmedText = text.trim();

    // 文本长度
    if (
      trimmedText.length < this.#setting.minLength ||
      trimmedText.length > this.#setting.maxLength
    ) {
      return true;
    }

    // 单个非字母数字字符。
    if (trimmedText.length === 1 && !trimmedText.match(/[a-zA-Z]/)) {
      return true;
    }

    // 只是一个数字
    if (!isNaN(parseFloat(trimmedText)) && isFinite(trimmedText)) {
      return true;
    }

    // 正则匹配
    if (this.#combinedSkipsRegex.test(trimmedText)) {
      return true;
    }

    return false;
  }

  // 翻译内联节点
  async #translateNodeGroup(nodes, hostNode, deLang) {
    const {
      transTag,
      textStyle,
      transStartHook,
      transEndHook,
      transOnly,
      termsStyle,
      selectStyle,
      parentStyle,
      grandStyle,
      // detectRemote,
      // toLang,
      // skipLangs = [],
      highlightWords,
    } = this.#rule;
    const {
      newlineLength,
      // langDetector，
    } = this.#setting;
    const parentNode = hostNode.parentElement;
    const hideOrigin = transOnly === "true";

    // 翻译开始钩子函数
    if (transStartHook?.trim()) {
      try {
        interpreter.run(`exports.transStartHook = ${transStartHook}`);
        interpreter.exports.transStartHook({
          hostNode,
          parentNode,
          nodes,
        });
      } catch (err) {
        kissLog("transStartHook", err);
      }
    }

    try {
      const [processedString, placeholderMap] = this.#serializeForTranslation(
        nodes,
        termsStyle
      );
      // console.log("processedString", processedString);
      if (this.#isInvalidText(processedString)) return;

      const wrapper = document.createElement(this.#translationTagName);
      wrapper.className = Translator.KISS_CLASS.warpper;

      if (processedString.length > newlineLength) {
        const br = document.createElement("br");
        br.hidden = hideOrigin;
        wrapper.appendChild(br);
      }

      const inner = document.createElement(transTag);
      inner.className = `${Translator.KISS_CLASS.inner} ${this.#textClass[textStyle]}`;
      inner.appendChild(createLoadingSVG());
      wrapper.appendChild(inner);
      nodes[nodes.length - 1].after(wrapper);

      const currentRunId = this.#runId;
      const [translatedText, isSameLang] = await this.#translateFetch(
        processedString,
        deLang
      );
      if (this.#runId !== currentRunId) {
        throw new Error("Request terminated");
      }

      if (!translatedText || isSameLang) {
        wrapper.remove();
        return;
      }

      const htmlString = this.#restoreFromTranslation(
        translatedText,
        placeholderMap
      );
      const trustedHTML = trustedTypesHelper.createHTML(htmlString);

      // const parser = new DOMParser();
      // const doc = parser.parseFromString(trustedHTML, "text/html");
      // const innerElement = doc.body.firstChild;
      // inner.replaceChildren(innerElement);

      inner.innerHTML = trustedHTML;

      this.#translationNodes.set(wrapper, {
        nodes,
        isHide: hideOrigin,
      });
      if (hideOrigin) {
        this.#removeNodes(nodes);
      }

      // 附加样式
      if (selectStyle && hostNode.style) {
        hostNode.style.cssText += selectStyle;
      }
      if (parentStyle && parentNode && parentNode.style) {
        parentNode.style.cssText += parentStyle;
      }
      if (grandStyle && parentNode && parentNode.parentElement) {
        parentNode.parentElement.style.cssText += grandStyle;
      }

      // 高亮词汇
      if (highlightWords === OPT_HIGHLIGHT_WORDS_AFTERTRANS) {
        nodes.forEach((node) => this.#highlightWordsDeeply(node));
      }

      // 翻译完成钩子函数
      if (transEndHook?.trim()) {
        try {
          interpreter.run(`exports.transEndHook = ${transEndHook}`);
          interpreter.exports.transEndHook({
            hostNode,
            parentNode,
            nodes,
            wrapperNode: wrapper,
            innerNode: inner,
          });
        } catch (err) {
          kissLog("transEndHook", err);
        }
      }
    } catch (err) {
      // inner.textContent = `[失败]...`;
      // todo: 失败重试按钮
      kissLog("translate group error: ", err.message);
      this.#cleanupDirectTranslations(hostNode);
    }
  }

  // 处理节点转为翻译字符串
  #serializeForTranslation(nodes, termsStyle) {
    let replaceCounter = 0; // {{n}}
    let wrapCounter = 0; // <tagn>
    const placeholderMap = new Map();
    const { startDelimiter, endDelimiter } = this.#placeholder;

    const pushReplace = (html) => {
      replaceCounter++;
      const placeholder = `${startDelimiter}${replaceCounter}${endDelimiter}`;
      placeholderMap.set(placeholder, html);
      return placeholder;
    };

    const traverse = (node) => {
      if (
        node.nodeType !== Node.ELEMENT_NODE &&
        node.nodeType !== Node.TEXT_NODE
      ) {
        return "";
      }

      // 文本节点
      if (node.nodeType === Node.TEXT_NODE) {
        let text = node.textContent;

        // 专业术语替换
        if (this.#combinedTermsRegex) {
          this.#combinedTermsRegex.lastIndex = 0;
          text = text.replace(this.#combinedTermsRegex, (...args) => {
            const groups = args.slice(1, -2);
            const matchedIndex = groups.findIndex(
              (group) => group !== undefined
            );
            const fullMatch = args[0];
            const termValue = this.#termValues[matchedIndex];

            return pushReplace(
              `<i class="${Translator.KISS_CLASS.term}" style="${termsStyle}">${termValue || fullMatch}</i>`
            );
          });
        }

        return text;
      }

      // 元素节点
      if (node.nodeType === Node.ELEMENT_NODE) {
        if (
          (this.#rule.hasRichText === "true" &&
            Translator.TAGS.REPLACE.has(node.tagName)) ||
          node.matches(this.#rule.keepSelector) ||
          node.matches(this.#ignoreSelector) ||
          !node.textContent.trim()
        ) {
          if (node.tagName === "IMG" || node.tagName === "SVG") {
            node.style.width = `${node.offsetWidth}px`;
            node.style.height = `${node.offsetHeight}px`;
          }
          return pushReplace(node.outerHTML);
        }

        let innerContent = "";
        node.childNodes.forEach((child) => {
          innerContent += traverse(child);
        });

        if (
          this.#rule.hasRichText === "true" &&
          Translator.TAGS.WARP.has(node.tagName)
        ) {
          wrapCounter++;
          const startPlaceholder = `<${this.#placeholder.tagName}${wrapCounter}>`;
          const endPlaceholder = `</${this.#placeholder.tagName}${wrapCounter}>`;
          placeholderMap.set(startPlaceholder, buildOpeningTag(node));
          placeholderMap.set(endPlaceholder, `</${node.localName}>`);
          return `${startPlaceholder}${innerContent}${endPlaceholder}`;
        }

        return innerContent;
      }

      return "";
    };

    function buildOpeningTag(node) {
      const escapeAttr = (str) => str.replace(/"/g, "&quot;");
      let tag = `<${node.tagName.toLowerCase()}`;
      for (const attr of node.attributes) {
        tag += ` ${attr.name}="${escapeAttr(attr.value)}"`;
      }
      tag += ">";
      return tag;
    }

    const processedString = nodes.map(traverse).join("").trim();

    return [processedString, placeholderMap];
  }

  // 组装恢复html字符串
  #restoreFromTranslation(translatedText, placeholderMap) {
    if (!placeholderMap.size) {
      return translatedText;
    }

    if (!translatedText) return "";

    return translatedText.replace(
      this.#placeholderRegex,
      (match) => placeholderMap.get(match) || match
    );
  }

  // 发起翻译请求
  #translateFetch(text, deLang = "") {
    const { fromLang, toLang } = this.#rule;

    return apiTranslate({
      text,
      fromLang: deLang || fromLang,
      toLang,
      apiSetting: this.#apiSetting,
      docInfo: this.#docInfo,
      glossary: this.#glossary,
    });
  }

  // 查找指定节点下所有译文节点
  #findTranslationWrappers(parentNode) {
    return parentNode.querySelectorAll(`:scope > ${APP_LCNAME}`);
  }

  // 清理所有插入的译文dom
  #cleanupAllNodes() {
    this.#rootNodes.forEach((root) => this.#cleanupAllTranslations(root));
  }

  // 清理节点下面所有译文dom
  #cleanupAllTranslations(root) {
    root
      .querySelectorAll(APP_LCNAME)
      .forEach((el) => this.#removeTranslationElement(el));
  }

  // 清理子节点译文dom
  #cleanupDirectTranslations(node) {
    this.#findTranslationWrappers(node).forEach((el) => {
      this.#removeTranslationElement(el);
    });
  }

  // 清理译文
  #removeTranslationElement(el) {
    const parentElement = el.parentElement;
    this.#processedNodes.delete(parentElement);

    // 如果是仅显示译文模式，先恢复原文
    const { nodes, isHide } = this.#translationNodes.get(el) || {};
    if (isHide) {
      this.#restoreOriginal(el, nodes);
    }

    this.#translationNodes.delete(el);
    el.remove();

    // todo: 可能不应深度清除
    if (this.#rule.highlightWords === OPT_HIGHLIGHT_WORDS_AFTERTRANS) {
      this.#removeHighlights(parentElement);
    }
    this.#removeBrTags(parentElement);
  }

  // 恢复原文
  #restoreOriginal(el, nodes) {
    if (nodes) {
      const frag = document.createDocumentFragment();
      nodes.forEach((n) => frag.appendChild(n));
      const parent = el.parentElement;
      parent?.insertBefore(frag, el);
    }
  }

  // 移除多个节点
  #removeNodes(nodes) {
    if (nodes) {
      const frag = document.createDocumentFragment();
      nodes.forEach((n) => frag.appendChild(n));
    }
  }

  // 切换译文和双语显示
  #toggleTranslationOnly(node, transOnly) {
    this.#findTranslationWrappers(node).forEach((el) => {
      const br = el.querySelector(":scope > br");
      const { nodes } = this.#translationNodes.get(el) || {};
      if (transOnly === "true") {
        // 双语变为仅译文
        if (br) br.hidden = true;
        this.#removeNodes(nodes);
        this.#translationNodes.set(el, { nodes, isHide: true });
      } else {
        // 仅译文变为双语
        if (br) br.hidden = false;
        this.#restoreOriginal(el, nodes);
        this.#translationNodes.set(el, { nodes, isHide: false });
      }
    });
  }

  // 更新样式
  #updateStyle(node, oldStyle, newStyle) {
    this.#findTranslationWrappers(node).forEach((el) => {
      const inner = el.querySelector(
        `:scope > .${Translator.KISS_CLASS.inner}`
      );
      inner.classList.remove(this.#textClass[oldStyle]);
      inner.classList.add(this.#textClass[newStyle]);
    });
  }

  // 刷新节点翻译
  #refreshNode(node) {
    this.#cleanupDirectTranslations(node);
    this.#processNode(node);
  }

  // 使指定节点的状态与当前的全局同步
  #performSyncNode(node) {
    const appliedRule = this.#processedNodes.get(node);
    if (!appliedRule) {
      this.#enabled && this.#processNode(node);
      return;
    }

    const { apiSlug, fromLang, toLang, hasRichText, textStyle, transOnly } =
      this.#rule;

    const needsRefresh =
      appliedRule.apiSlug !== apiSlug ||
      appliedRule.fromLang !== fromLang ||
      appliedRule.toLang !== toLang ||
      appliedRule.hasRichText !== hasRichText;

    // 需要重新翻译
    if (needsRefresh) {
      Object.assign(appliedRule, {
        apiSlug,
        fromLang,
        toLang,
        hasRichText,
        textStyle,
        transOnly,
      });
      this.#refreshNode(node); // 会自动应用新样式
      return;
    }

    // 样式规则过时
    if (appliedRule.textStyle !== textStyle) {
      const oldStyle = appliedRule.textStyle;
      appliedRule.textStyle = textStyle;
      this.#updateStyle(node, oldStyle, textStyle);
    }

    // 切换原文显示
    if (appliedRule.transOnly !== transOnly) {
      appliedRule.transOnly = transOnly;
      this.#toggleTranslationOnly(node, transOnly);
    }
  }

  // 停止监听，重置参数
  #resetOptions() {
    this.#io.disconnect();
    this.#mo.disconnect();
    this.#viewNodes.clear();
    this.#rootNodes.clear();
    this.#observedNodes = new WeakSet();
    this.#translationNodes = new WeakMap();
    this.#processedNodes = new WeakMap();
  }

  // 开启鼠标悬停翻译
  #enableMouseHover() {
    if (this.#mouseHoverEnabled) return;
    this.#mouseHoverEnabled = true;
    this.#setting.mouseHoverSetting.useMouseHover = true;

    document.addEventListener("mousemove", this.#boundMouseMoveHandler);
    const { mouseHoverKey } = this.#setting.mouseHoverSetting;
    if (mouseHoverKey.length === 0) {
      // mouseHoverKey = DEFAULT_MOUSEHOVER_KEY;
      return;
    }
    this.#removeKeydownHandler = shortcutRegister(
      mouseHoverKey,
      this.#boundKeyDownHandler
    );
  }

  // 禁用鼠标悬停翻译
  #disableMouseHover() {
    if (!this.#mouseHoverEnabled) return;
    this.#mouseHoverEnabled = false;
    this.#setting.mouseHoverSetting.useMouseHover = false;

    document.removeEventListener("mousemove", this.#boundMouseMoveHandler);
    this.#removeKeydownHandler?.();
  }

  // 注入JS/CSS
  #initInjector() {
    if (this.#isJsInjected) {
      return;
    }
    this.#isJsInjected = true;

    try {
      const { injectJs, injectCss } = this.#rule;
      if (isExt) {
        injectJs && sendBgMsg(MSG_INJECT_JS, injectJs);
        injectCss && sendBgMsg(MSG_INJECT_CSS, injectCss);
      } else {
        injectJs &&
          injectInlineJs(injectJs, "kiss-translator-userinit-injector");
        injectCss && injectInternalCss(injectCss);
      }
    } catch (err) {
      kissLog("inject js", err);
    }
  }

  // 移除JS/CSS
  #removeInjector() {
    document
      .querySelectorAll(`[data-source^="kiss-inject"]`)
      ?.forEach((el) => el.remove());
  }

  // 切换鼠标悬停翻译
  toggleMouseHover() {
    this.#mouseHoverEnabled
      ? this.#disableMouseHover()
      : this.#enableMouseHover();
  }

  // 开启翻译
  enable() {
    if (this.#enabled) return;
    this.#enabled = true;
    this.#rule.transOpen = "true";
    this.#runId++;

    if (this.#isInitialized) {
      if (this.#setting.transAllnow) {
        this.rescan();
      } else {
        this.#reIOViewNodes();
      }
    } else {
      this.#init();
    }

    if (this.#rule.transTitle === "true") {
      this.#translateTitle();
    }
  }

  // 翻译页面标题
  async #translateTitle() {
    const title = document.title;
    this.#docInfo.title = title;
    if (!title) return;

    try {
      const deLang = await tryDetectLang(title);
      const [translatedTitle] = await this.#translateFetch(title, deLang);
      document.title = translatedTitle || title;
    } catch (err) {
      kissLog("tanslate title", err);
    }
  }

  // 关闭翻译
  disable() {
    if (!this.#enabled) return;
    this.#enabled = false;
    this.#rule.transOpen = "false";
    this.#runId++;

    this.#cleanupAllNodes();
    clearFetchPool();
    clearAllBatchQueue();

    // 恢复页面标题
    if (this.#docInfo.title) {
      document.title = this.#docInfo.title;
    }
  }

  // 重新扫描页面
  rescan() {
    if (!this.#isInitialized) return;
    this.#runId++;

    this.#cleanupAllNodes();
    this.#resetOptions();
    clearFetchPool();
    clearAllBatchQueue();

    // 重新初始化
    this.#init();
  }

  // 切换是否翻译
  toggle() {
    this.#enabled ? this.disable() : this.enable();
  }

  // 快速切换模糊样式
  toggleStyle() {
    const textStyle =
      this.#rule.textStyle === OPT_STYLE_FUZZY
        ? OPT_STYLE_NONE
        : OPT_STYLE_FUZZY;
    this.updateRule({ textStyle });
  }

  // 切换划词翻译
  toggleTransbox() {
    this.#setting.tranboxSetting.transOpen =
      !this.#setting.tranboxSetting.transOpen;
  }

  // 切换输入框翻译
  toggleInputTranslate() {
    this.#setting.inputRule.transOpen = !this.#setting.inputRule.transOpen;
  }

  // 停止运行
  stop() {
    this.disable();
    this.#resetOptions();
    this.#srm.stop();
    this.#disableMouseHover();
    this.#removeInjector();
    this.#isInitialized = false;
  }

  // 更新规则
  updateRule(newRule) {
    let hasChanged = false;
    let needsRescan = false;
    for (const key in newRule) {
      if (
        Object.prototype.hasOwnProperty.call(this.#rule, key) &&
        this.#rule[key] !== newRule[key]
      ) {
        this.#rule[key] = newRule[key];
        if (key === "autoScan" || key === "hasShadowroot") {
          needsRescan = true;
        } else {
          hasChanged = true;
        }
      }
    }

    if (needsRescan || (this.#enabled && this.#setting.transAllnow)) {
      this.rescan();
      return;
    }

    if (hasChanged) {
      this.#reIOViewNodes();
    }
  }

  get setting() {
    return { ...this.#setting };
  }

  get rule() {
    return { ...this.#rule };
  }

  get docInfo() {
    return { ...this.#docInfo };
  }

  get eventName() {
    return this.#eventName;
  }
}
