import type { ToolInput } from "../../core/io/input-types";
import type { ToolResult } from "../../core/io/output-types";
import type { ToolContext } from "../../core/plugins/plugin-types";
import type { HtmlEntityOptions } from "./index";

const ENTITY_MAP: Record<string, string> = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  '"': "&quot;",
  "'": "&#39;",
  "©": "&copy;",
  "®": "&reg;",
  "™": "&trade;",
  "—": "&mdash;",
  "–": "&ndash;",
  "«": "&laquo;",
  "»": "&raquo;",
  "°": "&deg;",
  "±": "&plusmn;",
  "×": "&times;",
  "÷": "&divide;",
  "€": "&euro;",
  "£": "&pound;",
  "¥": "&yen;",
  "¢": "&cent;",
  "§": "&sect;",
  "¶": "&para;",
  "•": "&bull;",
  "…": "&hellip;",
};

const REVERSE_ENTITY_MAP: Record<string, string> = {};
for (const [char, entity] of Object.entries(ENTITY_MAP)) {
  REVERSE_ENTITY_MAP[entity] = char;
}

function encodeHtmlEntities(text: string): string {
  return text.replace(/[&<>"']|[©®™—–«»°±×÷€£¥¢§¶•…]/g, (char) => {
    return ENTITY_MAP[char] || char;
  });
}

function decodeHtmlEntities(text: string): string {
  let result = text;

  for (const [entity, char] of Object.entries(REVERSE_ENTITY_MAP)) {
    result = result.replaceAll(entity, char);
  }

  result = result.replace(/&#(\d+);/g, (_, code) => {
    return String.fromCharCode(parseInt(code, 10));
  });

  result = result.replace(/&#x([0-9a-fA-F]+);/g, (_, code) => {
    return String.fromCharCode(parseInt(code, 16));
  });

  return result;
}

export async function runHtmlEntity(
  input: ToolInput,
  options: HtmlEntityOptions,
  context: ToolContext
): Promise<ToolResult> {
  const text = input.text ?? "";
  if (!text) {
    return { type: "text", value: "" };
  }

  context.reportProgress({ percentage: 50, message: "Processing..." });

  const result = options.mode === "encode" ? encodeHtmlEntities(text) : decodeHtmlEntities(text);

  context.reportProgress({ percentage: 100, message: "Done" });

  return {
    type: "text",
    value: result,
  };
}
