/**
 * @fileoverview Utility module for validators.js
 * 
 * @module games.semaforo.validation.validators
 */

import { BaseGameValidator } from "../../../../shared/BaseGameValidator";

/**
 * Validador para o jogo Semáforo
 *
 * @class SemaforoValidator
 * @extends BaseGameValidator
 *
 * @description
 * Responsável por validar as ações do usuário em cada fase do jogo.
 * Utiliza validação baseada em histórico de comandos executados,
 * verificando sequências de cores, tempos de espera, sincronização
 * de semáforos, piscar de luzes e controle de sons.
 *
 * Validações por fase:
 * - Fase 1: Sequência básica de cores (verde → amarelo → vermelho)
 * - Fase 2: Sequência com pausas entre mudanças
 * - Fase 3: Sincronização de semáforos de carros e pedestres
 * - Fase 4: Adiciona piscar da luz de pedestre
 * - Fase 5: Adiciona controle de sons
 */
export class SemaforoValidator extends BaseGameValidator {
  /**
   * Método principal de validação - delega para métodos específicos por fase
   *
   * @param {Array<Object>} history - Histórico de comandos executados
   * @param {Object} config - Configuração da fase atual
   * @param {Object} gameConfig - Configuração geral do jogo (inclui mensagens)
   * @param {Object} sceneRef - Referência à cena do jogo (opcional)
   * @returns {Object} Resultado da validação {success: boolean, reason?: string}
   */
  validatePhase(history, config, gameConfig, sceneRef) {
    // Verificar se tem histórico
    if (!history || history.length === 0) {
      return this.failure(
        gameConfig.mensagens?.semComandos || "Nenhum comando executado",
      );
    }

    // Roteamento por fase
    const phaseMethod = this[`validateFase${config.id}`];
    if (phaseMethod) {
      return phaseMethod.call(this, history, config, gameConfig);
    }

    return this.success();
  }

  /**
   * Fase 1: Sequência básica de cores do semáforo
   * Valida: verde → amarelo → vermelho
   *
   * @param {Array<Object>} history - Histórico de comandos
   * @param {Object} config - Configuração da fase (contém expectedSequence)
   * @param {Object} gameConfig - Config do jogo (contém mensagens)
   * @returns {Object} Resultado da validação
   *
   * @note Fase 1 injeta automaticamente aguardarSegundos(2) entre cada mudança
   * para melhorar a visualização, então ignoramos comandos 'aguardar' na validação
   */
  validateFase1(history, config, gameConfig) {
    // Extrair apenas comandos de semáforo (ignorar aguardares automáticos)
    const cores = history
      .filter((h) => h.tipo === "semaforo")
      .map((h) => h.cor);

    const esperado = config.expectedSequence || [
      "vermelho",
      "amarelo",
      "verde",
    ];

    if (cores.length !== esperado.length) {
      return this.failure(
        gameConfig.mensagens?.sequenciaIncorreta || "Sequência incompleta",
      );
    }

    const match = cores.every((cor, i) => cor === esperado[i]);
    return match
      ? this.success()
      : this.failure(
          gameConfig.mensagens?.sequenciaIncorreta ||
            "Sequência de cores incorreta",
        );
  }

  /**
   * Fase 2: Sequência com aguardar
   * Valida: sequência + pausas entre mudanças
   */
  validateFase2(history, config, gameConfig) {
    const esperado = config.expectedCommands;

    if (history.length !== esperado.length) {
      return this.failure(
        gameConfig.mensagens?.faltaAguardar ||
          "Comandos faltando ou em excesso",
      );
    }

    for (let i = 0; i < esperado.length; i++) {
      if (history[i].tipo !== esperado[i].tipo) {
        return this.failure(
          gameConfig.mensagens?.sequenciaIncorreta ||
            "Sequência de comandos incorreta",
        );
      }

      if (
        esperado[i].tipo === "semaforo" &&
        history[i].cor !== esperado[i].cor
      ) {
        return this.failure(
          gameConfig.mensagens?.sequenciaIncorreta ||
            "Cor do semáforo incorreta",
        );
      }
    }

    return this.success();
  }

  /**
   * Verifica se um comando do histórico satisfaz um comando esperado.
   * Compara apenas as propriedades presentes no esperado.
   *
   * @param {Object} h - Comando do histórico
   * @param {Object} expected - Comando esperado
   * @returns {boolean}
   */
  _commandMatches(h, expected) {
    if (h.tipo !== expected.tipo) return false;
    if (expected.cor !== undefined && h.cor !== expected.cor) return false;
    if (expected.seg !== undefined && h.seg !== expected.seg) return false;
    if (expected.som !== undefined && h.som !== expected.som) return false;
    return true;
  }

  /**
   * Valida o histórico contra um array de grupos ordenados.
   * Dentro de cada grupo, os comandos podem aparecer em qualquer ordem.
   * Os grupos devem aparecer na ordem definida.
   *
   * @param {Array<Object>} history - Histórico de comandos executados
   * @param {Array<Array<Object>>} groups - Grupos de comandos esperados
   * @returns {boolean}
   */
  _matchCommandGroups(history, groups) {
    const totalExpected = groups.reduce((sum, g) => sum + g.length, 0);
    if (history.length !== totalExpected) return false;

    let histIdx = 0;
    for (const group of groups) {
      const slice = history.slice(histIdx, histIdx + group.length);
      const usedIndices = new Set();
      for (const expected of group) {
        const foundIdx = slice.findIndex(
          (h, i) => !usedIndices.has(i) && this._commandMatches(h, expected),
        );
        if (foundIdx === -1) return false;
        usedIndices.add(foundIdx);
      }
      histIdx += group.length;
    }
    return true;
  }

