/**
 * @fileoverview Utility module for integration.test.js
 * 
 * @module games.automato.__tests__.integration.test
 */

import { describe, it, expect, vi } from "vitest";
import { GameInterpreter } from "../../../../interpreters/GameInterpreter";
import { setupAutomatoAPI } from "../hooks/interpreterSetup";
import { validateSolution } from "../validation/validators";
import { gameConfig } from "../config/config";

// Mock de soluções para teste
const SOLUTIONS = {
  fase1: `
    moverParaFrente();
    moverParaFrente();
  `,
  fase1_fail: `
    moverParaFrente();
  `,
  fase2: `
    moverParaFrente();
    virarEsquerda();
    moverParaFrente();
    virarDireita();
    moverParaFrente();
  `,
  fase2_fail: `
    moverParaFrente();
    virarDireita();
    moverParaFrente();
  `,
  fase3: `
    while (!chegouNoAlvo()) {
      moverParaFrente();
    }
  `,
  fase3_fail: `
    moverParaFrente();
    moverParaFrente();
  `,
  fase4: `
    while (!chegouNoAlvo()) {
      moverParaFrente();
      virarEsquerda();
      moverParaFrente();
      virarDireita();
    }
  `,
  fase4_fail: `
    while (!chegouNoAlvo()) {
      moverParaFrente();
      virarEsquerda();
      virarDireita();
    }
  `,
  fase5: `
    moverParaFrente();
    moverParaFrente();
    virarEsquerda();
    while (!chegouNoAlvo()) {
      moverParaFrente();
    }
  `,
  fase5_fail: `
    while (!chegouNoAlvo()) {
      moverParaFrente();
      virarEsquerda();
      moverParaFrente();
      virarDireita();
    }
  `,
  fase6: `
    while (!chegouNoAlvo()) {
      if (haCaminho('frente')) {
        moverParaFrente();
      } else {
        virarEsquerda();
      }
    }
  `,
  fase6_fail: `
    while (!chegouNoAlvo()) {
      moverParaFrente();
    }
  `,
  fase7: `
    while (!chegouNoAlvo()) {
      if (haCaminho('esquerda')) {
        virarEsquerda();
        moverParaFrente();
      } else if (haCaminho('frente')) {
        moverParaFrente();
      } else {
        virarDireita();
      }
    }
  `,
  fase7_fail: `
    moverParaFrente();
    moverParaFrente();
    moverParaFrente();
  `,
  fase8: `
    while (!chegouNoAlvo()) {
      if (haCaminho('esquerda')) {
        virarEsquerda();
        moverParaFrente();
      } else if (haCaminho('frente')) {
        moverParaFrente();
      } else {
        virarDireita();
      }
    }
  `,
  fase8_fail: `
    while (!chegouNoAlvo()) {
      moverParaFrente();
      if (haCaminho("frente")) {
        virarEsquerda();
      }
      if (haCaminho("direita")) {
        virarDireita();
      }
    }
  `,
  fase9: `
    while (!chegouNoAlvo()) {
      if (haCaminho('esquerda')) {
        virarEsquerda();
        moverParaFrente();
      } else if (haCaminho('frente')) {
        moverParaFrente();
      } else {
        virarDireita();
      }
    }
  `,
  fase9_fail: `
    if (haCaminho("esquerda")) {
      moverParaFrente();
    } else {
      virarEsquerda();
    }
  `,
  fase10: `
    while (!chegouNoAlvo()) {
      if (haCaminho('esquerda')) {
        virarEsquerda();
        moverParaFrente();
      } else if (haCaminho('frente')) {
        moverParaFrente();
      } else if (haCaminho('direita')) {
        virarDireita();
        moverParaFrente();
      } else {
        virarDireita();
        virarDireita();
      }
    }
  `,
  fase10_fail: `
    if (haCaminho('esquerda')) {
      virarEsquerda();
      moverParaFrente();
    } else if (haCaminho('frente')) {
      moverParaFrente();
    }
  `,
};

