/**
 * @fileoverview Classe base para cenas de jogos Phaser na plataforma.
 *
 * `BaseGameScene` provê utilitários comuns: gerenciamento do `GameInterpreter`,
 * controle de execução, reprodução de áudio global, highlight de blocos e
 * integração com o `gameEventBus`.
 *
 * Subclasses devem implementar handlers específicos e sobrescrever hooks como
 * `onInit`, `onBeforeRun`, `onSuccess`, `onFailure` quando necessário.
 *
 * @module shared.BaseGameScene
 */

import Phaser from "phaser";
import { setupGameController } from "./gameController";
import { gameEventBus } from "../utils/gameEvents";
import { GameInterpreter } from "../interpreters/GameInterpreter";

// IMPORTAÇÃO DOS SONS GLOBAIS
import globalWinSound from "./../assets/win.mp3";
import globalFailSound from "./../assets/fail.mp3";

const GLOBAL_AUDIO = {
  WIN: "global_win",
  FAIL: "global_fail",
};

export class BaseGameScene extends Phaser.Scene {
  constructor(key) {
    super(key);
    this.historico = [];
    this.isRunning = false;
    this.workspace = null;
    this._validationTimer = null; // Referência ao timer de validação pendente
  }

  /**
   * Inicializa as configurações da cena.
   *
   * @param {Object} data - Dados passados à cena via `this.scene.start(key, data)`
   * @returns {void}
   */
  init(data) {
    const registry = this.registry.values;

    // Prioridade: Dados passados direto > Dados do Registry > Objeto Vazio
    this.configFase = data.configFase || registry.configFase || {};
    this.gameConfig = data.gameConfig || registry.gameConfig || {};
    this.customFailureHandler =
      data.customFailureHandler || registry.customFailureHandler;

    const delay = data.stepDelay || registry.stepDelay || 50;

    this.gameInterpreter = new GameInterpreter({
      stepDelay: delay,
      pauseExec: true,
    });

    this.historico = [];
    this.isRunning = false;

    if (this.onInit) this.onInit(data);
  }

  /**
   * Preload de assets globais (sons compartilhados entre jogos).
   * @returns {void}
   */
  preloadGlobalAssets() {
    this.load.audio(GLOBAL_AUDIO.WIN, globalWinSound);
    this.load.audio(GLOBAL_AUDIO.FAIL, globalFailSound);
  }

  /**
   * Pausa assíncrona em segundos, útil para fluxos de demonstração.
   * @param {number} segundos - Tempo em segundos para aguardar
   * @returns {Promise<void>}
   */
  async aguardar(segundos) {
    return new Promise((resolve) => {
      this.time.delayedCall(segundos * 1000, resolve);
    });
  }

  /**
   * Solicita highlight de um bloco no workspace (se existir).
   * @param {string|null} id - ID do bloco a ser destacado ou `null` para limpar
   * @returns {Promise<void>}
   */
  highlightBlock(id) {
    if (this.workspace) {
      this.workspace.highlightBlock(id);
    }
    return Promise.resolve();
  }

  /**
   * Toca um som global identificado pela chave.
   * @param {string} key - Chave do som (ex: GLOBAL_AUDIO.WIN)
   * @param {Object} [config] - Configurações de reprodução (volume, loop, etc.)
   * @returns {void}
   */
  playAudio(key, config = {}) {
    try {
      this.sound.play(key, config);
    } catch (error) {
      console.warn(`Falha ao tocar audio '${key}':`, error);
    }
  }

  /**
   * Para reprodução do som associado à `key`.
   * @param {string} key - Chave do som
   * @returns {void}
   */
  stopAudio(key) {
    try {
      this.sound.stopByKey(key);
    } catch (error) {
      console.warn(`Falha ao parar audio '${key}':`, error);
    }
  }

