// @ts-nocheck
import { ScryingPoolStrip } from './gm/ScryingPoolStrip.js';

/**
 * Reacts to state changes and applies visual state to AV tiles.
 * Constructs and manages the GM-only ScryingPoolStrip window.
 * Subscribes to Foundry Hooks after explicit `init()` call.
 */
export class RoleRenderer {
  /**
   * @param {object} stateStore - StateStore instance
   * @param {object} controller - ScryingPoolController instance
   * @param {object} avTileAdapter - AVTileAdapter instance
   * @param {object} adapter - FoundryAdapter instance
   * @param {object} [portraitFallbackHandler] - PortraitFallbackHandler instance (Story 4.2)
   */
  constructor(stateStore, controller, avTileAdapter, adapter, portraitFallbackHandler = null) {
    this._stateStore = stateStore;
    this._controller = controller;
    this._avTileAdapter = avTileAdapter;
    this._adapter = adapter;
    this._portraitFallbackHandler = portraitFallbackHandler;
    /** @type {ScryingPoolStrip|null} */
    this._strip = null;
  }

  /**
   * Returns the ScryingPoolStrip instance (GM only).
   * @returns {ScryingPoolStrip|null} The strip instance.
   */
  get strip() {
    return this._strip;
  }

  /**
   * Registers Hooks listeners. Must be called once during module ready.
   */
  init() {
    Hooks.on('scrying-pool:stateChanged', data => {
      if (data.userId) {
        this._applyAVTileState(data.userId, data.state);
      }
    });
    Hooks.on('scrying-pool:controllerAction', data => {
      this._onControllerAction(data);
    });
    Hooks.on('updateUser', () => {
      if (this._strip?.rendered) {
        this._strip.render(true);
      }
    });

    // Story 4.2: Refresh AV tile portrait when custom portrait changes
    if (this._portraitFallbackHandler) {
      this._portraitFallbackHandler.onPortraitChange((userId) => {
        const state = this._stateStore.getState(userId);
        if (state === 'never-connected' || state === 'cam-lost') {
          this._applyAVTileState(userId, state);
        }
      });
    }
  }

  /**
   * Applies visual state to an AV tile: state CSS class + lock overlay + portrait fallback.
   * @param {string} userId
   * @param {string} state
   */
  _applyAVTileState(userId, state) {
    this._avTileAdapter.setStateClass(userId, state);

    const HIDDEN = state === 'hidden';
    const CAMERA_ABSENT = state === 'never-connected' || state === 'cam-lost';

    if (HIDDEN) {
      const lockEl = document.createElement('div');
      lockEl.className = 'sp-lock-overlay';
      lockEl.dataset.spRole = 'lock-overlay';
      lockEl.title = 'Camera hidden by GM';
      this._avTileAdapter.mount(userId, lockEl);
    } else if (CAMERA_ABSENT) {
      // Story 4.2: Use PortraitFallbackHandler if available, otherwise fall back to generic div
      if (this._portraitFallbackHandler) {
        const portraitElement = this._portraitFallbackHandler.getFallbackImageElement(userId);
        this._avTileAdapter.mount(userId, portraitElement);
      } else {
        // Backward compatibility: generic fallback div
        const fallbackEl = document.createElement('div');
        fallbackEl.className = 'sp-portrait-fallback';
        fallbackEl.dataset.spRole = 'portrait-fallback';
        this._avTileAdapter.mount(userId, fallbackEl);
      }
    } else {
      this._avTileAdapter.unmount(userId);
    }
  }

  /**
   * Handles controller action events for pending op visual feedback.
   * @param {{ participantId: string, targetState: string, source: string }} data
   */
  _onControllerAction(data) {
    if (!data?.participantId) return;
    if (this._controller.hasPendingOp(data.participantId)) {
      this._avTileAdapter.setStateClass(data.participantId, 'pending');
    }
  }

  /**
   * Opens the ScryingPoolStrip window (GM only). Constructs lazily on first call.
   */
  openStrip() {
    if (!this._strip) {
      this._strip = new ScryingPoolStrip(
        this._stateStore,
        this._controller,
        this._avTileAdapter,
        this._adapter,
        { portraitFallbackHandler: this._portraitFallbackHandler }
      );
    }
    this._strip.render(true);
  }

  /**
   * Closes the ScryingPoolStrip window if open.
   */
  closeStrip() {
    if (this._strip) {
      this._strip.close();
    }
  }

  /**
   * Re-renders the strip if it is currently open.
   * Called by the dockLayout setting's onChange handler.
   */
  rerenderStrip() {
    if (this._strip?.rendered) {
      this._strip.render({ force: true });
    }
  }
}
