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

import Phaser from "phaser";
import { BaseGameScene } from "../../../shared/BaseGameScene.js";
import { setupSemaforoAPI } from "./hooks/interpreterSetup.js";
import { validateSolution } from "./validation/validators.js";
import { gameConfig } from "./config/config.js";

import car from "./../../../assets/car.png";
import motoca from "./../../../assets/motoca.png";
import police from "./../../../assets/police.png";
import truck from "./../../../assets/truck.png";
import beep_sound from "./assets/beep.mp3";
import city_sound from "./assets/city_sound.mp3";

const ConstantesJogo = {
  LARGURA_TELA: 800,
  ALTURA_TELA: 600,
  COR_FUNDO: "#242527",
  DEBUG_FISICA: false,
  CAMADAS: {
    CENARIO: 0,
    VEICULOS: 1,
    PEDESTRES: 2,
    SEMAFORO_FUNDO: 10,
    SEMAFORO_LUZES: 11,
    UI: 20,
  },
  CHAVES_ASSETS: {
    imagens: {
      carro: "carro",
      motoca: "motoca",
      policia: "policia",
      caminhao: "caminhao",
      pedestre: "pedestre",
    },
    audio: { beep: "beep", som_cidade: "som_cidade" },
  },
  CORES: {
    semaforoPrincipal: {
      verdeAceso: 0x00ff00,
      verdeApagado: 0x005e00,
      amareloAceso: 0xffff00,
      amareloApagado: 0x5e5e00,
      vermelhoAceso: 0xff0000,
      vermelhoApagado: 0x5e0000,
    },
    semaforoPedestre: {
      fundoAceso: 0xff0000,
      fundoLiberado: 0x00ff00,
      figura: 0x000000,
      brilho: 0xffffff,
    },
    cenario: {
      calcada: 0x808080,
      faixaDePedestres: 0xffffff,
      faixaDePista: 0xffffff,
      posteSemaforo: 0x333333,
      caixaSemaforo: 0x1a1a1a,
    },
  },
  VELOCIDADE_VEICULO: -200,
  INTERVALO_SPAWN_VEICULO: 1200,
  INTERVALO_SPAWN_PEDESTRE: 500,
  VELOCIDADE_PEDESTRE: -450,
};

/**
 * Classe principal da cena do jogo Semáforo
 * @extends BaseGameScene
 */
class SemaforoScene extends BaseGameScene {
  constructor() {
    super("SemaforoScene");

    this.alturaBloco = 100;
    this.larguraBloco = 35;
    this.velocidadeQuedaBloco = ConstantesJogo.VELOCIDADE_VEICULO;
    this.configSemaforo = {
      coresSemaforoPrincipal: ConstantesJogo.CORES.semaforoPrincipal,
      coresSemaforoPedestre: ConstantesJogo.CORES.semaforoPedestre,
    };
    this.estadoSemaforo = { carros: "verde", pedestre: "vermelho" };

    this.veiculos = null;
    this.pedestres = null;
    this.timerPedestre = null;

    this.semaforoPrincipalContainer = null;
    this.luzVerde = null;
    this.luzAmarela = null;
    this.luzVermelha = null;
    this.semaforoPedestreEsquerda = null;
    this.semaforoPedestreDireita = null;
    this.botaoAtravessar = null;

    this.somCidade = null;
    this.somBeep = null;

    this.highlightPause = false;
    this.pausaDestaque = false;
    this.resultadoJogada = "em_andamento";

    this.larguraMargem = 0;
    this.faixaSuperiorY = 0;
    this.faixaInferiorY = 0;
    this.posicoesPista = [];
    this.texturasVeiculos = [];
  }

  /**
   * Inicializa a cena do Semáforo com dados vindos do registry/boot.
   * @param {Object} data - Dados opcionais passados por `scene.start` ou pelo `registry`
   * @returns {void}
   */
  init(data) {
    super.init(data);
  }

