import sanitizeHtml from "sanitize-html";

// Allowed font families: Cairo, Tajawal, Amiri, Noto Naskh Arabic,
// IBM Plex Sans Arabic, system-ui, sans-serif, serif, monospace.
const FONT_NAME_PART = String.raw`(?:["']?(?:Cairo|Tajawal|Amiri|Noto Naskh Arabic|IBM Plex Sans Arabic|system-ui|sans-serif|serif|monospace)["']?)`;
const FONT_FAMILY_RE = new RegExp(
  `^${FONT_NAME_PART}(?:\\s*,\\s*${FONT_NAME_PART})*$`,
);

const FONT_SIZE_RE = /^(?:[8-9]|[1-9]\d)px$/;
const COLOR_RE = /^(?:#[0-9a-f]{3}|#[0-9a-f]{6}|rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\))$/i;

export const RICH_TEXT_OPTIONS: sanitizeHtml.IOptions = {
  allowedTags: ["p", "span", "strong", "b", "em", "i", "u", "br"],
  allowedAttributes: {
    p: ["style"],
    span: ["style"],
    strong: ["style"],
    b: ["style"],
    em: ["style"],
    i: ["style"],
    u: ["style"],
  },
  allowedStyles: {
    "*": {
      "color": [COLOR_RE],
      "background-color": [COLOR_RE],
      "font-weight": [/^(?:normal|bold|[1-9]00)$/i],
      "font-style": [/^(?:normal|italic|oblique)$/i],
      "text-decoration": [/^(?:none|underline)$/i],
      "text-align": [/^(?:left|right|center|start|end|justify)$/i],
      "font-size": [FONT_SIZE_RE],
      "font-family": [FONT_FAMILY_RE],
    },
  },
  allowedSchemes: [],
  disallowedTagsMode: "discard",
  enforceHtmlBoundary: false,
  exclusiveFilter: () => false,
};

/**
 * Sanitize a Tiptap-style rich-text HTML string from a client.
 *
 * - Allowed tags: span, strong/b, em/i, u, br
 * - Allowed inline styles: color, background-color, font-weight, font-style,
 *   text-decoration, text-align, font-size (px, 8-99), font-family (allowlist)
 * - All other tags/attributes/styles are stripped.
 *
 * Empty / null / undefined input becomes "".
 */
export function sanitizeRichText(input: unknown): string {
  if (input === null || input === undefined) return "";
  const s = typeof input === "string" ? input : String(input);
  if (s.length === 0) return "";
  return sanitizeHtml(s, RICH_TEXT_OPTIONS);
}

/**
 * Convenience for places that previously accepted plain text but now
 * accept rich text — null/empty preserved as null when needed.
 */
export function sanitizeRichTextOrNull(input: unknown): string | null {
  if (input === null || input === undefined) return null;
  const s = sanitizeRichText(input);
  return s === "" ? null : s;
}

/**
 * Strip ALL HTML tags from a string and HTML-escape any stray `<`, `>`,
 * `&`, or quote characters. Use for fields that the UI treats as plain
 * text but that get interpolated into HTML by templates (e.g. the print
 * page) — this prevents a payload like `<script>alert(1)</script>` from
 * surviving as executable markup if a future template forgets to escape.
 *
 * Empty / null / undefined input becomes "".
 */
export function sanitizePlainText(input: unknown): string {
  if (input === null || input === undefined) return "";
  const s = typeof input === "string" ? input : String(input);
  if (s.length === 0) return "";
  return sanitizeHtml(s, { allowedTags: [], allowedAttributes: {} });
}

/**
 * Same as {@link sanitizePlainText} but preserves null for fields that
 * are nullable in the database (e.g. attendee.title).
 */
export function sanitizePlainTextOrNull(input: unknown): string | null {
  if (input === null || input === undefined) return null;
  const s = sanitizePlainText(input);
  return s === "" ? null : s;
}