  /**
   * Configura handlers padrão de execução/reset/validação usando `setupGameController`.
   *
   * @param {Function} apiFactory - Factory que recebe `this` e retorna API usada pelo interpreter
   * @param {Function} validatorFunc - Função de validação que recebe (historico, configFase, gameConfig)
   * @returns {void}
   */
  setupStandardController(apiFactory, validatorFunc) {
    const executeHandler = async (codigo, workspace) => {
      if (this.sound.context.state === "suspended") {
        await this.sound.context.resume();
      }

      // PROTEÇÃO: Forçar cleanup se execução anterior ainda ativa
      if (this.isRunning || this.gameInterpreter.running) {
        console.warn('[BaseGameScene] Nova execução durante execução ativa, forçando cleanup');
        this.cleanupExecution();
        await new Promise(resolve => setTimeout(resolve, 50));
      }

      // Armazena o código para uso posterior pelos validators (ex: Aspirador Fase 5)
      this.currentCode = codigo;

      this.handleExecutionStart(workspace);

      // Validação de Estrutura (Regex)
      if (this.configFase.validationRegex) {
        if (!this.checkRegex(codigo)) {
          this.handleFailure(
            this.gameConfig.mensagens?.erroEstrutura ||
              "Erro estrutural no código.",
          );
          return;
        }
      }

      try {
        if (this.onBeforeRun) await this.onBeforeRun();

        const timeoutMs = (this.configFase.timeout || 15) * 1000;

        const executionResult = await Promise.race([
          this.gameInterpreter.executeCode(codigo, apiFactory(this)),
          new Promise((_, reject) =>
            this.time.delayedCall(timeoutMs, () =>
              reject(new Error("TIMEOUT")),
            ),
          ),
        ]);

        // BUG FIX: Não validar se o usuário interrompeu a execução.
        // Antes, a validação era agendada mesmo quando parada, e disparava
        // 1s depois sobre uma nova execução já em andamento — causando falha falsa.
        // Only abort validation when the *user* explicitly stopped execution.
        // 'stopped' means the game itself stopped the interpreter (e.g. Aspirador
        // calling stopInternal() after cleaning all dirt) — validation must still run.
        if (executionResult === 'stopped_by_user') {
          this.isRunning = false;
          return;
        }

        // Agendar validação com delay configurável (1000ms por padrão, 0 em testes)
        const validationDelay =
          this.configFase && this.configFase.delayValidacao !== undefined
            ? this.configFase.delayValidacao
            : 1000;

        if (validationDelay === 0) {
          // Executar imediatamente para testes
          this.handleValidation(validatorFunc);
        } else {
          // Agendar com delay para produção, guardando referência para cancelamento
          this._validationTimer = this.time.delayedCall(validationDelay, () => {
            this._validationTimer = null;
            this.handleValidation(validatorFunc);
          });
        }
      } catch (error) {
        console.error("Erro durante a execução:", error);
        const msgErro =
          error.message === "TIMEOUT"
            ? "Tempo limite de execução excedido."
            : "Erro inesperado na execução do código.";
        this.handleFailure(msgErro);
      }
    };

    const resetHandler = () => {
      this.cleanupExecution();
      if (this.onReset) this.onReset();
    };

    setupGameController(this, {
      onExecuteCode: executeHandler,
      onReset: resetHandler,
      onCheckSuccess: () => {},
    });
  }

  /**
   * Verifica o código com a regex configurada na fase (se houver).
   * @param {string} codigo - Código fonte a validar
   * @returns {boolean}
   */
  checkRegex(codigo) {
    try {
      const pattern = this.configFase.validationRegex;
      if (!pattern) return true;
      const regex = pattern instanceof RegExp ? pattern : new RegExp(pattern);
      return regex.test(codigo);
    } catch (e) {
      console.warn("Expressão regular inválida:", e);
      return true;
    }
  }

  /**
   * Inicializa estado antes de executar código: reseta histórico e marca execução ativa.
   * @param {*} workspace - Referência ao workspace Blockly/Editor usado na cena
   * @returns {void}
   */
  handleExecutionStart(workspace) {
    this.workspace = workspace;
    this.historico = [];
    this.isRunning = true;
    this.highlightBlock(null);
  }

  /**
   * Executa a validação usando o validatorFunc e dispara eventos/áudio conforme resultado.
   * Os hooks `onSuccess` e `onFailure` podem ser async: o evento React só é emitido
   * após a Promise resolver, permitindo animações antes do modal aparecer.
   * @param {Function} validatorFunc - Função que retorna { success: boolean, reason?: string }
   * @returns {Promise<void>}
   */
  async handleValidation(validatorFunc) {
    if (!this.isRunning) return;

    const resultado = validatorFunc(
      this.historico,
      this.configFase,
      this.gameConfig,
    );

    this.highlightBlock(null);

    if (resultado.success) {
      this.playAudio(GLOBAL_AUDIO.WIN);
      if (this.onSuccess) await this.onSuccess();
      gameEventBus.gameSuccess();
    } else {
      this.handleFailure(resultado.reason);
    }
    this.isRunning = false;
  }

  /**
   * Tratamento padrão de falha: reproduz áudio, aguarda onFailure (pode ser async)
   * e emite evento global após a animação de falha terminar.
   * @param {string} reason - Mensagem/razão da falha
   * @returns {Promise<void>}
   */
  async handleFailure(reason) {
    this.playAudio(GLOBAL_AUDIO.FAIL);

    if (this.onFailure) await this.onFailure();
    if (this.customFailureHandler) this.customFailureHandler(reason);

    gameEventBus.gameFailure(reason);
    this.isRunning = false;
  }

  /**
   * Limpa estado de execução (stop interpreter, limpa highlights, reseta flags)
   * @returns {void}
   */
  cleanupExecution() {
    // Cancelar timer de validação pendente para evitar validação stale
    if (this._validationTimer) {
      this._validationTimer.remove();
      this._validationTimer = null;
    }
    this.gameInterpreter.stop();
    this.highlightBlock(null);
    this.isRunning = false;
    this.historico = [];
  }
}
