import { assertNotEquals } from "https://deno.land/std@0.224.0/assert/assert_not_equals.ts";

import { Dict } from "../dict.ts";
import { enumerate } from "../utils.ts";
import { Gaming, GuessResult, Info } from "./game.ts";
import { assertEquals } from "https://deno.land/std@0.224.0/assert/assert_equals.ts";
import { assertExists } from "https://deno.land/std@0.224.0/assert/assert_exists.ts";

export class Simulator implements Gaming {
	word;

	constructor(word: string) {
		this.word = word;
	}

	static from_dict_rand(dict: Dict) {
		const index = Math.floor(Math.random() * dict.words.size);
		const word = [...dict.words.values()][index];
		return new Simulator(word);
	}

	guess(guess: string): GuessResult {
		if (guess === this.word) return { kind: "success" };
		const informations = info_of_guess(guess, this.word);
		return { kind: "failure", informations };
	}

	length(): number {
		return this.word.length;
	}
}

export function info_of_guess(guess: string, actual: string) {
	const actual_letters = [...actual];
	const infos = [...guess].map(() => null) as (Info | null)[];

	const used_letter_indices = new Set<number>();
	for (const [index, letter] of enumerate(guess)) {
		if (actual_letters[index] !== letter) continue;
		infos[index] = { kind: "there" };
		actual_letters[index] = ".";
		used_letter_indices.add(index);
	}

	for (const [index, letter] of enumerate(guess)) {
		assertNotEquals(letter, ".");
		if (used_letter_indices.has(index)) continue;
		if (!actual_letters.includes(letter)) {
			infos[index] = { kind: "abscent" };
			continue;
		}
		const actual_index = actual_letters.findIndex((i) => i === letter);
		assertNotEquals(actual_index, -1);
		infos[index] = { kind: "somewhere" };
		actual_letters[actual_index] = ".";
	}

	const infos_ = infos.map((i) => i === null ? ({ kind: "abscent" }) : i);
	for (const info of infos_) assertExists(info);
	return infos_ as Info[];
}

Deno.test("test_info_of_guess_peigne", () => {
	const guess_ = "soiree";
	const actual = "peigne";
	const constraints = [
		{ kind: "abscent" },
		{ kind: "abscent" },
		{ kind: "there" },
		{ kind: "abscent" },
		{ kind: "somewhere" },
		{ kind: "there" },
	] as Info[];
	assertEquals(info_of_guess(guess_, actual), constraints);
});

Deno.test("test_info_of_guess_genant", () => {
	const guess_ = "genant";
	const actual = "peigne";
	const constraints = [
		{ kind: "somewhere" },
		{ kind: "there" },
		{ kind: "abscent" },
		{ kind: "abscent" },
		{ kind: "there" },
		{ kind: "abscent" },
	] as Info[];
	assertEquals(info_of_guess(guess_, actual), constraints);
});

Deno.test("test_info_of_guess_arbre", () => {
	const guess_ = "resta";
	const actual = "arbre";
	const constraints = [
		{ kind: "somewhere" },
		{ kind: "somewhere" }, // <- "abscent" ; ptn ... gerté la lettre dans la mauvaise liste
		{ kind: "abscent" },
		{ kind: "abscent" },
		{ kind: "somewhere" },
	] as Info[];
	assertEquals(info_of_guess(guess_, actual), constraints);
});

Deno.test("test_info_of_guess_poncif", () => {
	const guess_ = "pacino";
	const actual = "poncif";
	const constraints = [
		{ kind: "there" },
		{ kind: "abscent" },
		{ kind: "somewhere" },
		{ kind: "somewhere" },
		{ kind: "somewhere" },
		{ kind: "somewhere" },
	] as Info[];
	assertEquals(info_of_guess(guess_, actual), constraints);
});
