// @ts-nocheck
/* global Dialog */
import { PortraitFallbackHandler } from '../../core/PortraitFallbackHandler.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() {}
      };

/**
 * Player Privacy Panel — UI component for players to control automation opt-ins.
 *
 * Extends ApplicationV2 via HandlebarsApplicationMixin.
 * Displays privacy settings and allows toggling (for own user) or read-only view (for other users).
 *
 * Import rule: may import from src/core/, src/foundry/, src/contracts/.
 * Constructors are side-effect free.
 *
 * @module ui/player/PlayerPrivacyPanel
 */

/**
 * Player Privacy Panel dialog for managing automation opt-in settings.
 */
export class PlayerPrivacyPanel extends _AppBase {
  static DEFAULT_OPTIONS = {
    id: 'scrying-pool-player-privacy-panel',
    classes: ['scrying-pool', 'player-privacy-panel'],
    window: {
      title: 'Player Privacy Panel',
      resizable: false,
      width: 380,
      height: 'auto',
    },
    position: {},
  };

  static PARTS = {
    dialog: {
      template: 'modules/scrying-pool/templates/player-privacy-panel.hbs',
    },
  };

  /**
   * @param {import('../../foundry/FoundryAdapter.js').FoundryAdapter} adapter
   *   Injected FoundryAdapter surface.
   * @param {import('../../core/PlayerPrivacyManager.js').PlayerPrivacyManager} playerPrivacyManager
   *   Injected PlayerPrivacyManager for privacy settings operations.
   * @param {import('../../core/PortraitFallbackHandler.js').PortraitFallbackHandler} [portraitFallbackHandler]
   *   Injected PortraitFallbackHandler for portrait operations (Story 4.2).
   * @param {string} targetUserId - The user ID whose settings are being viewed/edited.
   * @param {object} [options]
   * @throws {TypeError} If adapter or playerPrivacyManager is invalid.
   */
  constructor(adapter, playerPrivacyManager, targetUserId, portraitFallbackHandler = null, options = {}) {
    // Validate dependencies
    if (!adapter || typeof adapter !== 'object') {
      throw new TypeError(
        'PlayerPrivacyPanel: adapter argument is required and must be an object'
      );
    }
    if (!playerPrivacyManager || typeof playerPrivacyManager !== 'object') {
      throw new TypeError(
        'PlayerPrivacyPanel: playerPrivacyManager argument is required and must be an object'
      );
    }
    if (typeof targetUserId !== 'string' || targetUserId.length === 0) {
      throw new TypeError(
        'PlayerPrivacyPanel: targetUserId must be a non-empty string'
      );
    }

    super(options);

    this._adapter = adapter;
    this._playerPrivacyManager = playerPrivacyManager;
    this._portraitFallbackHandler = portraitFallbackHandler;
    this._targetUserId = targetUserId;

    // Cache for DOM elements
    /** @type {HTMLElement|null} */
    this._reactionCamToggle = null;
    /** @type {HTMLElement|null} */
    this._hpReactiveCamToggle = null;
    /** @type {HTMLElement|null} */
    this._fileInput = null;
    /** @type {HTMLElement|null} */
    this._portraitPreview = null;
    /** @type {boolean} */
    this._uploading = false;

    // Current settings state
    /** @type {import('../../contracts/privacy-settings.js').PrivacySettings|null} */
    this._currentSettings = null;
  }

  /**
   * Prepares the template context with i18n labels and current settings.
   * @returns {Promise<object>} Template context.
   */
  async _prepareContext() {
    const i18n = this._adapter.i18n;

    // Get current settings for the target user
    const settings = this._playerPrivacyManager.getSettings(this._targetUserId);
    this._currentSettings = settings;

    // Determine if this is the current user (editable) or another user (read-only)
    const currentUserId = this._adapter.users.current?.()?.id;
    const isOwnUser = this._targetUserId === currentUserId;
    const isReadOnly = !isOwnUser;

    // Story 4.2: Get portrait fallback data
    let portraitPreviewURL = null;
    let hasCustomPortrait = false;
    if (this._portraitFallbackHandler) {
      const custom = this._playerPrivacyManager.getPortraitFallback(this._targetUserId);
      hasCustomPortrait = custom !== null && custom !== undefined && custom !== '';
      portraitPreviewURL = hasCustomPortrait
        ? custom
        : this._portraitFallbackHandler.getFallbackImageURL(this._targetUserId);
    }

    return {
      // Panel metadata
      title: i18n.localize('SCRYING_POOL.PrivacyPanel.title'),
      sectionHeader: i18n.localize('SCRYING_POOL.PrivacyPanel.sectionHeader'),
      readOnlyNotice: i18n.localize('SCRYING_POOL.PrivacyPanel.readOnlyNotice'),

      // Automation effects
      automationEffects: [
        {
          key: 'reactionCam',
          label: i18n.localize('SCRYING_POOL.PrivacyPanel.reactionCamLabel'),
          description: i18n.localize('SCRYING_POOL.PrivacyPanel.reactionCamDescription'),
          enabled: settings.reactionCamEnabled,
          settingKey: 'reactionCamEnabled',
        },
      ],

      // Toggle labels
      toggleOnLabel: i18n.localize('SCRYING_POOL.PrivacyPanel.toggleOn'),
      toggleOffLabel: i18n.localize('SCRYING_POOL.PrivacyPanel.toggleOff'),

      // Story 4.2: Portrait fallback section
      hasPortraitSection: !!this._portraitFallbackHandler,
      portraitLabel: i18n.localize('SCRYING_POOL.PrivacyPanel.portraitFallbackLabel'),
      portraitDescription: i18n.localize('SCRYING_POOL.PrivacyPanel.portraitFallbackDescription'),
      chooseImageLabel: i18n.localize('SCRYING_POOL.PrivacyPanel.chooseImageLabel'),
      removeImageLabel: i18n.localize('SCRYING_POOL.PrivacyPanel.removeImageLabel'),
      hasCustomPortrait,
      portraitPreviewURL,

      // State
      isReadOnly,
      isOwnUser,
    };
  }

