// ST-BME: MVU (MagVarUpdate) compatibility helpers for private task prompts.
// These rules are intentionally narrow so we strip MVU artifacts without
// disturbing normal prompt or world info content.

export const MVU_ENTRY_COMMENT_REGEX = /\[(mvu_update|mvu_plot|initvar)\]/i;

const MVU_UPDATE_BLOCK_REGEX =
  /\n?<(update(?:variable)?|variableupdate)>(?:(?!<\1>).)*?<\/\1>/gis;
const MVU_STATUS_PLACEHOLDER_REGEX = /\n?<StatusPlaceHolderImpl\/>/gi;
const MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX =
  /\n?<status_current_variables?>[\s\S]*?<\/status_current_variables?>/gi;
const MVU_STATUS_CURRENT_VARIABLE_DETECT_REGEX =
  /<status_current_variables?>[\s\S]*?<\/status_current_variables?>/i;
const MVU_MESSAGE_VARIABLE_MACRO_REGEX =
  /\{\{\s*get_message_variable::(?:stat_data|display_data|delta_data)(?:\.[^}]+)?\s*}}/gi;
const MVU_GETVAR_REFERENCE_REGEX =
  /getvar\(\s*["'](?:stat_data|display_data|delta_data)["']\s*\)/gi;
const MVU_STATEFUL_TEMPLATE_TAG_REGEX =
  /<%[-=]?[\s\S]*?(?:SafeGetValue|getvar\(\s*["'](?:stat_data|display_data|delta_data)["']\s*\)|\b(?:stat_data|display_data|delta_data)\b)[\s\S]*?%>/gi;
const EJS_TEMPLATE_TAG_REGEX = /<%[-=]?[\s\S]*?%>/gi;
const MVU_VARIABLE_OUTPUT_ENTRY_REGEX = /变量输出格式:\s*[\s\S]*?<UpdateVariable>/i;
const MVU_VARIABLE_RULES_ENTRY_REGEX =
  /变量更新规则:\s*[\s\S]*?(?:type:\s*|check:\s*|当前时间:|近期事务:)/i;
const MVU_FORMAT_EMPHASIS_ENTRY_REGEX =
  /(?:变量输出格式强调|格式强调[：:]?-?变量更新规则|格式强调[：:]?-?剧情演绎|The following must be inserted to the end of (?:each )?reply,? and cannot be omitted)[\s\S]*?format:\s*\|-?/i;
const MVU_STATE_OBJECT_FIELD_REGEX =
  /["']?(?:stat_data|display_data|delta_data)["']?\s*:/i;
const MVU_STATE_PATH_REFERENCE_REGEX =
  /\b(?:stat_data|display_data|delta_data)(?:\.[\w$\u4e00-\u9fff\[\]"'-]+){1,}/i;
const MVU_STATE_HELPER_REFERENCE_REGEX =
  /\b(?:SafeGetValue\([^)]*(?:stat_data|display_data|delta_data)[^)]*\)|message_data\[\d+\]\.data\.(?:stat_data|display_data|delta_data))\b/i;

function uniq(values = []) {
  return [...new Set((Array.isArray(values) ? values : []).filter(Boolean))];
}

function normalizeText(value = "") {
  return String(value || "").replace(/\r\n/g, "\n");
}

function collapseWhitespace(value = "") {
  return String(value || "")
    .replace(/[ \t]{2,}/g, " ")
    .replace(/\n{3,}/g, "\n\n")
    .trim();
}

function countRegexMatches(text = "", regex) {
  if (!text || !(regex instanceof RegExp)) {
    return 0;
  }

  const source = new RegExp(regex.source, regex.flags);
  let count = 0;
  while (source.exec(text)) {
    count += 1;
  }
  return count;
}

function matchesRegex(text = "", regex) {
  if (!text || !(regex instanceof RegExp)) {
    return false;
  }

  return new RegExp(regex.source, regex.flags).test(text);
}

function stripMvuPromptArtifactsDetailed(content = "") {
  const input = normalizeText(content);
  if (!input) {
    return {
      text: "",
      changed: false,
      artifactRemovedCount: 0,
    };
  }

  const statefulTemplateTagCount = countRegexMatches(
    input,
    MVU_STATEFUL_TEMPLATE_TAG_REGEX,
  );
  const artifactRemovedCount =
    countRegexMatches(input, MVU_UPDATE_BLOCK_REGEX) +
    countRegexMatches(input, MVU_STATUS_PLACEHOLDER_REGEX) +
    countRegexMatches(input, MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX) +
    countRegexMatches(input, MVU_MESSAGE_VARIABLE_MACRO_REGEX) +
    countRegexMatches(input, MVU_GETVAR_REFERENCE_REGEX) +
    statefulTemplateTagCount;

  let stripped = input
    .replace(MVU_UPDATE_BLOCK_REGEX, "")
    .replace(MVU_STATUS_PLACEHOLDER_REGEX, "")
    .replace(MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX, "")
    .replace(MVU_MESSAGE_VARIABLE_MACRO_REGEX, "")
    .replace(MVU_GETVAR_REFERENCE_REGEX, "")
    .replace(MVU_STATEFUL_TEMPLATE_TAG_REGEX, "");
  if (statefulTemplateTagCount > 0) {
    stripped = stripped.replace(EJS_TEMPLATE_TAG_REGEX, "");
  }

  const normalized = collapseWhitespace(stripped);
  return {
    text: normalized,
    changed: normalized !== collapseWhitespace(input),
    artifactRemovedCount,
  };
}

function stripBlockedPromptContentsDetailed(content = "", blockedContents = []) {
  const input = normalizeText(content);
  const normalizedBlocked = uniq(
    (Array.isArray(blockedContents) ? blockedContents : [])
      .map((item) => collapseWhitespace(item))
      .filter(Boolean)
      .sort((left, right) => right.length - left.length),
  );

  if (!input || normalizedBlocked.length === 0) {
    return {
      text: collapseWhitespace(input),
      changed: false,
      blockedHitCount: 0,
    };
  }

  let output = input;
  let blockedHitCount = 0;
  for (const blocked of normalizedBlocked) {
    let index = output.indexOf(blocked);
    while (index >= 0) {
      blockedHitCount += 1;
      output = `${output.slice(0, index)}${output.slice(index + blocked.length)}`;
      index = output.indexOf(blocked);
    }
  }

  const normalized = collapseWhitespace(output);
  return {
    text: normalized,
    changed: normalized !== collapseWhitespace(input),
    blockedHitCount,
  };
}

export function isMvuTaggedWorldInfoComment(comment = "") {
  return MVU_ENTRY_COMMENT_REGEX.test(String(comment || ""));
}

export function isMvuTaggedWorldInfoNameOrComment(name = "", comment = "") {
  return (
    MVU_ENTRY_COMMENT_REGEX.test(String(name || "")) ||
    MVU_ENTRY_COMMENT_REGEX.test(String(comment || ""))
  );
}

export function isLikelyMvuWorldInfoContent(content = "") {
  const normalized = collapseWhitespace(content);
  if (!normalized) {
    return false;
  }
  const stateKeyMentionCount =
    normalized.match(/\b(?:stat_data|display_data|delta_data)\b/gi)?.length || 0;

  const stateSignals = [
    MVU_MESSAGE_VARIABLE_MACRO_REGEX,
    MVU_GETVAR_REFERENCE_REGEX,
    MVU_STATE_OBJECT_FIELD_REGEX,
    MVU_STATE_PATH_REFERENCE_REGEX,
    MVU_STATE_HELPER_REFERENCE_REGEX,
  ].reduce(
    (count, pattern) => count + (matchesRegex(normalized, pattern) ? 1 : 0),
    0,
  );

  return (
    matchesRegex(normalized, MVU_STATUS_CURRENT_VARIABLE_DETECT_REGEX) ||
    matchesRegex(normalized, MVU_VARIABLE_OUTPUT_ENTRY_REGEX) ||
    matchesRegex(normalized, MVU_VARIABLE_RULES_ENTRY_REGEX) ||
    matchesRegex(normalized, MVU_FORMAT_EMPHASIS_ENTRY_REGEX) ||
    stateSignals >= 2 ||
    (stateSignals >= 1 && stateKeyMentionCount >= 2)
  );
}

export function stripMvuPromptArtifacts(content = "") {
  return stripMvuPromptArtifactsDetailed(content).text;
}

export function stripBlockedPromptContents(content = "", blockedContents = []) {
  return stripBlockedPromptContentsDetailed(content, blockedContents).text;
}

export function sanitizeMvuContent(
  content = "",
  { mode = "aggressive", blockedContents = [] } = {},
) {
  const originalText = normalizeText(content);
  const originalCollapsed = collapseWhitespace(originalText);
  const sanitizedMode = String(mode || "aggressive").trim().toLowerCase();

  const artifactResult = stripMvuPromptArtifactsDetailed(originalCollapsed);
  const blockedResult = stripBlockedPromptContentsDetailed(
    artifactResult.text,
    blockedContents,
  );

  const reasons = [];
  if (artifactResult.artifactRemovedCount > 0) {
    reasons.push("artifact_stripped");
  }
  if (blockedResult.blockedHitCount > 0) {
    reasons.push("blocked_content_removed");
  }

  let text = blockedResult.text;
  let dropped = false;
  if (sanitizedMode === "aggressive") {
    if (
      isLikelyMvuWorldInfoContent(originalCollapsed) ||
      isLikelyMvuWorldInfoContent(text)
    ) {
      text = "";
      dropped = true;
      reasons.push("likely_mvu_content");
    }
  }

  return {
    text: collapseWhitespace(text),
    changed: collapseWhitespace(text) !== originalCollapsed,
    dropped,
    reasons: uniq(reasons),
    blockedHitCount: blockedResult.blockedHitCount,
    artifactRemovedCount: artifactResult.artifactRemovedCount,
  };
}
