import type { SolverStep } from '@wordle/shared';

const ALL_FEEDBACK: string[] = [];

function buildFeedbackPatterns(): string[] {
  if (ALL_FEEDBACK.length > 0) return ALL_FEEDBACK;
  const colors = ['g', 'y', 'x'];
  for (let i = 0; i < 243; i++) {
    let n = i; const p: string[] = [];
    for (let j = 0; j < 5; j++) { p.push(colors[n % 3]); n = Math.floor(n / 3); }
    ALL_FEEDBACK.push(p.join(''));
  }
  return ALL_FEEDBACK;
}

function getFeedback(target: string, guess: string): string {
  const tu = target.toLowerCase(), gu = guess.toLowerCase();
  const counts: Record<string, number> = {};
  for (const ch of tu) counts[ch] = (counts[ch] ?? 0) + 1;
  const r: string[] = Array(5).fill('x');
  for (let i = 0; i < 5; i++) { if (gu[i] === tu[i]) { r[i] = 'g'; counts[gu[i]]--; } }
  for (let i = 0; i < 5; i++) { if (r[i] === 'g') continue; if ((counts[gu[i]] ?? 0) > 0) { r[i] = 'y'; counts[gu[i]]--; } }
  return r.join('');
}

export function solveEntropy(target: string, allWords: string[], maxAttempts = 6): { attempts: number; steps: SolverStep[] } {
  buildFeedbackPatterns();
  let candidates = [...allWords];
  const steps: SolverStep[] = [];

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    let bestGuess = candidates[0];
    let bestEntropy = Infinity;
    const pool = attempt === 1 ? allWords : candidates;

    for (const guess of pool) {
      const buckets: Map<string, number> = new Map();
      for (const c of candidates) {
        const fb = getFeedback(c, guess);
        buckets.set(fb, (buckets.get(fb) ?? 0) + 1);
      }
      let expected = 0;
      for (const count of buckets.values()) expected += (count / candidates.length) * count;
      if (expected < bestEntropy) { bestEntropy = expected; bestGuess = guess; }
    }

    const fb = getFeedback(target, bestGuess);
    steps.push({ guess: bestGuess, colors: fb.split('') });

    if (fb === 'ggggg') return { attempts: attempt, steps };

    candidates = candidates.filter((c) => getFeedback(c, bestGuess) === fb);
    if (candidates.length === 0) return { attempts: maxAttempts + 1, steps };
  }

  return { attempts: maxAttempts + 1, steps };
}