// Constantes para tipos de tile
const TILE_TYPES = {
  PAREDE: 0,
  CAMINHO: 1,
  INICIO: 2,
  FIM: 3,
};

// Constantes para direções
const Direcao = {
  NORTE: 0,
  LESTE: 1,
  SUL: 2,
  OESTE: 3,
};

// Cena de teste que replica os métodos reais da AutomatoScene
class TestAutomatoScene {
  constructor(configFase) {
    this.mapa = configFase.mapa;
    this.historico = [];
    this.resultadoJogada = "em_andamento";
    this.configFase = configFase;

    this.posicaoInicial = this.encontrarPosicao(TILE_TYPES.INICIO);
    this.posicaoFinal = this.encontrarPosicao(TILE_TYPES.FIM);
    this.posicaoJogador = { ...this.posicaoInicial };
    this.direcaoJogador = Direcao.LESTE;

    this.pegmanSprite = {
      setPosition: vi.fn(),
      play: vi.fn(),
      setFrame: vi.fn(),
      on: vi.fn(),
      off: vi.fn(),
      x: 0,
      y: 0,
    };

    this.tweens = {
      add: vi.fn((config) => {
        if (config.onComplete) {
          setTimeout(config.onComplete, 0);
        }
        return { stop: vi.fn() };
      }),
    };

    this.sound = {
      play: vi.fn(),
      context: { state: "running", resume: vi.fn() },
    };

    this.anims = {
      exists: vi.fn(() => true),
    };
  }

  encontrarPosicao(tipo) {
    for (let y = 0; y < this.mapa.length; y++) {
      for (let x = 0; x < this.mapa[y].length; x++) {
        if (this.mapa[y][x] === tipo) return { x, y };
      }
    }
    return null;
  }

  atualizarVisualJogador() {
    if (this.posicaoJogador) {
      const TAMANHO_TILE = 50;
      const posX = this.posicaoJogador.x * TAMANHO_TILE + TAMANHO_TILE / 2;
      const posY = this.posicaoJogador.y * TAMANHO_TILE + TAMANHO_TILE / 2 - 6;
      this.pegmanSprite.setPosition(posX, posY);

      const animacoesDirecao = [
        "pegman_idle_norte",
        "pegman_idle_leste",
        "pegman_idle_sul",
        "pegman_idle_oeste",
      ];
      this.pegmanSprite.play(animacoesDirecao[this.direcaoJogador]);
    }
  }

  async moverParaFrente() {
    if (this.resultadoJogada !== "em_andamento") {
      return Promise.resolve();
    }

    // Proteção contra loops infinitos no teste
    if (this.historico.length > 500) {
      this.resultadoJogada = "falha";
      console.warn(
        "⚠️ Limite de ações atingido (500) - possível loop infinito",
      );
      return Promise.resolve();
    }

    let { x, y } = this.posicaoJogador;

    if (this.direcaoJogador === Direcao.NORTE) y--;
    else if (this.direcaoJogador === Direcao.LESTE) x++;
    else if (this.direcaoJogador === Direcao.SUL) y++;
    else if (this.direcaoJogador === Direcao.OESTE) x--;

    const proximoTile =
      this.mapa[y] && this.mapa[y][x] !== undefined ? this.mapa[y][x] : -1;

    if (proximoTile === TILE_TYPES.PAREDE || proximoTile === -1) {
      this.animarFalha();
      this.resultadoJogada = "falha";
      return Promise.resolve();
    } else {
      const TAMANHO_TILE = 50;
      const novaX = x * TAMANHO_TILE + TAMANHO_TILE / 2;
      const novaY = y * TAMANHO_TILE + TAMANHO_TILE / 2 - 6;

      return new Promise((resolve) => {
        this.tweens.add({
          targets: this.pegmanSprite,
          x: novaX,
          y: novaY,
          duration: 0,
          ease: "Power2",
          onComplete: () => {
            this.posicaoJogador = { x, y };
            this.historico.push({
              action: "moverParaFrente",
              position: { ...this.posicaoJogador },
              direction: this.direcaoJogador,
            });
            this.atualizarVisualJogador();
            resolve();
          },
        });
      });
    }
  }

