/**
 * @fileoverview Utility module for game.js
 * 
 * @module games.cripto.game
 */

import Phaser from "phaser";
import { BaseGameScene } from "../../../shared/BaseGameScene.js";
import { setupCriptoAPI } from "./hooks/interpreterSetup.js";
import { validationSolution } from "./validation/validators.js";
import { gameConfig } from "./config/config.js";
import { ConstantesJogo } from "./ui/constants.js";
import { inicializarLayout } from "./ui/layout.js";
import {
  animarEntradaCaractere,
  animarNovoCaractereSaida,
} from "./ui/animations.js";
import backgroundLoopSound from "./assets/background_loop.mp3";

const CRIPTO_AUDIO = {
  BACKGROUND_LOOP: "cripto_background_loop",
};

class CriptoScene extends BaseGameScene {
  constructor() {
    super("CriptoScene");

    // Variáveis globais do jogo
    this.entrada = "";
    this.saida = "";
    this.contador = 0;

    // Textos visuais
    this.textoEntrada = null;
    this.textoSaida = null;

    // Animação Matrix
    this.colunasMatrix = null;
    this.matrixEffect = null;

    // Grid background animado
    this.gridBackground = null;

    // Monitor CRT
    this.crtMonitor = null;
  }

  /**
   * Inicializa a cena `CriptoScene` com dados opcionais.
   * @param {Object} data - Dados passados pela inicialização da cena
   * @returns {void}
   */
  init(data) {
    super.init(data);
    this.limparVariaveis();
  }

  /**
   * Faz o preload dos recursos necessários para o jogo (áudio, imagens).
   * Usa `preloadGlobalAssets` para recursos compartilhados.
   * @returns {void}
   */
  preload() {
    this.preloadGlobalAssets();
    this.load.audio(CRIPTO_AUDIO.BACKGROUND_LOOP, backgroundLoopSound);
  }
  
  /**
   * Cria elementos visuais da cena e configura o controlador padrão.
   * É responsável por inicializar o layout, textos e efeitos visuais.
   * @returns {void}
   */
  create() {
    this.setupStandardController(
      () => setupCriptoAPI(this, { animationSpeed: 100 }),
      (historico) =>
        validationSolution(historico, this.configFase, gameConfig, this),
    );

    // Inicializar layout visual
    const layout = inicializarLayout(this);
    this.textoEntrada = layout.textoEntrada;
    this.textoSaida = layout.textoSaida;
    this.colunasMatrix = layout.colunasMatrix;
    this.matrixEffect = layout.matrixEffect;
    this.gridBackground = layout.gridBackground;
    this.crtMonitor = layout.crtMonitor;
  }

  /**
   * Loop de atualização da cena, chamado pelo Phaser a cada frame.
   * Atualiza efeitos visuais dependentes de `time`/`delta`.
   * @param {number} time - Tempo atual do jogo
   * @param {number} delta - Intervalo em ms desde o último frame
   * @returns {void}
   */
  update(time, delta) {
    if (this.gridBackground) {
      this.gridBackground.update();
    }

    if (this.matrixEffect) {
      this.matrixEffect.update(delta);
    }
  }

  /**
   * Preparações feitas imediatamente antes da execução do código do aluno.
   * Reinicia histórico e variáveis, e inicia áudio de fundo.
   * @returns {void}
   */
  onBeforeRun() {
    this.historico = [];
    this.limparVariaveis();
    this.playAudio(CRIPTO_AUDIO.BACKGROUND_LOOP, { loop: true, volume: 0.5 });
  }

  /**
   * Ação executada quando a cena é resetada manualmente pelo usuário.
   * Deve restaurar estado visual e interromper áudios em execução.
   * @returns {void}
   */
  onReset() {
    this.limparVariaveis();
    this.stopAudio(CRIPTO_AUDIO.BACKGROUND_LOOP);
  }

  /**
   * Limpa variáveis de estado internas do jogo e reseta textos visuais.
   * @returns {void}
   */
  limparVariaveis() {
    this.entrada = "";
    this.saida = "";
    this.contador = 0;

    if (this.textoEntrada) this.textoEntrada.setText("");
    if (this.textoSaida) this.textoSaida.setText("");
  }