  /**
   * Sets up event handlers after rendering.
   * @param {HTMLElement|JQuery|object} context - ApplicationV2 context, jQuery object, or plain object with querySelector.
   */
  _onRender(context) {
    // ApplicationV2 passes the template context as the first argument, not the element.
    // Prefer this.element (the rendered root). Fall back to treating the argument as an element
    // (for jQuery objects or test mocks that expose querySelector directly).
    let element;
    if (this.element instanceof HTMLElement) {
      element = this.element;
    } else if (context instanceof HTMLElement) {
      element = context;
    } else {
      element = context?.[0] ?? context;
    }
    if (!element || typeof element.querySelector !== 'function') return;

    // Cache toggle elements
    this._reactionCamToggle = element.querySelector('[data-setting="reactionCamEnabled"]');

    // Sync badge text with actual checkbox state (Handlebars renders from context which may
    // be the default false state, while the real setting is already true in the DB).
    this._syncToggleBadges(element);

    // Story 4.2: Set up portrait section event handlers
    this._setupPortraitHandlers(element);

    // Set up toggle change handlers
    this._setupToggleHandlers(element);
  }

  /**
   * Syncs toggle badge text/icon with current checkbox state.
   * Needed because Handlebars renders from context at render time, which may not
   * match the actual persisted setting if it was already enabled.
   * @param {HTMLElement} element - The panel root element.
   * @private
   */
  _syncToggleBadges(element) {
    const onLabel = this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.toggleOn');
    const offLabel = this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.toggleOff');
    const toggles = element.querySelectorAll('.player-privacy-panel__toggle-input');
    for (const toggle of toggles) {
      const span = toggle.nextElementSibling;
      if (span?.classList.contains('player-privacy-panel__toggle-text')) {
        span.innerHTML = toggle.checked
          ? `<i class="fas fa-check" aria-hidden="true"></i> ${onLabel}`
          : `<i class="fas fa-times" aria-hidden="true"></i> ${offLabel}`;
      }
    }
  }

  /**
   * Sets up event handlers for toggle switches.
   * @param {HTMLElement} element - The dialog element.
   */
  _setupToggleHandlers(element) {
    const toggles = element.querySelectorAll('.player-privacy-panel__toggle input[type="checkbox"]');

    for (const toggle of toggles) {
      toggle.addEventListener('change', (event) => this._onToggleChange(event));
    }
  }

  /**
   * Sets up event handlers for portrait fallback section.
   * @param {HTMLElement} element - The dialog element.
   */
  _setupPortraitHandlers(element) {
    if (!this._portraitFallbackHandler || this._isReadOnlyMode()) {
      return;
    }

    // Cache portrait elements
    this._fileInput = element.querySelector('.player-privacy-panel__portrait-input');
    this._portraitPreview = element.querySelector('.player-privacy-panel__portrait-preview img');

    // Find and set up the choose image button
    const chooseButton = element.querySelector('.player-privacy-panel__portrait-choose');
    if (chooseButton) {
      chooseButton.addEventListener('click', () => this._onChooseImageClick());
    }

    // Find and set up the remove button (only shown when custom portrait exists)
    const removeButton = element.querySelector('.player-privacy-panel__portrait-remove');
    if (removeButton) {
      removeButton.addEventListener('click', () => this._onRemovePortraitClick());
    }

    // Set up file input change handler
    if (this._fileInput) {
      this._fileInput.addEventListener('change', (event) => this._onFileSelected(event));
    }
  }

  /**
   * Handles click on Choose Image button.
   * @private
   */
  _onChooseImageClick() {
    if (this._fileInput) {
      this._fileInput.click();
    }
  }

