/**
 * Zentrale Keyword-Blacklist zur Erkennung unangemessener Inhalte
 * (Chat, Journal, Notizen, Kommentare, Leads etc.).
 *
 * Diese Datei enthält nur die Liste + Prüf-Logik. Das Einhängen in die
 * einzelnen Insert-/Update-Stellen (use-chat.ts, use-journal.ts, ...)
 * ist ein separater Schritt.
 */

// Kategorien nur zur Pflege/Übersicht, technisch eine flache Liste.
const GERMAN_PROFANITY = [
  "arsch",
  "arschloch",
  "arschgeige",
  "wichser",
  "wichsen",
  "scheisse",
  "scheiße",
  "verschissen",
  "hurensohn",
  "hure",
  "nutte",
  "fotze",
  "schlampe",
  "miststück",
  "mistkerl",
  "drecksau",
  "dreckssau",
  "drecksack",
  "spast",
  "spasti",
  "mongo",
  "behindert", // in beleidigendem Kontext, ggf. Whitelist-Ausnahmen nötig
  "missgeburt",
  "vollidiot",
  "vollpfosten",
  "wixer",
  "schwuchtel",
  "hackfresse",
  "fresse halten",
  "halt die fresse",
  "leck mich",
  "verpiss dich",
  "kackbratze",
  "flachwichser",
  "hosenscheisser",
  "hosenscheißer",
];

const GERMAN_HATE_SLURS = [
  "kanake",
  "neger",
  "negerin",
  "zigeuner",
  "untermensch",
  "judensau",
  "scheißjude",
  "scheissjude",
  "scheißausländer",
  "scheissausländer",
];

const ENGLISH_PROFANITY = [
  "fuck",
  "fucking",
  "motherfucker",
  "shit",
  "bullshit",
  "asshole",
  "bitch",
  "bastard",
  "dick",
  "dickhead",
  "piss off",
  "cunt",
  "whore",
  "slut",
  "retard",
  "retarded",
  "idiot",
];

const ENGLISH_HATE_SLURS = [
  "nigger",
  "nigga",
  "faggot",
  "tranny",
  "spic",
  "chink",
  "kike",
];

const VIOLENCE_THREATS = [
  "ich bring dich um",
  "ich töte dich",
  "ich schlag dich",
  "i will kill you",
  "kill yourself",
  "bring dich um",
  "umbringen werde ich dich",
];

export const BLACKLISTED_WORDS: readonly string[] = Object.freeze([
  ...GERMAN_PROFANITY,
  ...GERMAN_HATE_SLURS,
  ...ENGLISH_PROFANITY,
  ...ENGLISH_HATE_SLURS,
  ...VIOLENCE_THREATS,
]);

/**
 * Normalisiert Text, damit simple Umgehungsversuche (Groß-/Kleinschreibung,
 * Sonderzeichen als Trenner, wiederholte Buchstaben, gängige Leetspeak-
 * Ersetzungen) trotzdem erkannt werden.
 */
function normalize(text: string): string {
  return text
    .toLowerCase()
    .normalize("NFKD")
    .replace(/[̀-ͯ]/g, "") // Akzente entfernen
    .replace(/[013457@$!]/g, (char) => {
      const leetMap: Record<string, string> = {
        "0": "o",
        "1": "i",
        "3": "e",
        "4": "a",
        "5": "s",
        "7": "t",
        "@": "a",
        "$": "s",
        "!": "i",
      };
      return leetMap[char] ?? char;
    })
    .replace(/[^a-z0-9äöüß\s]/g, "") // Sonderzeichen/Trenner (z.B. a.r.s.c.h) entfernen
    .replace(/(.)\1{2,}/g, "$1$1") // wiederholte Buchstaben einkürzen (aaarsch -> aarsch)
    .replace(/\s+/g, " ")
    .trim();
}

function buildWordRegex(word: string): RegExp {
  const normalizedWord = normalize(word);
  const escaped = normalizedWord.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  // Mehrwort-Begriffe (z.B. "kill yourself") ohne Wortgrenzen-Zwang matchen,
  // Einzelwörter mit Wortgrenzen, damit z.B. "Arschitektur" nicht triggert.
  const pattern = normalizedWord.includes(" ")
    ? escaped
    : `\\b${escaped}\\b`;
  return new RegExp(pattern, "i");
}

const BLACKLIST_REGEXES = BLACKLISTED_WORDS.map((word) => ({
  word,
  regex: buildWordRegex(word),
}));

/** Prüft, ob der Text mindestens einen Blacklist-Begriff enthält. */
export function containsBlacklistedWord(text: string): boolean {
  if (!text) return false;
  const normalized = normalize(text);
  return BLACKLIST_REGEXES.some(({ regex }) => regex.test(normalized));
}

/** Liefert alle in einem Text gefundenen Blacklist-Begriffe (Originalform aus der Liste). */
export function findBlacklistedWords(text: string): string[] {
  if (!text) return [];
  const normalized = normalize(text);
  return BLACKLIST_REGEXES.filter(({ regex }) => regex.test(normalized)).map(
    ({ word }) => word,
  );
}

/** Ersetzt gefundene Blacklist-Begriffe im Originaltext durch Sternchen. */
export function censorText(text: string, mask = "*"): string {
  if (!text) return text;
  let result = text;
  for (const { word } of BLACKLIST_REGEXES) {
    // Auf Originaltext anwenden, damit Groß-/Kleinschreibung erhalten bleibt.
    const looseRegex = new RegExp(
      word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s+"),
      "gi",
    );
    result = result.replace(looseRegex, (match) => mask.repeat(match.length));
  }
  return result;
}

/** Extrahiert reinen Text aus einem Tiptap-JSON-Dokument (rekursiv). */
export function extractPlainTextFromDoc(
  content: Record<string, unknown> | null | undefined,
): string {
  if (!content) return "";
  const parts: string[] = [];

  function walk(value: unknown) {
    if (!value || typeof value !== "object") return;
    const node = value as { content?: unknown[]; text?: unknown };
    if (typeof node.text === "string") {
      parts.push(node.text);
    }
    if (Array.isArray(node.content)) {
      node.content.forEach(walk);
    }
  }

  walk(content);
  return parts.join(" ").trim();
}

/** Zensiert Blacklist-Begriffe innerhalb eines Tiptap-JSON-Dokuments, ohne die Struktur zu verändern. */
export function censorTiptapContent<T extends Record<string, unknown>>(
  content: T | null | undefined,
): T | null | undefined {
  if (!content) return content;

  function walk(value: unknown): unknown {
    if (Array.isArray(value)) {
      return value.map(walk);
    }
    if (value && typeof value === "object") {
      const node = { ...(value as Record<string, unknown>) };
      if (typeof node.text === "string") {
        node.text = censorText(node.text);
      }
      if (Array.isArray(node.content)) {
        node.content = node.content.map(walk);
      }
      return node;
    }
    return value;
  }

  return walk(content) as T;
}