  /**
   * Define o valor de entrada do jogo e registra no histórico.
   * Retorna uma Promise que resolve quando animação (se houver) termina.
   * @param {string} valor - Novo valor de entrada
   * @returns {Promise<string>|Promise<void>}
   */
  definirEntrada(valor) {
    this.entrada = String(valor || "");
    this.historico.push({ tipo: "definir_entrada", valor: this.entrada });

    if (this.textoEntrada) {
      return animarEntradaCaractere(this, this.textoEntrada, this.entrada);
    }

    return Promise.resolve(this.entrada);
  }

  /**
   * Atualiza a string de saída e registra ação no histórico.
   * @param {string} valor - Novo conteúdo da saída
   * @returns {Promise<void>}
   */
  definirSaida(valor) {
    this.saida = String(valor || "");
    this.historico.push({ tipo: "definir_saida", valor: this.saida });

    if (this.textoSaida) {
      this.textoSaida.setText(this.saida);
    }

    return Promise.resolve();
  }

  /**
   * Ajusta o contador interno do jogo.
   * @param {number} valor - Valor numérico para o contador
   * @returns {Promise<void>}
   */
  definirContador(valor) {
    this.contador = Number(valor) || 0;
    this.historico.push({ tipo: "definir_contador", valor: this.contador });
    return Promise.resolve();
  }

  /**
   * Retorna o valor atual de entrada e registra a leitura no histórico.
   * @returns {string}
   */
  obterEntrada() {
    this.historico.push({ tipo: "obter_entrada", valor: this.entrada });
    return this.entrada;
  }

  /**
   * Retorna a string de saída atual e registra a leitura no histórico.
   * @returns {string}
   */
  obterSaida() {
    this.historico.push({ tipo: "obter_saida", valor: this.saida });
    return this.saida;
  }

  /**
   * Retorna o valor numérico do contador e registra a leitura.
   * @returns {number}
   */
  obterContador() {
    const valor = Number(this.contador);
    this.historico.push({ tipo: "obter_contador", valor: valor });
    return valor;
  }

  /**
   * Concatena texto à saída atual, animando a transição quando aplicável.
   * @param {string} valor - Valor a ser concatenado à saída
   * @returns {Promise<void>}
   */
  concatenarSaida(valor) {
    const valorString =
      valor !== null && valor !== undefined ? String(valor) : "";
    const saidaAnterior = this.saida;
    this.saida += valorString;
    this.historico.push({ tipo: "concatenar_saida", valor: this.saida });

    if (this.textoSaida && valorString) {
      return animarNovoCaractereSaida(
        this,
        this.textoSaida,
        saidaAnterior,
        this.saida,
      );
    }

    return Promise.resolve();
  }

  /**
   * Chamado quando a validação sinaliza sucesso. Limpa áudios/efeitos.
   * @returns {void}
   */
  onSuccess() {
    this.stopAudio(CRIPTO_AUDIO.BACKGROUND_LOOP);
  }

  /**
   * Chamado quando a validação sinaliza falha. Limpa áudios/efeitos.
   * @returns {void}
   */
  onFailure() {
    this.stopAudio(CRIPTO_AUDIO.BACKGROUND_LOOP);
  }

  /**
   * Destaca um bloco no workspace do Blockly e pausa a execução visual.
   * @param {string} id - Id do bloco a ser destacado
   * @returns {void}
   */
  highlightBlock(id) {
    if (this.workspace) this.workspace.highlightBlock(id);
    this.highlightPause = true;
  }

  
}

/**
 * Cria a configuração Phaser para o jogo Cripto.
 * Retorna o objeto de configuração usado por `new Phaser.Game(config)`
 * @param {HTMLElement} elementoPai - Elemento DOM que conterá o canvas Phaser
 * @param {Object} configFaseAtual - Configuração da fase atual
 * @returns {Object} Configuração Phaser para inicializar a cena Cripto
 */
export const createGame = (elementoPai, configFaseAtual) => {
  const scene = new CriptoScene();

  return {
    type: Phaser.AUTO,
    width: ConstantesJogo.LARGURA_TELA,
    height: ConstantesJogo.ALTURA_TELA,
    backgroundColor: ConstantesJogo.COR_FUNDO,
    parent: elementoPai,
    scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH },
    scene: scene,
    callbacks: {
      preBoot: function (game) {
        game.registry.set("configFase", configFaseAtual);
        game.registry.set("gameConfig", gameConfig);
      },
    },
  };
};


