/**
 * Sanitization für vom User importierte HTML-Section-Blöcke (bodyMode='html_raw').
 *
 * Der User pasted typischerweise ein komplettes Email-HTML-Dokument (`<!DOCTYPE>...<html>...<body>...`)
 * mit Inline-CSS, mso/VML-Conditionals und Tabellen-Layout. Wir extrahieren den Body-Inhalt
 * (alles innerhalb von `<body>`), strippen `<style>`-Tags (Email-Clients wie Gmail strippen
 * non-inline-CSS sowieso), entfernen XSS-Vektoren (`<script>`, `<iframe>`, `on*`-Handler,
 * `javascript:`-URLs) und behalten alles email-safe wie Tabellen, Inline-CSS, mso-Comments.
 *
 * Output ist ein HTML-Fragment (kein Doctype, kein <html>/<head>/<body>) das in der
 * Generate-Pipeline in `<mj-raw>...</mj-raw>` gewrapped wird.
 */

import DOMPurify from 'isomorphic-dompurify';

/** Email-safe Tags. Bewusst keine form-Elemente (<input>, <select>, <textarea>),
 *  keine <script>/<iframe>/<object>/<embed>/<video>/<audio>/<source>/<canvas>. */
const ALLOWED_TAGS = [
  'a',
  'b',
  'blockquote',
  'body',
  'br',
  'center',
  'div',
  'em',
  'font',
  'h1',
  'h2',
  'h3',
  'h4',
  'h5',
  'h6',
  'hr',
  'i',
  'img',
  'li',
  'ol',
  'p',
  'pre',
  'small',
  'span',
  'strong',
  'sub',
  'sup',
  'table',
  'tbody',
  'td',
  'tfoot',
  'th',
  'thead',
  'tr',
  'u',
  'ul',
];

const ALLOWED_ATTR = [
  'align',
  'alt',
  'bgcolor',
  'border',
  'cellpadding',
  'cellspacing',
  'class',
  'color',
  'colspan',
  'dir',
  'face',
  'height',
  'href',
  'id',
  'lang',
  'role',
  'rowspan',
  'size',
  'src',
  'style',
  'target',
  'title',
  'valign',
  'width',
];

/**
 * Extrahiert den Inhalt zwischen `<body>...</body>` falls Input ein vollständiges
 * HTML-Dokument ist. Sonst Input unverändert. Regex-basiert weil wir hier auf
 * Server-Side ohne DOMParser auskommen wollen.
 */
function extractBodyContent(input: string): string {
  const bodyMatch = input.match(/<body\b[^>]*>([\s\S]*?)<\/body\s*>/i);
  return bodyMatch ? bodyMatch[1] : input;
}

/**
 * Strippt `<style>...</style>`-Blöcke. In Email-Clients wie Gmail werden non-inline-
 * CSS-Regeln sowieso entfernt — wir verlassen uns ausschließlich auf inline-style.
 * Globale Regeln aus dem `<head>` sind nach extractBodyContent eh schon weg.
 */
function stripStyleTags(input: string): string {
  return input.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/gi, '');
}

export interface SanitizeResult {
  html: string;
  /** True wenn der Sanitizer etwas verworfen hat (z.B. <script>). UI kann es als Hinweis zeigen. */
  modified: boolean;
}

export function sanitizeSectionHtml(rawInput: string): SanitizeResult {
  const stage1 = extractBodyContent(rawInput);
  const stage2 = stripStyleTags(stage1);

  const cleaned = DOMPurify.sanitize(stage2, {
    ALLOWED_TAGS,
    ALLOWED_ATTR,
    ALLOW_UNKNOWN_PROTOCOLS: false,
    ALLOW_DATA_ATTR: false,
    KEEP_CONTENT: true,
    // Comments behalten — mso/VML-Conditionals (`<!--[if mso]>...<![endif]-->`) sind
    // für Outlook-Rendering kritisch.
    ALLOW_ARIA_ATTR: true,
    FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'select', 'textarea', 'button'],
    FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout', 'onfocus', 'onblur'],
  });

  // Stripping-Indicator: wenn nach Cleanup signifikant kürzer → wir haben was entfernt.
  const modified = cleaned.length < stage2.length || stage2.length < rawInput.length;

  return { html: cleaned, modified };
}

