/**
 * @fileoverview Utility module for validators.js
 * 
 * @module games.mole-mash.validation.validators
 */

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

class MoleMashValidator extends BaseGameValidator {
  validate(history, configFase, gameConfig, sceneRef) {
    if (!configFase || Object.keys(configFase).length === 0) {
      console.error("Validação falhou: Configuração da fase não encontrada.");
      return this.failure("Erro Técnico: Fase não configurada.");
    }

    const sanityCheck = this.checkSanity(history, gameConfig);
    if (!sanityCheck.success) return sanityCheck;

    if (configFase.expectedSequence) {
      const sequenceCheck = configFase.ignoreSequenceOrder
        ? this.checkSequenceIgnoreOrder(
            history,
            configFase.expectedSequence,
            gameConfig,
          )
        : this.checkSequence(history, configFase.expectedSequence, gameConfig);

      if (!sequenceCheck.success) return sequenceCheck;
    }

    return this.validatePhase(history, configFase, gameConfig, sceneRef);
  }

  validatePhase(history, config, gameConfig) {
    // 1. Regra de Domínio: Verifica se saiu do tabuleiro (3x3)
    if (this.hasOutOfBounds(history)) {
      return this.failure(
        gameConfig?.mensagens?.foraTabuleiro || "Movimento fora do tabuleiro.",
      );
    }

    // 2. Regra de Configuração: Verifica Contagem Mínima de Hits
    if (config.requiredHits && history.length < config.requiredHits) {
      return this.failure(
        gameConfig.mensagens.faltamSaltos
          .replace("{requiredHits}", config.requiredHits)
          .replace("{actualHits}", history.length),
      );
    }

    // 3. Roteamento Dinâmico (Strategy Pattern)
    // Procura se existe um método com o nome "faseX" (ex: fase3, fase4)
    const phaseMethod = this[`fase${config.id}`];
    if (phaseMethod) {
      // Chama o método passando o contexto correto (this)
      return phaseMethod.call(this, history, config, gameConfig);
    }

    // Se passou por tudo e não tem regra específica, é sucesso.
    return this.success();
  }

  // --- Helpers Internos do MoleMash ---

  hasOutOfBounds(history) {
    return history.some(
      (h) =>
        (h.l !== undefined && (h.l < 1 || h.l > 3)) ||
        (h.c !== undefined && (h.c < 1 || h.c > 3)),
    );
  }

  checkSequenceIgnoreOrder(history, expected, gameConfig) {
    const historyToCompare = history.slice(0, expected.length);

    if (historyToCompare.length < expected.length) {
      return this.failure(
        gameConfig?.mensagens?.caminhoErrado || "Sequência incorreta.",
      );
    }

    const keys = Array.from(
      new Set(expected.flatMap((step) => Object.keys(step))),
    ).sort();

    const serialize = (item) =>
      keys.map((key) => `${key}:${item?.[key]}`).join("|");

    const expectedCount = new Map();
    expected.forEach((item) => {
      const key = serialize(item);
      expectedCount.set(key, (expectedCount.get(key) || 0) + 1);
    });

    const historyCount = new Map();
    historyToCompare.forEach((item) => {
      const key = serialize(item);
      historyCount.set(key, (historyCount.get(key) || 0) + 1);
    });

    const isMatch = Array.from(expectedCount.entries()).every(
      ([key, count]) => historyCount.get(key) === count,
    );

    return isMatch
      ? this.success()
      : this.failure(
          gameConfig?.mensagens?.caminhoErrado || "Sequência incorreta.",
        );
  }

  // --- Lógicas Específicas por Fase ---
  // Alternar entre Centro (2,2) e Cima (1,2)
  fase3(hist, config, gameConfig) {
    if (hist.length !== 10)
      return this.failure(gameConfig.mensagens.saltoInsuficiente);

    const isCenter = (m) => m.l === 2 && m.c === 2;
    const isTopCenter = (m) => m.l === 1 && m.c === 2;

    for (let i = 0; i < hist.length; i++) {
      const isEven = i % 2 === 0;
      const correct = isEven ? isCenter(hist[i]) : isTopCenter(hist[i]);

      if (!correct) {
        const local = isEven ? "centro (2,2)" : "cima (1,2)";
        return this.failure(
          gameConfig.mensagens.saltoErrado
            .replace("{numero}", i + 1)
            .replace("{local}", local),
        );
      }
    }
    return this.success();
  }

  // Visitar 9 buracos únicos
  fase4(hist, config, gameConfig) {
    const visitadas = new Set(hist.map((p) => `${p.l},${p.c}`));
    if (visitadas.size < 9) {
      return this.failure(
        gameConfig.mensagens.buracosFaltando.replace(
          "{visitados}",
          visitadas.size,
        ),
      );
    }
    return this.success();
  }

  // Apenas linha 2, mas várias colunas
  fase5(hist, config, gameConfig) {
    if (!hist.every((h) => h.l === 2))
      return this.failure(gameConfig.mensagens.linhaErrada);

    const colunas = new Set(hist.map((h) => h.c));
    if (colunas.size <= 1)
      return this.failure(gameConfig.mensagens.semMovimento);

    return this.success();
  }

  // Não repetir linha consecutiva (exceto linha 2)
  fase8(hist, config, gameConfig) {
    for (let i = 1; i < hist.length; i++) {
      if (hist[i].l === hist[i - 1].l && hist[i].l !== 2) {
        return this.failure(gameConfig.mensagens.repeticaoLinha);
      }
    }
    return this.success();
  }

  // Lógica complexa de "Não ficar parado" e "Não repetir centro"
  fase10(hist, config, gameConfig) {
    for (let i = 1; i < hist.length; i++) {
      const ant = hist[i - 1];
      const atual = hist[i];

      if (atual.l === ant.l && atual.c === ant.c)
        return this.failure(gameConfig.mensagens.mesmoLugar);

      if (ant.l === 2 && atual.l === 2)
        return this.failure(gameConfig.mensagens.linhaCentral);
    }
    return this.success();
  }
}

const validatorInstance = new MoleMashValidator();

export const validateSolution = (history, config, gameConfig) => {
  return validatorInstance.validate(history, config, gameConfig);
};
