/**
 * PresetExportDialog — Dialog for exporting scene presets to JSON file.
 *
 * Extends ApplicationV2 via HandlebarsApplicationMixin to provide FoundryVTT-native
 * dialog experience. Allows GM to download all presets as a JSON file.
 *
 * @module ui/gm/PresetExportDialog
 */

import { PresetImportExportManager } from '../../core/PresetImportExportManager.js';
import { escapeHtml } from '../../utils/html.js';

// Conditional base class — test environment lacks foundry globals.
// At module load time in tests, foundry is undefined → fallback class is used.

/** @private */
const _AppBase =
  typeof foundry !== 'undefined' &&
  foundry.applications?.api?.HandlebarsApplicationMixin &&
  foundry.applications?.api?.ApplicationV2
    ? foundry.applications.api.HandlebarsApplicationMixin(
        foundry.applications.api.ApplicationV2
      )
    : class _FallbackApp {
        static DEFAULT_OPTIONS = {};
        static PARTS = {};
        get rendered() { return this._rendered ?? false; }
        set rendered(v) { this._rendered = v; }
        get element() { return this._element ?? null; }
        set element(v) { this._element = v; }
        async render() { this._rendered = true; }
        async close() { this._rendered = false; }
        async _prepareContext() { return {}; }
        _onRender() {}
        _onClose() {}
        _onPosition() {}
      };

/**
 * Export dialog for scene presets.
 * Provides a simple interface with an export button that triggers file download.
 */
export class PresetExportDialog extends _AppBase {
  /**
   * @param {object} options - Dialog options.
   * @param {import('../../foundry/FoundryAdapter.js').FoundryAdapter} options.adapter - Foundry adapter.
   * @param {import('../../core/ScenePresetManager.js').ScenePresetManager} options.scenePresetManager - Scene preset manager.
   */
  constructor(options = {}) {
    super(options);

    if (!options || typeof options !== 'object') {
      throw new TypeError('PresetExportDialog: options argument is required and must be an object');
    }
    if (!options.adapter || typeof options.adapter !== 'object') {
      throw new TypeError('PresetExportDialog: adapter option is required and must be an object');
    }
    if (!options.scenePresetManager || typeof options.scenePresetManager !== 'object') {
      throw new TypeError('PresetExportDialog: scenePresetManager option is required and must be an object');
    }

    this._adapter = options.adapter;
    this._scenePresetManager = options.scenePresetManager;
    this._exportManager = new PresetImportExportManager(this._adapter, this._scenePresetManager);
    
    // Event listener tracking for cleanup
    /** @type {Array<{element: Element, type: string, listener: EventListener}>} */
    this._eventListeners = [];
  }

  static DEFAULT_OPTIONS = {
    id: 'scrying-pool-preset-export',
    classes: ['scrying-pool', 'dialog', 'preset-export'],
    window: { title: 'Export Scene Presets', resizable: false },
    position: { width: 400, height: 'auto' },
  };

  static PARTS = {
    dialog: {
      template: 'modules/scrying-pool/templates/preset-export.hbs',
    },
  };

  /**
   * Get data for template rendering.
   * @returns {Promise<object>} Template data.
   */
  async _prepareContext() {
    const presetCount = this._scenePresetManager.list().length;
    const currentScene = this._adapter.scenes.current?.();
    const sceneName = currentScene?.name ?? 'Unknown Scene';

    return {
      presetCount,
      sceneName,
      hasScene: !!currentScene,
      filename: this._exportManager.generateExportFilename(sceneName),
    };
  }

  /**
   * ApplicationV2 lifecycle — sets up event listeners on every render.
   * @inheritdoc
   */
  _onRender(context, options) {
    super._onRender?.(context, options);
    const root = this.element;
    if (!root) return;

    // Clean up previous event listeners to prevent memory leaks
    for (const { element, type, listener } of this._eventListeners) {
      element.removeEventListener(type, listener);
    }
    this._eventListeners = [];

    // Export button click handler
    const exportBtn = root.querySelector('.sp-export-btn');
    if (exportBtn) {
      const handler = async (event) => {
        event.preventDefault();
        await this._onExport();
      };
      exportBtn.addEventListener('click', handler);
      this._eventListeners.push({ element: exportBtn, type: 'click', listener: handler });
    }
  }

  /**
   * ApplicationV2 lifecycle — clean up event listeners when closed.
   * @inheritdoc
   */
  async _onClose(options) {
    await super._onClose?.(options);
    
    // Clean up event listeners
    for (const { element, type, listener } of this._eventListeners) {
      try {
        element.removeEventListener(type, listener);
      } catch {
        // Ignore errors during cleanup
      }
    }
    this._eventListeners = [];
    
    // Clean up any references
    this._exportManager = null;
    this._scenePresetManager = null;
    this._adapter = null;
  }

  /**
   * Handles the export action.
   * Exports all presets and triggers file download.
   * @returns {Promise<void>}
   * @private
   */
  async _onExport() {
    const btn = this.element?.querySelector('.sp-export-btn');
    if (!btn) return;

    const originalLabel = btn.innerHTML;

    try {
      // Disable button and show loading state (using DOM methods for safety)
      btn.disabled = true;
      btn.textContent = '';
      const spinner = document.createElement('i');
      spinner.className = 'fas fa-spinner fa-spin';
      const text = document.createTextNode(' ' + this._adapter.i18n.localize('scrying-pool.presetExport.exporting'));
      btn.appendChild(spinner);
      btn.appendChild(text);

      // Export presets
      const jsonString = await this._exportManager.exportAllPresets();
      const currentScene = this._adapter.scenes.current?.();
      const worldName = currentScene?.parent?.name ?? currentScene?.name ?? 'world';
      const filename = this._exportManager.generateExportFilename(worldName);

      // Trigger download
      this._exportManager.downloadExportFile(jsonString, filename);

      // Show success notification
      if (this._adapter.notifications) {
        this._adapter.notifications.info(this._adapter.i18n.localize('scrying-pool.presetExport.exportSuccess'));
      }

      // Close dialog
      this.close();
    } catch (err) {
      const errorMsg = err instanceof Error ? err.message : String(err);
      if (this._adapter.notifications) {
        this._adapter.notifications.error(this._adapter.i18n.localize('scrying-pool.presetExport.exportFailed') + ': ' + escapeHtml(errorMsg));
      }
    } finally {
      if (btn) {
        btn.disabled = false;
        btn.innerHTML = originalLabel;
      }
    }
  }
}