  animarFalha() {
    this.pegmanSprite.play("pegman_fall");
  }

  async virarEsquerda() {
    const novaDirecao = (this.direcaoJogador + 3) % 4;
    await this.animarRotacao(novaDirecao);
    this.historico.push({
      action: "virarEsquerda",
      direction: this.direcaoJogador,
    });
    return Promise.resolve();
  }

  async virarDireita() {
    const novaDirecao = (this.direcaoJogador + 1) % 4;
    await this.animarRotacao(novaDirecao);
    this.historico.push({
      action: "virarDireita",
      direction: this.direcaoJogador,
    });
    return Promise.resolve();
  }

  async animarRotacao(novaDirecao) {
    this.direcaoJogador = novaDirecao;
    const nomesDirecoes = ["norte", "leste", "sul", "oeste"];
    const novaDirecaoNome = nomesDirecoes[novaDirecao];

    this.pegmanSprite.play(`pegman_idle_${novaDirecaoNome}`);
    this.atualizarVisualJogador();

    return Promise.resolve();
  }

  chegouNoAlvo() {
    // Para uso do código do usuário: sair do loop se falhou
    if (this.resultadoJogada === "falha") {
      return true;
    }

    return this.verificarChegadaReal();
  }

  // Método auxiliar para verificação real (usado pelo validator)
  verificarChegadaReal() {
    return (
      this.posicaoJogador.x === this.posicaoFinal.x &&
      this.posicaoJogador.y === this.posicaoFinal.y
    );
  }

  haCaminho(direcaoRelativa) {
    let direcaoAbsoluta = this.direcaoJogador;
    if (direcaoRelativa === "esquerda") {
      direcaoAbsoluta = (this.direcaoJogador + 3) % 4;
    } else if (direcaoRelativa === "direita") {
      direcaoAbsoluta = (this.direcaoJogador + 1) % 4;
    }

    let { x, y } = this.posicaoJogador;
    if (direcaoAbsoluta === Direcao.NORTE) y--;
    else if (direcaoAbsoluta === Direcao.LESTE) x++;
    else if (direcaoAbsoluta === Direcao.SUL) y++;
    else if (direcaoAbsoluta === Direcao.OESTE) x--;

    const proximoTile =
      this.mapa[y] && this.mapa[y][x] !== undefined ? this.mapa[y][x] : -1;
    return proximoTile !== TILE_TYPES.PAREDE && proximoTile !== -1;
  }

  highlightBlock() {}
}

