/**
 * Inline-Editor: Inject von Style + Script ins Preview-HTML, sodass Text-Slots
 * mit `data-edit-key` direkt im iframe per Click editiert werden können.
 *
 * Wichtig: Wird NUR auf den Preview-iframe-srcDoc angewendet — niemals auf das HTML
 * das per Worker an Empfänger geschickt wird (das hat keine `data-edit-key` Attribute zwar
 * im Source, aber Email-Clients ignorieren sie sowieso; Script wird nicht injectiert).
 *
 * Architektur:
 *  - `data-edit-key="hero_headline"` markiert ein editierbares Text-Slot
 *  - `data-edit-index="0"` zusätzlich für Array-Felder (z.B. benefits[0])
 *  - `data-mf-action="add-benefit" | "remove-benefit"` markiert Action-Slots
 *    (im echten Mail per inline-style display:none versteckt; das Preview-Script unhided sie)
 *  - `data-mf-placeholder="FIRST_NAME"` markiert {{XXX}}-Tokens als nicht-editierbar
 *  - Hover → pinker Outline + Pencil-Cursor
 *  - Click → contentEditable=true + Focus
 *  - Blur oder Enter → contentEditable=false + postMessage zum Parent
 *  - Escape → Abbruch ohne Update
 *  - Auto-Height: ResizeObserver + MutationObserver melden scrollHeight an Parent,
 *    der setzt iframe.style.height → kein nested vertical scroll im iframe.
 */

const INLINE_EDIT_STYLE = `
[data-edit-key] {
  cursor: text;
  transition: outline 0.12s ease, background 0.12s ease;
  outline: 2px dashed transparent;
  outline-offset: 4px;
  border-radius: 2px;
}
[data-edit-key]:hover {
  outline: 2px dashed #ff006a;
  background: rgba(255, 0, 106, 0.04);
}
[data-edit-key][contenteditable="true"] {
  outline: 2px solid #ff006a !important;
  background: rgba(255, 0, 106, 0.08) !important;
  cursor: text;
}
[data-edit-key][contenteditable="true"]:focus {
  outline: 2px solid #ff006a !important;
}
/* Subtle "click to edit" hint via cursor + tooltip */
[data-edit-key]:hover::after {
  content: '';
}
/* Benefits-Inline-Aktionen — nur im Preview sichtbar (Script unhided per JS) */
.mf-benefit-remove {
  display: inline-block !important;
  opacity: 0;
  margin-left: 8px;
  cursor: pointer;
  color: #ff006a;
  font-size: 12px;
  font-weight: bold;
  transition: opacity 0.15s ease;
  user-select: none;
}
li:hover .mf-benefit-remove {
  opacity: 1;
}
.mf-benefit-add {
  display: list-item !important;
  list-style: none;
  margin-left: -18px;
  cursor: pointer;
  color: #a1a1aa;
  font-style: italic;
  font-size: 13px;
  padding-top: 4px;
  user-select: none;
  transition: color 0.15s ease;
}
.mf-benefit-add:hover {
  color: #ff006a;
}
/* Platzhalter-Tokens (z.B. {{FIRST_NAME}}) sind nicht editierbar, leicht hervorgehoben */
[data-mf-placeholder] {
  display: inline-block;
  background: rgba(99, 102, 241, 0.10);
  color: #4f46e5;
  border-radius: 3px;
  padding: 0 3px;
  font-size: 0.95em;
  cursor: not-allowed;
  user-select: none;
}
`;

