/**
 * @fileoverview Engine de validação unificada para algoritmos de ordenação.
 *
 * Fluxo de validação:
 *   1. Valores preservados  — nenhum elemento foi perdido ou duplicado.
 *   2. Ordenação correta    — lista em ordem crescente.
 *   3. Algoritmo detectado  — a trilha é avaliada por todos os perfis registrados.
 *   4. Correspondência      — o algoritmo detectado deve bater com o esperado.
 *
 * Uso:
 *
 *   import { SortingValidationEngine } from "./SortingValidationEngine.js";
 *   import { bubbleProfile }   from "../profiles/bubbleProfile.js";
 *   import { selectionProfile } from "../profiles/selectionProfile.js";
 *   import { insertionProfile } from "../profiles/insertionProfile.js";
 *
 *   const engine = new SortingValidationEngine({
 *     bubble: bubbleProfile,
 *     selection: selectionProfile,
 *     insertion: insertionProfile,
 *   });
 *
 *   const result = engine.validate({
 *     initialList: [5, 2, 8, 1, 9],
 *     finalList: vm.toArray(),
 *     trace: vm.getTrace(),
 *     expectedAlgorithm: "bubble",
 *     messages: configFase.validation.messages,
 *   });
 *
 * Retorno:
 *   {
 *     success: boolean,
 *     message: string,
 *     detectedAlgorithm: string,
 *     checks: { valuesPreserved, sorted, algorithmMatch },
 *     debug: { compareCount, swapCount, writeCount, readCount, traceSize }
 *   }
 */

// ── Helpers ───────────────────────────────────────────────────────────────────

function checkValuesPreserved(initial, final) {
  if (initial.length !== final.length) return false;
  const a = [...initial].sort((x, y) => x - y);
  const b = [...final].sort((x, y) => x - y);
  return a.every((v, i) => v === b[i]);
}

function checkSorted(list) {
  return list.every((v, i) => i === 0 || list[i - 1] <= v);
}

function buildDebug(trace) {
  return {
    compareCount: trace.filter(e => e.type === "compare").length,
    swapCount:    trace.filter(e => e.type === "swap").length,
    writeCount:   trace.filter(e => e.type === "write").length,
    readCount:    trace.filter(e => e.type === "read").length,
    traceSize:    trace.length,
  };
}

// ── Engine ────────────────────────────────────────────────────────────────────

export class SortingValidationEngine {
  /**
   * @param {Record<string, (trace: TraceEvent[], n: number) => { match: boolean, failReason?: string }>} profiles
   *   Mapa de nome-do-algoritmo → função de perfil.
   *   A ordem das chaves define a prioridade de detecção.
   */
  constructor(profiles) {
    if (!profiles || Object.keys(profiles).length === 0) {
      throw new Error("SortingValidationEngine: ao menos um perfil de algoritmo é necessário.");
    }
    this._profiles = profiles;
  }

  /**
   * Valida a solução do aluno.
   *
   * @param {Object} params
   * @param {number[]} params.initialList       - Lista original antes da execução.
   * @param {number[]} params.finalList          - Lista após a execução do código do aluno.
   * @param {TraceEvent[]} params.trace          - Trilha capturada pela InstrumentedList.
   * @param {string} params.expectedAlgorithm    - Algoritmo cobrado pela fase (ex: "bubble").
   * @param {Object} [params.messages]           - Mensagens pedagógicas da fase (configFase.validation.messages).
   * @returns {ValidationResult}
   */
  validate({ initialList, finalList, trace, expectedAlgorithm, messages = {} }) {
    const n = initialList.length;
    const debug = buildDebug(trace);

    // ── 1) Valores preservados ─────────────────────────────────────────────
    if (!checkValuesPreserved(initialList, finalList)) {
      return {
        success: false,
        message: messages.valoresAlterados ?? "Algum valor foi perdido ou duplicado.",
        detectedAlgorithm: "unknown",
        checks: { valuesPreserved: false, sorted: false, algorithmMatch: false },
        debug,
      };
    }

    // ── 2) Ordenação final ─────────────────────────────────────────────────
    if (!checkSorted(finalList)) {
      return {
        success: false,
        message: messages.listaDesordenada ?? "A lista não ficou ordenada.",
        detectedAlgorithm: "unknown",
        checks: { valuesPreserved: true, sorted: false, algorithmMatch: false },
        debug,
      };
    }

    // ── 3) Detecção do algoritmo ───────────────────────────────────────────
    let detectedAlgorithm = "unknown";
    let detectionFailReason = null;

    for (const [algo, profileFn] of Object.entries(this._profiles)) {
      const result = profileFn(trace, n);
      if (result.match) {
        detectedAlgorithm = algo;
        break;
      }
      // Guarda o motivo de falha do perfil esperado, para mensagem mais precisa
      if (algo === expectedAlgorithm) {
        detectionFailReason = result.failReason ?? null;
      }
    }

    // ── 4) Correspondência ─────────────────────────────────────────────────
    const algorithmMatch = detectedAlgorithm === expectedAlgorithm;

    if (!algorithmMatch) {
      let message;
      if (detectedAlgorithm === "unknown") {
        // O perfil do algoritmo esperado explicou por que falhou
        message = detectionFailReason
          ?? messages.algoritmoNaoDetectado
          ?? `Não consegui identificar o padrão do algoritmo esperado (${expectedAlgorithm}).`;
      } else {
        const fn = messages.algoritmoErrado;
        message = typeof fn === "function"
          ? fn(detectedAlgorithm)
          : `A lista ficou ordenada, mas o padrão detectado foi "${detectedAlgorithm}", não "${expectedAlgorithm}".`;
      }

      return {
        success: false,
        message,
        detectedAlgorithm,
        checks: { valuesPreserved: true, sorted: true, algorithmMatch: false },
        debug,
      };
    }

    // ── Sucesso ────────────────────────────────────────────────────────────
    return {
      success: true,
      message: messages.success ?? "Correto!",
      detectedAlgorithm,
      checks: { valuesPreserved: true, sorted: true, algorithmMatch: true },
      debug,
    };
  }
}