  /**
   * Preload dos assets específicos do jogo (imagens e áudios locais).
   * Também chama `preloadGlobalAssets` da `BaseGameScene` para sons compartilhados.
   * @returns {void}
   */
  preload() {
    this.preloadGlobalAssets();

    const { imagens, audio } = ConstantesJogo.CHAVES_ASSETS;
    this.load.image(imagens.carro, car);
    this.load.image(imagens.motoca, motoca);
    this.load.image(imagens.policia, police);
    this.load.image(imagens.caminhao, truck);
    this.load.audio(audio.beep, beep_sound);
    this.load.audio(audio.som_cidade, city_sound);

    const graficosPedestre = this.make.graphics({ add: false });
    graficosPedestre.fillStyle(0x0000ff, 1);
    graficosPedestre.fillCircle(15, 15, 15);
    graficosPedestre.generateTexture(imagens.pedestre, 30, 30);
    graficosPedestre.destroy();
  }

  /**
   * Cria a cena: configura elementos gráficos, grupos de física, timers
   * e registra o controller padrão que conecta o interpretador.
   * @returns {void}
   */
  create() {
    const larguraTela = this.sys.game.config.width;
    const alturaTela = this.sys.game.config.height;

    this.texturasVeiculos = ["carro", "motoca", "policia", "caminhao"];
    this.somCidade = this.sound.add(
      ConstantesJogo.CHAVES_ASSETS.audio.som_cidade,
      { loop: true, volume: 0.1 },
    );
    this.somBeep = this.sound.add(ConstantesJogo.CHAVES_ASSETS.audio.beep, {
      loop: true,
      volume: 0.3,
    });

    this.desenharCenario(larguraTela, alturaTela);
    this.criarSemaforoPrincipal();

    if (this.configFase.temPedestre) {
      this.criarSemaforosPedestres();
    }

    if (this.configFase.temBotaoPedreste) {
      this.criarBotaoAtravessar();
    }

    this.veiculos = this.physics.add.group();
    this.pedestres = this.physics.add.group();
    this.timerPedestre = null;

    this.time.addEvent({
      delay: ConstantesJogo.INTERVALO_SPAWN_VEICULO,
      callback: this.spawnVeiculo,
      callbackScope: this,
      loop: true,
    });

    this.atualizarEstadoSemaforos();

    this.setupStandardController(
      () => setupSemaforoAPI(this),
      (historico) =>
        validateSolution(historico, this.configFase, gameConfig, this),
    );
  }

  /**
   * Loop principal de atualização da cena (chamado a cada frame).
   * Limpa entidades que saem da tela e mantém o estado visual consistente.
   * @returns {void}
   */
  update() {
    this.veiculos.getChildren().forEach((veiculo) => {
      if (veiculo.y < -100) {
        veiculo.destroy();
      }
    });

    this.pedestres.getChildren().forEach((pedestre) => {
      if (pedestre.x < -20) {
        pedestre.destroy();
      }
    });
  }

  /**
   * Hook executado imediatamente antes de iniciar a execução do código do aluno.
   * Deve garantir que sons estejam prontos, limpar highlights e preparar o estado.
   * @returns {void}
   */
  onBeforeRun() {
    this.somCidade.stop();
    this.somBeep.stop();
    this.somCidade.play({ loop: true });

    this.highlightPause = false;
    this.resultadoJogada = "em_andamento";

    if (this.veiculos) {
      this.veiculos.getChildren().forEach((veiculo) => {
        veiculo.destroy();
      });
    }
  }

  /**
   * Hook chamado quando a cena é resetada manualmente (botão Reset).
   * Restaura estado visual sem alterar a configuração da fase.
   * @returns {void}
   */
  onReset() {
    this.resultadoJogada = "em_andamento";
    this.highlightPause = false;

    if (this.veiculos) {
      this.veiculos.getChildren().forEach((veiculo) => {
        veiculo.destroy();
      });
    }

    this.somCidade.stop();
    this.somBeep.stop();
  }