// Das Script läuft im iframe (sandbox-frei dank srcDoc + same-origin) und nutzt window.parent.postMessage,
// um Edits an die Parent-Page zu kommunizieren. Wir verwenden hier KEINE TS-Syntax — der String wird
// 1:1 ins HTML geschrieben und vom Browser geparsed.
const INLINE_EDIT_SCRIPT = `
(function() {
  var EDIT_TYPE = 'mailforge:inline-edit';
  var ACTION_TYPE = 'mailforge:inline-action';
  var PLACEHOLDER_PATTERN = /(\\{\\{[A-Z0-9_]+\\}\\})/;

  // Walk Text-Nodes innerhalb eines editierbaren Slots und wrappe {{XXX}}-Tokens
  // in contenteditable=false-Spans, damit der User sie nicht versehentlich löschen kann.
  // Wir nutzen split(/(token)/) statt regex-stateful-Iteration — robuster und einfacher.
  function wrapPlaceholders(root) {
    var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
    var textNodes = [];
    var node;
    while ((node = walker.nextNode())) {
      if (node.parentElement && node.parentElement.hasAttribute('data-mf-placeholder')) continue;
      if (/\\{\\{[A-Z0-9_]+\\}\\}/.test(node.nodeValue || '')) {
        textNodes.push(node);
      }
    }
    textNodes.forEach(function(textNode) {
      var text = textNode.nodeValue || '';
      var parts = text.split(PLACEHOLDER_PATTERN);
      if (parts.length <= 1) return;
      var frag = document.createDocumentFragment();
      parts.forEach(function(part) {
        if (!part) return;
        var tokenMatch = part.match(/^\\{\\{([A-Z0-9_]+)\\}\\}$/);
        if (tokenMatch) {
          var span = document.createElement('span');
          span.setAttribute('contenteditable', 'false');
          span.setAttribute('data-mf-placeholder', tokenMatch[1]);
          span.textContent = part;
          frag.appendChild(span);
        } else {
          frag.appendChild(document.createTextNode(part));
        }
      });
      textNode.parentNode.replaceChild(frag, textNode);
    });
  }

  function setupSlot(el) {
    if (el.dataset.mfEditWired === '1') return;
    el.dataset.mfEditWired = '1';
    el.title = 'Klick zum Editieren · Enter speichert · Escape verwirft';

    // {{XXX}}-Tokens schützen, bevor das contenteditable-Verhalten startet
    wrapPlaceholders(el);

    var originalText = '';

    el.addEventListener('click', function(e) {
      if (el.getAttribute('contenteditable') === 'true') return;
      // Verhindert dass Klick auf editierbare Headline einen Link öffnet (falls in <a> verschachtelt)
      var anchor = el.closest('a');
      if (anchor) e.preventDefault();
      originalText = el.innerText;
      el.setAttribute('contenteditable', 'true');
      el.focus();
      // Caret ans Ende setzen statt Text zu selektieren — User-freundlicher
      var range = document.createRange();
      range.selectNodeContents(el);
      range.collapse(false);
      var sel = window.getSelection();
      sel.removeAllRanges();
      sel.addRange(range);
    });

    el.addEventListener('keydown', function(e) {
      if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault();
        el.blur();
      }
      if (e.key === 'Escape') {
        e.preventDefault();
        el.innerText = originalText;
        el.setAttribute('contenteditable', 'false');
        el.blur();
      }
    });

    el.addEventListener('blur', function() {
      if (el.getAttribute('contenteditable') !== 'true') return;
      el.setAttribute('contenteditable', 'false');
      var newValue = el.innerText.trim();
      if (newValue === originalText.trim()) return; // keine Änderung → kein Re-Render
      var key = el.getAttribute('data-edit-key');
      var idxStr = el.getAttribute('data-edit-index');
      var index = idxStr !== null ? parseInt(idxStr, 10) : undefined;
      window.parent.postMessage({
        type: EDIT_TYPE,
        key: key,
        index: index,
        value: newValue,
      }, '*');
    });
  }

  function setupActionMarker(el) {
    if (el.dataset.mfActionWired === '1') return;
    el.dataset.mfActionWired = '1';
    // Inline display:none entfernen → im Preview wird der Marker sichtbar.
    // Email-Clients haben das Script nicht und behalten display:none, also dort versteckt.
    el.style.display = '';
    el.addEventListener('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      var action = el.getAttribute('data-mf-action');
      var idxStr = el.getAttribute('data-edit-index');
      var index = idxStr !== null ? parseInt(idxStr, 10) : undefined;
      window.parent.postMessage({
        type: ACTION_TYPE,
        action: action,
        index: index,
      }, '*');
    });
  }

  // Auto-Height: meldet scrollHeight des iframes an den Parent, sodass der iframe
  // mit dem Mail-Content wächst und kein nested vertical scroll entsteht.
  // ResizeObserver fängt Größenänderungen (Bilder laden, CSS-Reflow);
  // MutationObserver fängt DOM-Mutationen (inline-edit fügt Bullet hinzu).
  // Debounce auf 50ms collapsed schnelle Bursts zu einem postMessage.
  var RESIZE_TYPE = 'mailforge:resize';
  var lastReportedHeight = 0;
  var resizeDebounceTimer = null;

  function reportHeight() {
    var h = document.documentElement.scrollHeight || document.body.scrollHeight || 0;
    if (h === lastReportedHeight) return;
    lastReportedHeight = h;
    window.parent.postMessage({ type: RESIZE_TYPE, height: h }, '*');
  }

  function scheduleHeightReport() {
    if (resizeDebounceTimer) clearTimeout(resizeDebounceTimer);
    resizeDebounceTimer = setTimeout(reportHeight, 50);
  }

  function setupAutoHeight() {
    // Initial-Report direkt
    reportHeight();
    // Bilder können nach Initial-Load die Höhe ändern → nochmal beim window.load
    window.addEventListener('load', scheduleHeightReport);
    if (typeof ResizeObserver !== 'undefined') {
      var ro = new ResizeObserver(scheduleHeightReport);
      ro.observe(document.body);
    }
    if (typeof MutationObserver !== 'undefined') {
      var mo = new MutationObserver(scheduleHeightReport);
      mo.observe(document.body, { childList: true, subtree: true, characterData: true });
    }
  }

  function init() {
    document.querySelectorAll('[data-edit-key]').forEach(setupSlot);
    document.querySelectorAll('[data-mf-action]').forEach(setupActionMarker);
    setupAutoHeight();
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();
`;

