import { VALID_GUESSES, WORDS_BY_LENGTH } from '../data/words';
import type { GuessResult, LetterState } from '../types';

export const normalizeGuess = (value: string) => value.toLowerCase().replace(/[^a-z]/g, '');

export const getDateKey = (date = new Date()) => date.toISOString().slice(0, 10);

const hashString = (value: string) => {
  let hash = 2166136261;
  for (let index = 0; index < value.length; index += 1) {
    hash ^= value.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return hash >>> 0;
};

export const seededIndex = (seed: string, length: number) => hashString(seed) % length;

export const getWordsByLength = (length: number) => WORDS_BY_LENGTH[length] ?? [];

export const isValidGuess = (guess: string, length: number) => {
  const clean = normalizeGuess(guess);
  return clean.length === length && VALID_GUESSES.has(clean);
};

export const pickDailyWord = (length: number, salt: string) => {
  const words = getWordsByLength(length);
  return words[seededIndex(`${getDateKey()}-${salt}-${length}`, words.length)];
};

export const pickSeededWord = (length: number, seed: string) => {
  const words = getWordsByLength(length);
  return words[seededIndex(`${seed}-${length}`, words.length)];
};

export const pickRandomWord = (length: number) => {
  const words = getWordsByLength(length);
  return words[Math.floor(Math.random() * words.length)];
};

export const getInfiniteLength = (level: number) => {
  if (level < 3) return 4;
  if (level < 7) return 5;
  if (level < 12) return 6;
  return 7;
};

export const getSuperEvilLength = () => 10 + Math.floor(Math.random() * 6);

export const evaluateGuess = (guess: string, target: string): GuessResult[] => {
  const cleanGuess = normalizeGuess(guess);
  const targetLetters = target.split('');
  const result: GuessResult[] = cleanGuess.split('').map((letter) => ({ letter, state: 'absent' }));
  const remaining = new Map<string, number>();

  targetLetters.forEach((letter, index) => {
    if (cleanGuess[index] === letter) {
      result[index].state = 'correct';
    } else {
      remaining.set(letter, (remaining.get(letter) ?? 0) + 1);
    }
  });

  result.forEach((entry, index) => {
    if (entry.state === 'correct') return;
    const count = remaining.get(entry.letter) ?? 0;
    if (count > 0) {
      result[index].state = 'present';
      remaining.set(entry.letter, count - 1);
    }
  });

  return result;
};

export const mergeKeyboardState = (guesses: string[], target: string) => {
  const rank: Record<LetterState, number> = { empty: 0, absent: 1, present: 2, correct: 3 };
  const state = new Map<string, LetterState>();

  guesses.forEach((guess) => {
    evaluateGuess(guess, target).forEach(({ letter, state: letterState }) => {
      const current = state.get(letter) ?? 'empty';
      if (rank[letterState] > rank[current]) {
        state.set(letter, letterState);
      }
    });
  });

  return state;
};