  /**
   * Handler chamado quando a validação indica sucesso.
   * Pode reproduzir som/animar vitória; aqui delega a lógica padrão da cena.
   * @returns {void}
   */
  onSuccess() {}

  /**
   * Handler chamado quando a validação indica falha.
   * Deve apresentar feedback ao jogador e limpar estados se necessário.
   * @returns {void}
   */
  onFailure() {}

  desenharCenario(larguraTela, alturaTela) {
    const percentualMargem = 0.1;
    this.larguraMargem = larguraTela * percentualMargem;
    const areaJogoX = this.larguraMargem;
    const larguraAreaJogo = larguraTela - this.larguraMargem * 2;
    const graficos = this.add.graphics({
      depth: ConstantesJogo.CAMADAS.CENARIO,
    });
    graficos
      .fillStyle(ConstantesJogo.CORES.cenario.calcada, 1)
      .fillRect(0, 0, this.larguraMargem, alturaTela)
      .fillRect(
        larguraTela - this.larguraMargem,
        0,
        this.larguraMargem,
        alturaTela,
      );
    graficos
      .fillStyle(0x000000, 0.4)
      .fillRect(this.larguraMargem - 5, 0, 5, alturaTela)
      .fillRect(larguraTela - this.larguraMargem, 0, 5, alturaTela);
    const centroY = alturaTela / 2;
    const alturaFaixa = alturaTela * 0.05 * 1.25;
    this.faixaSuperiorY = centroY - alturaFaixa;
    this.faixaInferiorY = centroY + alturaFaixa;
    graficos.fillStyle(ConstantesJogo.CORES.cenario.faixaDePedestres, 1);
    for (let i = 0; i <= Math.floor(larguraAreaJogo / 60); i++) {
      graficos.fillRect(
        areaJogoX + i * 60,
        this.faixaSuperiorY,
        30,
        this.faixaInferiorY - this.faixaSuperiorY,
      );
    }
    graficos.fillStyle(ConstantesJogo.CORES.cenario.faixaDePista, 0.5);
    const larguraPista = larguraAreaJogo / 4;
    this.posicoesPista = [
      areaJogoX + larguraPista * 0.5,
      areaJogoX + larguraPista * 1.5,
      areaJogoX + larguraPista * 2.5,
      areaJogoX + larguraPista * 3.5,
    ];
    for (let i = 1; i < 4; i++) {
      for (let y = 0; y < alturaTela; y += 65) {
        graficos.fillRect(areaJogoX + i * larguraPista - 4, y, 8, 40);
      }
    }
  }

  /**
   * Desenha o cenário base (calçadas, faixas e pistas) e inicializa métricas de layout.
   * @param {number} larguraTela
   * @param {number} alturaTela
   * @returns {void}
   */
  desenharCenario(larguraTela, alturaTela) {
    const percentualMargem = 0.1;
    this.larguraMargem = larguraTela * percentualMargem;
    const areaJogoX = this.larguraMargem;
    const larguraAreaJogo = larguraTela - this.larguraMargem * 2;
    const graficos = this.add.graphics({
      depth: ConstantesJogo.CAMADAS.CENARIO,
    });
    graficos
      .fillStyle(ConstantesJogo.CORES.cenario.calcada, 1)
      .fillRect(0, 0, this.larguraMargem, alturaTela)
      .fillRect(
        larguraTela - this.larguraMargem,
        0,
        this.larguraMargem,
        alturaTela,
      );
    graficos
      .fillStyle(0x000000, 0.4)
      .fillRect(this.larguraMargem - 5, 0, 5, alturaTela)
      .fillRect(larguraTela - this.larguraMargem, 0, 5, alturaTela);
    const centroY = alturaTela / 2;
    const alturaFaixa = alturaTela * 0.05 * 1.25;
    this.faixaSuperiorY = centroY - alturaFaixa;
    this.faixaInferiorY = centroY + alturaFaixa;
    graficos.fillStyle(ConstantesJogo.CORES.cenario.faixaDePedestres, 1);
    for (let i = 0; i <= Math.floor(larguraAreaJogo / 60); i++) {
      graficos.fillRect(
        areaJogoX + i * 60,
        this.faixaSuperiorY,
        30,
        this.faixaInferiorY - this.faixaSuperiorY,
      );
    }
    graficos.fillStyle(ConstantesJogo.CORES.cenario.faixaDePista, 0.5);
    const larguraPista = larguraAreaJogo / 4;
    this.posicoesPista = [
      areaJogoX + larguraPista * 0.5,
      areaJogoX + larguraPista * 1.5,
      areaJogoX + larguraPista * 2.5,
      areaJogoX + larguraPista * 3.5,
    ];
    for (let i = 1; i < 4; i++) {
      for (let y = 0; y < alturaTela; y += 65) {
        graficos.fillRect(areaJogoX + i * larguraPista - 4, y, 8, 40);
      }
    }
  }