/**
 * Reichert HTML mit Inline-Editor-Style und -Script an.
 *
 * Heuristik für Insertion:
 *  - Style: in `<head>` direkt vor `</head>`
 *  - Script: am Body-Ende vor `</body>`
 *  - Falls kein head/body vorhanden → fallback: prepend bzw. append am Wurzel-HTML
 */
export function injectInlineEditorScript(html: string): string {
  const styleTag = `<style data-mf-inline-editor>${INLINE_EDIT_STYLE}</style>`;
  const scriptTag = `<script data-mf-inline-editor>${INLINE_EDIT_SCRIPT}</script>`;

  let modified = html;
  if (modified.includes('</head>')) {
    modified = modified.replace('</head>', `${styleTag}</head>`);
  } else {
    modified = styleTag + modified;
  }
  if (modified.includes('</body>')) {
    modified = modified.replace('</body>', `${scriptTag}</body>`);
  } else {
    modified = modified + scriptTag;
  }
  return modified;
}

/**
 * Postmessage-Payload-Type für Text-Edits. Wird vom Parent-Listener importiert für Type-Safety.
 */
export interface InlineEditMessage {
  type: 'mailforge:inline-edit';
  key: string;
  index: number | undefined;
  value: string;
}

/**
 * Postmessage-Payload-Type für strukturelle Aktionen (Add/Remove Bullet etc.).
 */
export interface InlineActionMessage {
  type: 'mailforge:inline-action';
  action: 'add-benefit' | 'remove-benefit';
  index: number | undefined;
}

/**
 * Type-Guard: prüft ob ein unbekanntes Event-Data-Object eine InlineEditMessage ist.
 */
export function isInlineEditMessage(data: unknown): data is InlineEditMessage {
  if (!data || typeof data !== 'object') return false;
  const d = data as Record<string, unknown>;
  return (
    d.type === 'mailforge:inline-edit' &&
    typeof d.key === 'string' &&
    typeof d.value === 'string'
  );
}

/**
 * Type-Guard: prüft ob ein unbekanntes Event-Data-Object eine InlineActionMessage ist.
 */
export function isInlineActionMessage(data: unknown): data is InlineActionMessage {
  if (!data || typeof data !== 'object') return false;
  const d = data as Record<string, unknown>;
  if (d.type !== 'mailforge:inline-action') return false;
  if (d.action !== 'add-benefit' && d.action !== 'remove-benefit') return false;
  if (d.action === 'remove-benefit' && typeof d.index !== 'number') return false;
  return true;
}

/**
 * Postmessage-Payload-Type für iframe-Auto-Height. Iframe meldet bei jedem Resize/Mutation
 * die scrollHeight, der Parent setzt iframe.style.height entsprechend.
 */
export interface InlineResizeMessage {
  type: 'mailforge:resize';
  height: number;
}

/**
 * Type-Guard: prüft ob ein unbekanntes Event-Data-Object eine InlineResizeMessage ist.
 */
export function isInlineResizeMessage(data: unknown): data is InlineResizeMessage {
  if (!data || typeof data !== 'object') return false;
  const d = data as Record<string, unknown>;
  return d.type === 'mailforge:resize' && typeof d.height === 'number' && d.height > 0;
}