describe("Automato - Integração de Lógica (Código -> Validação)", () => {
  let scene;
  let interpreter;

  const runFlow = async (code, phaseId) => {
    const configFase = gameConfig.fases.find((f) => f.id === phaseId);
    scene = new TestAutomatoScene(configFase);
    interpreter = new GameInterpreter({ stepDelay: 0, pauseExec: false });
    const api = setupAutomatoAPI(scene, { animationSpeed: 0 });
    await interpreter.executeCode(code, api);
    return validateSolution(scene.historico, configFase, gameConfig, scene);
  };

  it("Fase 1: Deve aprovar solução correta (Primeiro Passo)", async () => {
    const result = await runFlow(SOLUTIONS.fase1, 1);
    expect(result.success).toBe(true);
  });

  it("Fase 1: Deve reprovar solução incompleta", async () => {
    const result = await runFlow(SOLUTIONS.fase1_fail, 1);
    expect(result.success).toBe(false);
  });

  it("Fase 2: Deve aprovar solução correta (Primeira Curva)", async () => {
    const result = await runFlow(SOLUTIONS.fase2, 2);
    expect(result.success).toBe(true);
  });

  it("Fase 2: Deve reprovar direção errada", async () => {
    const result = await runFlow(SOLUTIONS.fase2_fail, 2);
    expect(result.success).toBe(false);
  });

  it("Fase 3: Deve aprovar loop correto (Linha Reta)", async () => {
    const result = await runFlow(SOLUTIONS.fase3, 3);
    expect(result.success).toBe(true);
  });

  it("Fase 3: Deve reprovar sem loop", async () => {
    const result = await runFlow(SOLUTIONS.fase3_fail, 3);
    expect(result.success).toBe(false);
  });

  it("Fase 4: Deve aprovar escadaria correta", async () => {
    const result = await runFlow(SOLUTIONS.fase4, 4);
    expect(result.success).toBe(true);
  });

  it("Fase 4: Deve reprovar movimento incompleto", async () => {
    const result = await runFlow(SOLUTIONS.fase4_fail, 4);
    expect(result.success).toBe(false);
  });

  it("Fase 5: Deve aprovar torre correta", async () => {
    const result = await runFlow(SOLUTIONS.fase5, 5);
    expect(result.success).toBe(true);
  });

  it("Fase 5: Deve reprovar sequência errada", async () => {
    const result = await runFlow(SOLUTIONS.fase5_fail, 5);
    expect(result.success).toBe(false);
  });

  it("Fase 6: Deve aprovar caminho com condicionais", async () => {
    const result = await runFlow(SOLUTIONS.fase6, 6);
    expect(result.success).toBe(true);
  });

  it("Fase 6: Deve reprovar caminho sem validação", async () => {
    const result = await runFlow(SOLUTIONS.fase6_fail, 6);
    expect(result.success).toBe(false);
  });

  it("Fase 7: Deve aprovar labirinto ramificado", async () => {
    const result = await runFlow(SOLUTIONS.fase7, 7);
    expect(result.success).toBe(true);
  });

  it("Fase 7: Deve reprovar prioridade errada", async () => {
    const result = await runFlow(SOLUTIONS.fase7_fail, 7);
    expect(result.success).toBe(false);
  });

  it("Fase 8: Deve aprovar labirinto complexo", async () => {
    const result = await runFlow(SOLUTIONS.fase8, 8);
    expect(result.success).toBe(true);
  });

  it("Fase 8: Deve reprovar lógica incompleta", async () => {
    const result = await runFlow(SOLUTIONS.fase8_fail, 8);
    expect(result.success).toBe(false);
  });

  it("Fase 9: Deve aprovar labirinto desafiador", async () => {
    const result = await runFlow(SOLUTIONS.fase9, 9);
    expect(result.success).toBe(true);
  });

  it("Fase 9: Deve reprovar lógica simplificada demais", async () => {
    const result = await runFlow(SOLUTIONS.fase9_fail, 9);
    expect(result.success).toBe(false);
  });

  it("Fase 10: Deve aprovar labirinto final", async () => {
    const result = await runFlow(SOLUTIONS.fase10, 10);
    expect(result.success).toBe(true);
  });

  it("Fase 10: Deve reprovar lógica incompleta no final", async () => {
    const result = await runFlow(SOLUTIONS.fase10_fail, 10);
    expect(result.success).toBe(false);
  });

  it("Deve detectar colisão com parede", async () => {
    const badCode = `virarEsquerda(); moverParaFrente();`;
    const result = await runFlow(badCode, 1);
    expect(result.success).toBe(false);
    expect(scene.resultadoJogada).toBe("falha");
  });

  it("Deve verificar chegouNoAlvo() corretamente", async () => {
    const configFase = gameConfig.fases.find((f) => f.id === 1);
    scene = new TestAutomatoScene(configFase);
    expect(scene.chegouNoAlvo()).toBe(false);
    scene.posicaoJogador = { ...scene.posicaoFinal };
    expect(scene.chegouNoAlvo()).toBe(true);
  });

  it("Deve verificar haCaminho() corretamente", async () => {
    const configFase = gameConfig.fases.find((f) => f.id === 1);
    scene = new TestAutomatoScene(configFase);
    expect(scene.haCaminho("frente")).toBe(true);
    expect(scene.haCaminho("esquerda")).toBe(false);
  });
}, 60000);