  criarSemaforoPrincipal() {
    const { width } = this.sys.game.config;
    const { coresSemaforoPrincipal: cores } = this.configSemaforo;

    const graficos = this.add.graphics();
    graficos
      .fillStyle(ConstantesJogo.CORES.cenario.posteSemaforo, 1)
      .fillRect(-width / 2, -7, width, 10);
    graficos
      .fillStyle(ConstantesJogo.CORES.cenario.caixaSemaforo, 1)
      .fillRoundedRect(-70, -22, 140, 45, 8);

    this.luzVerde = this.add.circle(-40, 0, 14, cores.verdeApagado);
    this.luzAmarela = this.add.circle(0, 0, 14, cores.amareloApagado);
    this.luzVermelha = this.add.circle(40, 0, 14, cores.vermelhoApagado);

    this.semaforoPrincipalContainer = this.add.container(width / 2, 82, [
      graficos,
      this.luzVerde,
      this.luzAmarela,
      this.luzVermelha,
    ]);

    this.semaforoPrincipalContainer.setDepth(
      ConstantesJogo.CAMADAS.SEMAFORO_LUZES,
    );
    this.semaforoPrincipalContainer.setSize(140, 45);
  }

  /**
   * Cria os elementos visuais do semáforo principal (carros) e inicializa
   * as referências às luzes (verde, amarelo, vermelho).
   * @returns {void}
   */

  criarSemaforosPedestres() {
    const posicoes = [
      { x: this.larguraMargem / 2, y: this.faixaInferiorY - 100 },
      {
        x: this.sys.game.config.width - this.larguraMargem / 2,
        y: this.faixaInferiorY - 100,
      },
    ];
    const graficos = this.add.graphics({
      depth: ConstantesJogo.CAMADAS.SEMAFORO_FUNDO,
    });
    posicoes.forEach((pos) => {
      graficos.fillStyle(0x4a4a4a, 1).fillRect(pos.x - 8, pos.y - 50, 16, 50);
      graficos
        .fillStyle(ConstantesJogo.CORES.cenario.caixaSemaforo, 1)
        .fillRect(pos.x - 20, pos.y - 80, 40, 35);
    });
    this.semaforoPedestreEsquerda = this.criarConteudoSemaforoPedestre(
      posicoes[0].x,
      posicoes[0].y - 62,
    );
    this.semaforoPedestreDireita = this.criarConteudoSemaforoPedestre(
      posicoes[1].x,
      posicoes[1].y - 62,
    );
  }

  /**
   * Cria os semáforos de pedestre nas laterais da faixa e retorna os containers
   * com os componentes internos (fundo, figura, brilho).
   * @returns {void}
   */

  criarConteudoSemaforoPedestre(x, y) {
    const { coresSemaforoPedestre: cores } = this.configSemaforo;
    const fundo = this.add.circle(0, 0, 12);
    const figura = this.add.graphics();
    const brilho = this.add
      .circle(0, 0, 18, cores.brilho, 0.5)
      .setVisible(false);

    const container = this.add.container(x, y, [brilho, fundo, figura]);
    container.setDepth(ConstantesJogo.CAMADAS.SEMAFORO_LUZES);
    container.setData("componentes", { fundo, figura, brilho });
    return container;
  }