  /**
   * Handles file selection from the file picker.
   * @param {Event} event - The change event from the file input.
   * @private
   */
  async _onFileSelected(event) {
    if (this._uploading) return;
    const file = event.target?.files?.[0];
    if (!file) return;

    this._uploading = true;

    // Validate the file
    const validation = PortraitFallbackHandler.validatePortraitFile(file);
    if (!validation.valid) {
      this._adapter.notifications.error(validation.error);
      // Reset the file input
      if (this._fileInput) {
        this._fileInput.value = '';
      }
      this._uploading = false;
      return;
    }

    try {
      // Convert file to DataURL
      const dataURL = await PortraitFallbackHandler.fileToDataURL(file);

      // Validate the DataURL
      const result = this._playerPrivacyManager.setPortraitFallback(
        this._targetUserId,
        dataURL
      );

      // Wait for the async operation
      await result;

      // Update the preview and notification
      if (this._portraitPreview) {
        this._portraitPreview.src = dataURL;
      }

      // Reset the file input
      if (this._fileInput) {
        this._fileInput.value = '';
      }

      // Update our cached state
      if (this._currentSettings) {
        this._currentSettings.customPortraitFallback = dataURL;
      }

      // Show success notification
      this._adapter.notifications.info(
        this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.portraitSaved')
      );

      // Re-render to update the UI (show remove button, etc.)
      // Guard against stale render after panel has been closed
      if (this.rendered) {
        this.render(true);
      }
    } catch (err) {
      console.error('[ScryingPool] PlayerPrivacyPanel: failed to set portrait:', err);

      // Reset the file input
      if (this._fileInput) {
        this._fileInput.value = '';
      }

      // Show error notification
      if (err instanceof TypeError) {
        this._adapter.notifications.error(err.message);
      } else {
        this._adapter.notifications.error(
          this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.portraitErrorInvalid')
        );
      }
    } finally {
      this._uploading = false;
    }
  }

  /**
   * Handles click on Remove custom image button.
   * @private
   */
  async _onRemovePortraitClick() {
    if (this._isReadOnlyMode()) return;

    try {
      // Show confirmation dialog
      // Use FoundryVTT Dialog.confirm if available, fallback to browser confirm
      let confirmed;
      if (typeof Dialog !== 'undefined' && Dialog.confirm) {
        confirmed = await Dialog.confirm({
          title: this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.removeImageLabel'),
          content: this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.removeImageConfirm'),
        });
      } else {
        confirmed = window.confirm(
          this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.removeImageConfirm')
        );
      }

      if (!confirmed) return;

      // Remove the custom portrait
      await this._playerPrivacyManager.removePortraitFallback(this._targetUserId);

      // Update our cached state
      if (this._currentSettings) {
        this._currentSettings.customPortraitFallback = null;
      }

      // Show success notification
      this._adapter.notifications.info(
        this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.portraitRemoved')
      );

      // Re-render to update the UI (guard against stale render)
      if (this.rendered) {
        this.render(true);
      }
    } catch (err) {
      console.error('[ScryingPool] PlayerPrivacyPanel: failed to remove portrait:', err);
      this._adapter.notifications.error(
        this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.portraitErrorInvalid')
      );
    }
  }

  /**
   * Handles toggle change events.
   * @param {Event} event - The change event from the checkbox.
   */
  async _onToggleChange(event) {
    const checkbox = event.target;
    if (!checkbox || checkbox.type !== 'checkbox') return;

    const settingKey = checkbox.getAttribute('data-setting');
    const newValue = checkbox.checked;

    // Don't allow changes in read-only mode
    if (this._isReadOnlyMode()) {
      checkbox.checked = !newValue; // Revert the change
      return;
    }

    if (!settingKey) {
      console.warn('[ScryingPool] PlayerPrivacyPanel: toggle missing data-setting attribute');
      return;
    }

    try {
      // Update the setting via PlayerPrivacyManager
      await this._playerPrivacyManager.setSetting(
        this._targetUserId,
        settingKey,
        newValue
      );

      // Update our cached settings
      if (this._currentSettings) {
        this._currentSettings[settingKey] = newValue;
      }

      // Update toggle badge text+icon dynamically
      const span = checkbox.nextElementSibling;
      if (span?.classList.contains('player-privacy-panel__toggle-text')) {
        const onLabel = this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.toggleOn');
        const offLabel = this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.toggleOff');
        span.innerHTML = newValue
          ? `<i class="fas fa-check" aria-hidden="true"></i> ${onLabel}`
          : `<i class="fas fa-times" aria-hidden="true"></i> ${offLabel}`;
      }

      // Show success notification
      this._adapter.notifications.info(
        this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.savedNotification')
      );
    } catch (err) {
      // Revert the checkbox if the update failed
      checkbox.checked = !newValue;

      // Log error
      console.error('[ScryingPool] PlayerPrivacyPanel: failed to update setting:', err);

      // Show error notification
      if (err instanceof TypeError) {
        this._adapter.notifications.error(err.message);
      } else {
        this._adapter.notifications.error(
          this._adapter.i18n.localize('SCRYING_POOL.PrivacyPanel.saveError')
        );
      }
    }
  }

  /**
   * Checks if the panel is in read-only mode.
   * @returns {boolean} True if in read-only mode.
   * @private
   */
  _isReadOnlyMode() {
    const currentUserId = this._adapter.users.current?.()?.id;
    return this._targetUserId !== currentUserId;
  }

  /**
   * Cleans up event listeners.
   */
  _onClose() {
    // Clear cached elements
    this._reactionCamToggle = null;
    this._fileInput = null;
    this._portraitPreview = null;
    this._currentSettings = null;
  }
}