  /**
   * Fase 3: Carros + pedestres
   * Valida: sincronização entre semáforos de carros e pedestres.
   * Suporta expectedCommandGroups (ordem livre dentro de cada grupo)
   * e expectedCommands (ordem estrita, retrocompatível).
   */
  validateFase3(history, config, gameConfig) {
    if (config.expectedCommandGroups) {
      return this._matchCommandGroups(history, config.expectedCommandGroups)
        ? this.success()
        : this.failure(
            gameConfig.mensagens?.pedestreErrado ||
              "Sincronização dos semáforos incorreta",
          );
    }

    const esperado = config.expectedCommands;

    if (history.length !== esperado.length) {
      return this.failure(
        gameConfig.mensagens?.pedestreErrado ||
          "Comandos faltando ou em excesso",
      );
    }

    for (let i = 0; i < esperado.length; i++) {
      if (history[i].tipo !== esperado[i].tipo) {
        return this.failure(
          gameConfig.mensagens?.sequenciaIncorreta ||
            "Tipo de comando incorreto",
        );
      }

      if (
        (history[i].tipo === "semaforo" || history[i].tipo === "pedestre") &&
        history[i].cor !== esperado[i].cor
      ) {
        return this.failure(
          gameConfig.mensagens?.pedestreErrado || "Cor incorreta",
        );
      }

      if (
        history[i].tipo === "aguardar" &&
        history[i].seg !== esperado[i].seg
      ) {
        return this.failure(
          gameConfig.mensagens?.faltaAguardar || "Tempo de espera incorreto",
        );
      }
    }

    return this.success();
  }

  /**
   * Fase 4: + piscar
   * Valida: inclui piscar da luz do pedestre.
   * Suporta expectedCommandGroups (ordem livre dentro de cada grupo)
   * e expectedCommands (ordem estrita, retrocompatível).
   */
  validateFase4(history, config, gameConfig) {
    if (config.expectedCommandGroups) {
      return this._matchCommandGroups(history, config.expectedCommandGroups)
        ? this.success()
        : this.failure(
            gameConfig.mensagens?.semPiscar ||
              "Sequência com piscar incorreta",
          );
    }

    const esperado = config.expectedCommands;

    if (history.length !== esperado.length) {
      return this.failure(
        gameConfig.mensagens?.semPiscar || "Comandos faltando ou em excesso",
      );
    }

    for (let i = 0; i < esperado.length; i++) {
      if (history[i].tipo !== esperado[i].tipo) {
        return this.failure(
          gameConfig.mensagens?.sequenciaIncorreta ||
            "Tipo de comando incorreto",
        );
      }

      if (
        (history[i].tipo === "semaforo" ||
          history[i].tipo === "pedestre" ||
          history[i].tipo === "piscar") &&
        history[i].cor !== esperado[i].cor
      ) {
        return this.failure(gameConfig.mensagens?.semPiscar || "Cor incorreta");
      }

      if (
        (history[i].tipo === "aguardar" || history[i].tipo === "piscar") &&
        history[i].seg !== esperado[i].seg
      ) {
        return this.failure(
          gameConfig.mensagens?.faltaAguardar || "Tempo incorreto",
        );
      }
    }

    return this.success();
  }

  /**
   * Fase 5: + sons
   * Valida: inclui controle de sons (tocar e parar).
   * Suporta expectedCommandGroups (ordem livre dentro de cada grupo)
   * e expectedCommands (ordem estrita, retrocompatível).
   */
  validateFase5(history, config, gameConfig) {
    if (config.expectedCommandGroups) {
      return this._matchCommandGroups(history, config.expectedCommandGroups)
        ? this.success()
        : this.failure(
            gameConfig.mensagens?.semSom || "Sequência com sons incorreta",
          );
    }

    const esperado = config.expectedCommands;

    if (history.length !== esperado.length) {
      return this.failure(
        gameConfig.mensagens?.semSom || "Comandos faltando ou em excesso",
      );
    }

    for (let i = 0; i < esperado.length; i++) {
      if (history[i].tipo !== esperado[i].tipo) {
        return this.failure(
          gameConfig.mensagens?.sequenciaIncorreta ||
            "Tipo de comando incorreto",
        );
      }

      if (
        (history[i].tipo === "semaforo" ||
          history[i].tipo === "pedestre" ||
          history[i].tipo === "piscar") &&
        history[i].cor !== esperado[i].cor
      ) {
        return this.failure(
          gameConfig.mensagens?.sequenciaIncorreta || "Cor incorreta",
        );
      }

      if (
        (history[i].tipo === "aguardar" || history[i].tipo === "piscar") &&
        history[i].seg !== esperado[i].seg
      ) {
        return this.failure(
          gameConfig.mensagens?.faltaAguardar || "Tempo incorreto",
        );
      }

      if (
        (history[i].tipo === "tocar" || history[i].tipo === "parar") &&
        history[i].som !== esperado[i].som
      ) {
        return this.failure(gameConfig.mensagens?.semSom || "Som incorreto");
      }
    }

    return this.success();
  }
}

/**
 * Função exportada para validação de soluções do Semáforo.
 * @param {Array} history - Histórico de ações
 * @param {Object} config - Configuração da fase
 * @param {Object} gameConfig - Configuração global do jogo
 * @param {Object} sceneRef - Referência à cena (opcional)
 * @returns {{success:boolean, reason?:string}}
 */
export function validateSolution(history, config, gameConfig, sceneRef) {
  const validator = new SemaforoValidator();
  return validator.validatePhase(history, config, gameConfig, sceneRef);
}