  /**
   * Desenha a figura do pedestre no gráfico fornecido, alternando entre
   * postura andando ou parado.
   * @param {PIXI.Graphics} graficos - Instância de gráficos do Phaser
   * @param {number} cor - Cor a ser usada para desenho (hex)
   * @param {boolean} estaAndando - Se true, desenha perna em movimento
   * @returns {void}
   */

  desenharFiguraPedestre(graficos, cor, estaAndando) {
    graficos.clear().fillStyle(cor, 1).lineStyle(4, cor, 1);
    graficos.fillCircle(0, -8, 4);
    graficos.beginPath().moveTo(0, -4).lineTo(0, 6).moveTo(-5, 0).lineTo(5, 0);
    if (estaAndando) {
      graficos.moveTo(0, 6).lineTo(-6, 12).moveTo(0, 6).lineTo(6, 12);
    } else {
      graficos.moveTo(0, 6).lineTo(-4, 12).moveTo(0, 6).lineTo(4, 12);
    }
    graficos.strokePath();
  }

  /**
   * Cria um botão de UI para simular pedido de travessia por pedestre.
   * O handler atual apenas registra o clique no console.
   * @returns {void}
   */

  criarBotaoAtravessar() {
    const { width, height } = this.sys.game.config;
    const estiloBotao = {
      fontSize: "24px",
      fill: "#000",
      fontStyle: "bold",
      backgroundColor: "#e0e0e0",
      padding: { x: 15, y: 8 },
    };
    this.botaoAtravessar = this.add
      .text(width / 2, height - 45, "Atravessar", estiloBotao)
      .setOrigin(0.5)
      .setInteractive({ useHandCursor: true })
      .setDepth(ConstantesJogo.CAMADAS.UI);
    this.botaoAtravessar.on("pointerdown", () =>
      console.log("Botão de travessia clicado!"),
    );
  }

  /**
   * Atualiza visualmente o estado dos semáforos (carros e pedestres)
   * e gerencia timers/respawn de pedestres conforme o estado.
   * @returns {void}
   */

  atualizarEstadoSemaforos() {
    const {
      coresSemaforoPrincipal: coresPrincipais,
      coresSemaforoPedestre: coresPedestre,
    } = this.configSemaforo;
    this.luzVerde.setFillStyle(
      this.estadoSemaforo.carros === "verde"
        ? coresPrincipais.verdeAceso
        : coresPrincipais.verdeApagado,
    );
    this.luzAmarela.setFillStyle(
      this.estadoSemaforo.carros === "amarelo"
        ? coresPrincipais.amareloAceso
        : coresPrincipais.amareloApagado,
    );
    this.luzVermelha.setFillStyle(
      this.estadoSemaforo.carros === "vermelho"
        ? coresPrincipais.vermelhoAceso
        : coresPrincipais.vermelhoApagado,
    );
    this.gerenciarTransicaoVeiculos(this.estadoSemaforo.carros === "vermelho");

    if (!this.configFase.temPedestre) return;

    [this.semaforoPedestreEsquerda, this.semaforoPedestreDireita].forEach(
      (semaforo) => {
        if (!semaforo) return;
        const componentes = semaforo.getData("componentes");
        if (!componentes) return;

        componentes.brilho.setVisible(true);
        if (this.estadoSemaforo.pedestre === "verde") {
          componentes.fundo.setFillStyle(coresPedestre.fundoLiberado);
          this.desenharFiguraPedestre(
            componentes.figura,
            coresPedestre.figura,
            true,
          );
          componentes.brilho.setFillStyle(coresPedestre.fundoLiberado);
        } else {
          componentes.fundo.setFillStyle(coresPedestre.fundoAceso);
          this.desenharFiguraPedestre(
            componentes.figura,
            coresPedestre.figura,
            false,
          );
          componentes.brilho.setFillStyle(coresPedestre.fundoAceso);
        }

        if (this.estadoSemaforo.pedestre === "verde") {
          if (!this.timerPedestre) {
            this.timerPedestre = this.time.addEvent({
              delay: ConstantesJogo.INTERVALO_SPAWN_PEDESTRE,
              callback: () => {
                this.spawnPedestre();
              },
              loop: true,
            });
          }
        } else if (this.estadoSemaforo.pedestre === "vermelho") {
          if (this.timerPedestre) {
            this.timerPedestre.remove(false);
            this.timerPedestre = null;
          }
        }
      },
    );
  }

