/**
 * @fileoverview Perfil de detecção: Bubble Sort.
 *
 * Invariantes obrigatórios:
 *   1. TODAS as trocas são entre posições adjacentes (|i - j| === 1).
 *   2. A maioria das comparações é entre posições adjacentes
 *      (razão >= ADJACENT_COMPARE_THRESHOLD).
 *   3. O padrão de comparações é de varredura progressiva para frente
 *      (índices crescem dentro de cada passada).
 *
 * Variações aceitas (toleradas pela heurística):
 *   - Com ou sem flag de parada antecipada (otimização).
 *   - Loop externo crescente (i = 0..n-2) ou decrescente (limite diminuindo).
 *   - Poucas trocas (lista quase ordenada).
 *
 * O que NÃO é aceito:
 *   - Qualquer troca não adjacente.
 *   - Razão de comparações adjacentes abaixo do limiar.
 *   - Padrão de varredura para trás dentro da passada (sinal de Insertion Sort).
 */

const ADJACENT_COMPARE_THRESHOLD = 0.80;

/**
 * @param {import("../core/ExecutionTrace.js").TraceEvent[]} trace
 * @param {number} n - Tamanho da lista.
 * @returns {{ match: boolean, failReason?: string }}
 */
export function bubbleProfile(trace, n) {
  const compares = trace.filter(e => e.type === "compare");
  const swaps    = trace.filter(e => e.type === "swap");
  const reads    = trace.filter(e => e.type === "read");

  // Sem operações relevantes
  if (compares.length === 0 && swaps.length === 0) {
    return {
      match: false,
      failReason:
        "Nenhuma troca foi detectada. No Bubble Sort, quando há inversões, " +
        "o algoritmo deve realizar trocas entre pares vizinhos (j e j+1).",
    };
  }

  // Leitura mínima ajuda a filtrar soluções que apenas sobrescrevem a lista.
  if (swaps.length > 0 && reads.length === 0 && compares.length === 0) {
    return {
      match: false,
      failReason:
        "Não houve leituras da lista antes das trocas. Verifique se você está " +
        "comparando valores para decidir quando trocar elementos adjacentes.",
    };
  }

  // No modo real (sem compare explícito), exige evidência mínima de padrão Bubble.
  // Um único swap inferido pode aparecer como ruído em algoritmos de deslocamento.
  if (compares.length === 0 && swaps.length < 2) {
    return {
      match: false,
      failReason:
        "Poucas trocas para caracterizar Bubble Sort. " +
        "No Bubble, em listas não ordenadas, é esperado observar várias trocas adjacentes ao longo das passadas.",
    };
  }

  // Invariante 1: todas as trocas devem ser adjacentes
  const nonAdjacentSwap = swaps.find(e => Math.abs(e.i - e.j) !== 1);
  if (nonAdjacentSwap) {
    return {
      match: false,
      failReason:
        `Troca não adjacente detectada (posições ${nonAdjacentSwap.i} e ${nonAdjacentSwap.j}). ` +
        "O Bubble Sort só troca elementos que estão lado a lado — posições j e j+1.",
    };
  }

  // Invariante 2: razão de comparações adjacentes (quando compare existe)
  if (compares.length > 0) {
    const adjacentCompares = compares.filter(e => Math.abs(e.i - e.j) === 1);
    const adjacentRatio = adjacentCompares.length / compares.length;
    if (adjacentRatio < ADJACENT_COMPARE_THRESHOLD) {
      return {
        match: false,
        failReason:
          `Apenas ${Math.round(adjacentRatio * 100)}% das comparações foram entre posições adjacentes. ` +
          "O Bubble Sort percorre a lista comparando pares vizinhos (j e j+1) em cada passada.",
      };
    }
  }

  // Invariante 3: padrão de varredura para frente (detectar Insertion Sort swap-variant)
  // Conta pares consecutivos de comparações onde o índice cai (padrão de inserção)
  if (compares.length > 1) {
    let backwardPairs = 0;
    for (let k = 1; k < compares.length; k++) {
      const prev = Math.min(compares[k - 1].i, compares[k - 1].j);
      const curr = Math.min(compares[k].i, compares[k].j);
      if (curr < prev) backwardPairs++;
    }
    const backwardRatio = backwardPairs / (compares.length - 1);
    if (backwardRatio > 0.5) {
      return {
        match: false,
        failReason:
          "O padrão de comparações indica varredura para trás (Insertion Sort), não para frente. " +
          "O Bubble Sort percorre a lista da esquerda para a direita em cada passada.",
      };
    }
  }

  return { match: true };
}
