/**
 * ScriptBlocker - Blockiert Skripte bis Consent erteilt wird
 *
 * Verwendet das data-consent Attribut zur Identifikation von
 * Skripten, die erst nach Consent geladen werden duerfen.
 *
 * Beispiel:
 * <script data-consent="analytics" data-src="..." type="text/plain"></script>
 */

import type { ConsentConfig, ConsentCategory } from '../types';

/**
 * Script-Element mit Consent-Attributen
 */
interface ConsentScript extends HTMLScriptElement {
  dataset: DOMStringMap & {
    consent?: string;
    src?: string;
  };
}

/**
 * iFrame-Element mit Consent-Attributen
 */
interface ConsentIframe extends HTMLIFrameElement {
  dataset: DOMStringMap & {
    consent?: string;
    src?: string;
  };
}

/**
 * ScriptBlocker - Verwaltet Script-Blocking
 */
export class ScriptBlocker {
  private config: ConsentConfig;
  private observer: MutationObserver | null = null;
  private enabledCategories: Set<ConsentCategory> = new Set(['essential']);
  private processedElements: WeakSet<Element> = new WeakSet();

  constructor(config: ConsentConfig) {
    this.config = config;
  }

  /**
   * Initialisieren und Observer starten
   */
  init(): void {
    if (typeof window === 'undefined') {
      return;
    }

    // Bestehende Elemente verarbeiten
    this.processExistingElements();

    // MutationObserver fuer neue Elemente
    this.observer = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        for (const node of mutation.addedNodes) {
          if (node.nodeType === Node.ELEMENT_NODE) {
            this.processElement(node as Element);
          }
        }
      }
    });

    this.observer.observe(document.documentElement, {
      childList: true,
      subtree: true,
    });

    this.log('ScriptBlocker initialized');
  }

  /**
   * Kategorie aktivieren
   */
  enableCategory(category: ConsentCategory): void {
    if (this.enabledCategories.has(category)) {
      return;
    }

    this.enabledCategories.add(category);
    this.log('Category enabled:', category);

    // Blockierte Elemente dieser Kategorie aktivieren
    this.activateCategory(category);
  }

  /**
   * Kategorie deaktivieren
   */
  disableCategory(category: ConsentCategory): void {
    if (category === 'essential') {
      // Essential kann nicht deaktiviert werden
      return;
    }

    this.enabledCategories.delete(category);
    this.log('Category disabled:', category);

    // Hinweis: Bereits geladene Skripte koennen nicht entladen werden
    // Page-Reload noetig fuer vollstaendige Deaktivierung
  }

  /**
   * Alle Kategorien blockieren (ausser Essential)
   */
  blockAll(): void {
    this.enabledCategories.clear();
    this.enabledCategories.add('essential');
    this.log('All categories blocked');
  }

  /**
   * Pruefen ob Kategorie aktiviert
   */
  isCategoryEnabled(category: ConsentCategory): boolean {
    return this.enabledCategories.has(category);
  }

  /**
   * Observer stoppen
   */
  destroy(): void {
    this.observer?.disconnect();
    this.observer = null;
    this.log('ScriptBlocker destroyed');
  }

  // ===========================================================================
  // Internal Methods
  // ===========================================================================

  /**
   * Bestehende Elemente verarbeiten
   */
  private processExistingElements(): void {
    // Scripts mit data-consent
    const scripts = document.querySelectorAll<ConsentScript>(
      'script[data-consent]'
    );
    scripts.forEach((script) => this.processScript(script));

    // iFrames mit data-consent
    const iframes = document.querySelectorAll<ConsentIframe>(
      'iframe[data-consent]'
    );
    iframes.forEach((iframe) => this.processIframe(iframe));

    this.log(`Processed ${scripts.length} scripts, ${iframes.length} iframes`);
  }

  /**
   * Element verarbeiten
   */
  private processElement(element: Element): void {
    if (element.tagName === 'SCRIPT') {
      this.processScript(element as ConsentScript);
    } else if (element.tagName === 'IFRAME') {
      this.processIframe(element as ConsentIframe);
    }

    // Auch Kinder verarbeiten
    element
      .querySelectorAll<ConsentScript>('script[data-consent]')
      .forEach((script) => this.processScript(script));
    element
      .querySelectorAll<ConsentIframe>('iframe[data-consent]')
      .forEach((iframe) => this.processIframe(iframe));
  }

  /**
   * Script-Element verarbeiten
   */
  private processScript(script: ConsentScript): void {
    if (this.processedElements.has(script)) {
      return;
    }

    const category = script.dataset.consent as ConsentCategory | undefined;
    if (!category) {
      return;
    }

    this.processedElements.add(script);

    if (this.enabledCategories.has(category)) {
      this.activateScript(script);
    } else {
      this.log(`Script blocked (${category}):`, script.dataset.src || 'inline');
    }
  }

  /**
   * iFrame-Element verarbeiten
   */
  private processIframe(iframe: ConsentIframe): void {
    if (this.processedElements.has(iframe)) {
      return;
    }

    const category = iframe.dataset.consent as ConsentCategory | undefined;
    if (!category) {
      return;
    }

    this.processedElements.add(iframe);

    if (this.enabledCategories.has(category)) {
      this.activateIframe(iframe);
    } else {
      this.log(`iFrame blocked (${category}):`, iframe.dataset.src);
      // Placeholder anzeigen
      this.showPlaceholder(iframe, category);
    }
  }

  /**
   * Script aktivieren
   */
  private activateScript(script: ConsentScript): void {
    const src = script.dataset.src;

    if (src) {
      // Externes Script: neues Element erstellen
      const newScript = document.createElement('script');

      // Attribute kopieren
      for (const attr of script.attributes) {
        if (attr.name !== 'type' && attr.name !== 'data-src') {
          newScript.setAttribute(attr.name, attr.value);
        }
      }

      newScript.src = src;
      newScript.removeAttribute('data-consent');

      // Altes Element ersetzen
      script.parentNode?.replaceChild(newScript, script);

      this.log('External script activated:', src);
    } else {
      // Inline-Script: type aendern
      const newScript = document.createElement('script');

      for (const attr of script.attributes) {
        if (attr.name !== 'type') {
          newScript.setAttribute(attr.name, attr.value);
        }
      }

      newScript.textContent = script.textContent;
      newScript.removeAttribute('data-consent');

      script.parentNode?.replaceChild(newScript, script);

      this.log('Inline script activated');
    }
  }

  /**
   * iFrame aktivieren
   */
  private activateIframe(iframe: ConsentIframe): void {
    const src = iframe.dataset.src;
    if (!src) {
      return;
    }

    // Placeholder entfernen falls vorhanden
    const placeholder = iframe.parentElement?.querySelector(
      '.bp-consent-placeholder'
    );
    placeholder?.remove();

    // src setzen
    iframe.src = src;
    iframe.removeAttribute('data-src');
    iframe.removeAttribute('data-consent');
    iframe.style.display = '';

    this.log('iFrame activated:', src);
  }

  /**
   * Placeholder fuer blockierten iFrame anzeigen
   */
  private showPlaceholder(iframe: ConsentIframe, category: ConsentCategory): void {
    // iFrame verstecken
    iframe.style.display = 'none';

    // Placeholder erstellen
    const placeholder = document.createElement('div');
    placeholder.className = 'bp-consent-placeholder';
    placeholder.setAttribute('data-category', category);
    placeholder.innerHTML = `
      <div class="bp-consent-placeholder-content">
        <p>Dieser Inhalt erfordert Ihre Zustimmung.</p>
        <button type="button" class="bp-consent-placeholder-btn">
          ${this.getCategoryName(category)} aktivieren
        </button>
      </div>
    `;

    // Click-Handler
    const btn = placeholder.querySelector('button');
    btn?.addEventListener('click', () => {
      // Event dispatchen damit ConsentManager reagieren kann
      window.dispatchEvent(
        new CustomEvent('bp-consent-request', {
          detail: { category },
        })
      );
    });

    // Nach iFrame einfuegen
    iframe.parentNode?.insertBefore(placeholder, iframe.nextSibling);
  }

  /**
   * Alle Elemente einer Kategorie aktivieren
   */
  private activateCategory(category: ConsentCategory): void {
    // Scripts
    const scripts = document.querySelectorAll<ConsentScript>(
      `script[data-consent="${category}"]`
    );
    scripts.forEach((script) => this.activateScript(script));

    // iFrames
    const iframes = document.querySelectorAll<ConsentIframe>(
      `iframe[data-consent="${category}"]`
    );
    iframes.forEach((iframe) => this.activateIframe(iframe));

    this.log(
      `Activated ${scripts.length} scripts, ${iframes.length} iframes for ${category}`
    );
  }

  /**
   * Kategorie-Name fuer UI
   */
  private getCategoryName(category: ConsentCategory): string {
    const names: Record<ConsentCategory, string> = {
      essential: 'Essentielle Cookies',
      functional: 'Funktionale Cookies',
      analytics: 'Statistik-Cookies',
      marketing: 'Marketing-Cookies',
      social: 'Social Media-Cookies',
    };
    return names[category] ?? category;
  }

  /**
   * Debug-Logging
   */
  private log(...args: unknown[]): void {
    if (this.config.debug) {
      console.log('[ScriptBlocker]', ...args);
    }
  }
}

export default ScriptBlocker;