  /**
   * Gerencia a transição de veículos: pausa ou libera o movimento conforme
   * o sinal de semáforo.
   * @param {boolean} devePausar - Indica se os veículos devem pausar.
   * @returns {void}
   */

  gerenciarTransicaoVeiculos(devePausar) {
    if (!this.sys || !this.sys.game || !this.sys.game.config) {
      return;
    }

    const margemParada = this.sys.game.config.height * 0.05;
    const posicaoParadaY =
      this.faixaInferiorY + this.alturaBloco / 2 + margemParada;
    this.veiculos.getChildren().forEach((veiculo) => {
      if (devePausar) {
        if (
          veiculo.body.velocity.y < 0 &&
          veiculo.y - this.alturaBloco / 2 > this.faixaInferiorY
        ) {
          veiculo.body.setVelocityY(0);
          this.tweens.add({
            targets: veiculo,
            y: posicaoParadaY,
            duration: 500,
            ease: "Power2",
          });

          /**
           * Cria um container de veículo com física e adiciona ao grupo de veículos.
           * @param {number} x - Posição x inicial
           * @param {number} y - Posição y inicial
           * @returns {Phaser.GameObjects.Container} Container criado para o veículo
           */
        }
      } else {
        if (veiculo.body.velocity.y === 0) {

          /**
           * Adiciona um veículo parado na pista (usado quando semáforo está vermelho).
           * Posiciona corretamente respeitando veículos já parados.
           * @returns {void}
           */
          veiculo.body.setVelocityY(this.velocidadeQuedaBloco);
        }
      }
    });
  }

  criarContainerVeiculo(x, y) {
    const textura = Phaser.Math.RND.pick(this.texturasVeiculos);
    const imagemVeiculo = this.add.image(0, 0, textura);
    let escalaBase = this.larguraBloco / imagemVeiculo.width;
    if (textura === "caminhao") imagemVeiculo.setScale(escalaBase * 1.2);
    else if (textura === "carro") imagemVeiculo.setScale(escalaBase * 0.9);
    else if (textura === "motoca") imagemVeiculo.setScale(escalaBase * 0.75);
    else imagemVeiculo.setScale(escalaBase);

    const container = this.add.container(x, y, [imagemVeiculo]);
    container.setSize(this.larguraBloco, this.alturaBloco);

    this.veiculos.add(container);
    this.physics.world.enable(container);
    container.body.setImmovable(true);

    container.setDepth(ConstantesJogo.CAMADAS.VEICULOS);

    return container;
  }

  /**
   * Spawna um veículo na pista de acordo com o estado do semáforo.
   * Não retorna valor; registros de ações são empurrados em `this.historico`.
   * @returns {void}
   */
  spawnVeiculo() {
    if (
      this.estadoSemaforo.carros === "verde" ||
      this.estadoSemaforo.carros === "amarelo"
    ) {
      const xPos = Phaser.Math.RND.pick(this.posicoesPista);
      const veiculo = this.criarContainerVeiculo(
        xPos,
        this.sys.game.config.height + 100,
      );
      if (veiculo) {
        veiculo.body.setVelocityY(this.velocidadeQuedaBloco);
      }
    } else if (this.estadoSemaforo.carros === "vermelho") {
      this.adicionarVeiculoParado();
    }
  }