/**
 * Hilfs-Wrapper für die Generate-Pipeline. Wickelt sanitized HTML in `<mj-raw>`,
 * sodass der MJML-Compiler es 1:1 in das finale Mail-HTML übernimmt.
 */
export function wrapHtmlAsMjmlRaw(rawInput: string): string {
  const { html } = sanitizeSectionHtml(rawInput);
  return `<mj-raw>${html}</mj-raw>`;
}

export interface ExtractedMjml {
  /** Der `<mj-section>`-Inhalt aus `<mj-body>` — bereit zum Inject. */
  body: string;
  /**
   * `<mj-style>`-Blöcke aus dem User-`<mj-head>`, joined als String. Werden vom Caller
   * (injectExtraSections) in den Mail-`<mj-head>` gemerget, damit User-Selektoren in
   * der finalen Mail wirken statt still verworfen zu werden.
   *
   * Format: `<mj-style>...</mj-style>\n<mj-style inline="inline">...</mj-style>` etc.
   * Empty string wenn keine User-Styles vorhanden waren.
   */
  styles: string;
}

/**
 * Extrahiert den Body einer vollständigen MJML-Datei (`<mjml>...<mj-body>...</mj-body>...</mjml>`)
 * sodass nur die `<mj-section>`-Blöcke übrigbleiben — passend zum Splice-Insertion in eine
 * existierende Mail. Wenn der Input bereits ein nacktes Section-Fragment ist (z.B. nur
 * `<mj-section>...</mj-section>`), wird er unverändert zurückgegeben.
 *
 * Bonus: `<mj-style>`-Blöcke aus dem User-Header werden separat extrahiert, damit der
 * Caller sie in den Mail-`<mj-head>` migrieren kann (sonst gehen sie still verloren).
 * Andere `<mj-head>`-Children (`<mj-attributes>`, `<mj-font>`, `<mj-title>`, …) werden
 * verworfen — die kommen aus der Haupt-Mail.
 *
 * Hintergrund: User pasten oft komplette MJML-Dokumente. Da unsere Mail bereits einen
 * `<mj-head>` + `<mj-body>` hat, würde ein zweites `<mjml>`-Wurzel-Element den Compiler
 * verwirren oder die Section verwerfen.
 */
export function extractMjml(rawInput: string): ExtractedMjml {
  // 1. Style-Blöcke aus dem User-Head extrahieren (vor allem anderen, weil sie unabhängig
  //    von der Body-Struktur sind und bei Section-Fragmenten ohne <mj-head> einfach fehlen).
  const styleMatches = rawInput.matchAll(/<mj-style\b[^>]*>[\s\S]*?<\/mj-style\s*>/gi);
  const styles = Array.from(styleMatches, (m) => m[0]).join('\n');

  // 2. Body extrahieren — wenn <mj-body> existiert, dessen Inhalt; sonst alles ohne <mjml>/<mj-head>.
  const bodyMatch = rawInput.match(/<mj-body\b[^>]*>([\s\S]*?)<\/mj-body\s*>/i);
  if (bodyMatch) return { body: bodyMatch[1].trim(), styles };

  const stripped = rawInput
    .replace(/<\/?mjml\b[^>]*>/gi, '')
    .replace(/<mj-head\b[^>]*>[\s\S]*?<\/mj-head\s*>/gi, '');
  return { body: stripped.trim(), styles };
}

/**
 * Backwards-compat-Alias — frühere Callsites holen sich nur den Body.
 * @deprecated Nutze `extractMjml()` für Style-Migration in den Mail-Head.
 */
export function extractMjmlBody(rawInput: string): string {
  return extractMjml(rawInput).body;
}

/**
 * Detektiert ob ein User-MJML-Snippet `<mj-style>` enthält. Frontend-Helper für UI-Hints.
 * Server-side wird das via `extractMjml` automatisch in den Mail-Head migriert.
 */
export function hasMjStyle(rawInput: string): boolean {
  return /<mj-style\b/i.test(rawInput);
}