  adicionarVeiculoParado() {
    const xPos = Phaser.Math.RND.pick(this.posicoesPista);
    const margemParada = this.sys.game.config.height * 0.05;
    let yAlvo = this.faixaInferiorY + this.alturaBloco / 2 + margemParada;
    const veiculosNaPista = this.veiculos
      .getChildren()
      .filter((v) => v.x === xPos && v.body.velocity.y === 0);
    if (veiculosNaPista.length > 0) {
      const ultimoVeiculo = veiculosNaPista.reduce((prev, current) =>
        prev.y > current.y ? prev : current,
      );
      yAlvo = ultimoVeiculo.y + this.alturaBloco + 10;
    }
    if (yAlvo > this.sys.game.config.height) return;
    const veiculo = this.criarContainerVeiculo(
      xPos,
      this.sys.game.config.height + 100,
    );
    if (veiculo) {
      veiculo.body.setEnable(false);
      this.tweens.add({
        targets: veiculo,
        y: yAlvo,
        duration: 800,
        ease: "Cubic.easeOut",
        onComplete: () => {
          veiculo.body.setEnable(true);
          veiculo.body.setVelocityY(0);
        },
      });
    }
  }

  /**
   * Spawna um pedestre em posição aleatória dentro da faixa configurada.
   * @returns {void}
   */
  spawnPedestre() {
    const larguraTela = this.sys.game.config.width;
    const yPos = Phaser.Math.Between(this.faixaSuperiorY, this.faixaInferiorY);
    const pedestre = this.pedestres.create(
      larguraTela + 20,
      yPos,
      ConstantesJogo.CHAVES_ASSETS.imagens.pedestre,
    );
    if (pedestre) {
      pedestre.setDepth(ConstantesJogo.CAMADAS.PEDESTRES);
      pedestre.setVelocityX(ConstantesJogo.VELOCIDADE_PEDESTRE);
    }
  }

  /**
   * Muda a cor do semáforo de carros
   * @param {string} cor - Cor do semáforo ('verde', 'amarelo', 'vermelho')
   * @returns {Promise<void>} Promise que resolve após delay (apenas fase 1)
   */
  mudarSemaforoCarros(cor) {
    this.historico.push({ tipo: "semaforo", cor });

    this.estadoSemaforo.carros = cor;
    this.atualizarEstadoSemaforos();

    if (this.configFase?.id === 1) {
      return new Promise((resolve) => {
        this.time.delayedCall(2000, resolve);
      });
    }
  }

  /**
   * Muda a cor do semáforo de pedestres
   * @param {string} cor - Cor do semáforo ('verde', 'vermelho')
   * @returns {Promise<void>} Promise que resolve após delay (apenas fase 1)
   */
  mudarSemaforoPedestre(cor) {
    this.historico.push({ tipo: "pedestre", cor });

    this.estadoSemaforo.pedestre = cor;
    this.atualizarEstadoSemaforos();

    if (this.configFase?.id === 1) {
      return new Promise((resolve) => {
        this.time.delayedCall(5000, resolve);
      });
    }
  }

  /**
   * Aguarda um tempo específico antes de continuar a execução
   * @param {number} segundos - Tempo de espera em segundos
   * @returns {Promise<void>} Promise que resolve após o tempo especificado
   */
  aguardarSegundos(segundos) {
    this.historico.push({ tipo: "aguardar", seg: segundos });

    return new Promise((resolve) => {
      this.time.delayedCall(segundos * 1000, resolve);
    });
  }

  /**
   * Toca um som do jogo
   * @param {string} nomeSom - Nome do som a tocar ('beep_semaforo' ou 'barulho_cidade')
   */
  tocarSom(nomeSom) {
    let som;
    if (nomeSom === "beep_semaforo") som = this.somBeep;
    else if (nomeSom === "barulho_cidade") som = this.somCidade;
    else return;

    this.historico.push({ tipo: "tocar", som: nomeSom });

    som.play();
  }

  /**
   * Para um som do jogo que está tocando
   * @param {string} nomeSom - Nome do som a parar ('beep_semaforo' ou 'barulho_cidade')
   */
  pararSom(nomeSom) {
    let som;
    if (nomeSom === "beep_semaforo") som = this.somBeep;
    else if (nomeSom === "barulho_cidade") som = this.somCidade;
    else return;

    this.historico.push({ tipo: "parar", som: nomeSom });

    som.stop();
  }

  /**
   * Faz a luz do semáforo de pedestre piscar por um tempo determinado
   * @param {string} cor - Cor da luz piscante ('verde' ou 'vermelho')
   * @param {number} duracaoSegundos - Duração do piscar em segundos (padrão: 5)
   * @returns {Promise<void>} Promise que resolve após o piscar terminar
   */
  async piscarLuzPedestre(cor = "vermelho", duracaoSegundos = 5) {
    this.historico.push({ tipo: "piscar", cor, seg: duracaoSegundos });

    if (this.timerPedestre) {
      this.timerPedestre.remove(false);
      this.timerPedestre = null;
    }

    const interval = 300;
    const total = Math.floor((duracaoSegundos * 1000) / interval);
    let visivel = true;

    [this.semaforoPedestreEsquerda, this.semaforoPedestreDireita].forEach(
      (semaforo) => {
        if (semaforo) {
          const componentes = semaforo.getData("componentes");
          if (componentes) {
            componentes.fundo.setFillStyle(
              cor === "verde"
                ? this.configSemaforo.coresSemaforoPedestre.fundoLiberado
                : this.configSemaforo.coresSemaforoPedestre.fundoAceso,
            );
            componentes.brilho.setFillStyle(
              cor === "verde"
                ? this.configSemaforo.coresSemaforoPedestre.fundoLiberado
                : this.configSemaforo.coresSemaforoPedestre.fundoAceso,
            );
          }
        }
      },
    );

    for (let i = 0; i < total; i++) {
      [this.semaforoPedestreEsquerda, this.semaforoPedestreDireita].forEach(
        (semaforo) => {
          if (semaforo) {
            const componentes = semaforo.getData("componentes");
            if (componentes) {
              componentes.fundo.setVisible(visivel);
              componentes.brilho.setVisible(visivel);
            }
          }
        },
      );
      visivel = !visivel;
      await new Promise((resolve) => this.time.delayedCall(interval, resolve));
    }

    [this.semaforoPedestreEsquerda, this.semaforoPedestreDireita].forEach(
      (semaforo) => {
        if (semaforo) {
          const componentes = semaforo.getData("componentes");
          if (componentes) {
            componentes.fundo.setVisible(true);
            componentes.brilho.setVisible(true);
          }
        }
      },
    );
  }

  destacarBloco(id) {
    if (this.workspace) this.workspace.highlightBlock(id);
    this.pausaDestaque = true;
  }


  /**
   * Destaca visualmente um bloco no workspace (API usada pelo interpretador).
   * @param {string} id - ID do bloco a destacar
   * @returns {void}
   */
  highlightBlock(id) {
    if (this.workspace) this.workspace.highlightBlock(id);
    this.highlightPause = true;
  }
}

/**
 * Fábrica que cria a configuração do Phaser para o jogo Semáforo.
 * É usada pelo componente `GameBase` para instanciar a cena com a `configFaseAtual`.
 * @param {HTMLElement} elementoPai - Elemento DOM que hospeda o canvas do jogo
 * @param {Object} configFaseAtual - Configuração da fase ativa (de `gameConfig.fases`)
 * @returns {Object} Configuração do Phaser usada para inicializar o jogo
 */
export const createGame = (elementoPai, configFaseAtual) => {
  const scene = new SemaforoScene();

  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 },
    antialias: true,
    roundPixels: true,
    pixelArt: false,
    physics: {
      default: "arcade",
      arcade: { gravity: { y: 0 }, debug: ConstantesJogo.DEBUG_FISICA },
    },
    scene: scene,
    callbacks: {
      preBoot: function (game) {
        game.registry.set("configFase", configFaseAtual);
        game.registry.set("gameConfig", gameConfig);
      },
    },
  };
};