import pytest

from multiplayer_crosswords.crossword import Crossword, CrosswordWord
from multiplayer_crosswords.dictionary import Dictionary, Word


def make_test_dictionary():
	d = Dictionary()
	d.add_word(Word("dog", hints=["bark"], difficulty=1.0))
	d.add_word(Word("cat", hints=["meow"], difficulty=1.0))
	return d


def test_extract_words_and_positions():
	# small 5x5 grid with two placed words: "dog" horizontal at (0,0)
	# and "cat" vertical starting at (4,1)
	grid = [
		["d", "o", "g", "#", "#"],
		["#", "#", "#", "#", "c"],
		["#", "#", "#", "#", "a"],
		["#", "#", "#", "#", "t"],
		["#", "#", "#", "#", "#"],
	]

	d = make_test_dictionary()

	cw = Crossword(dictionary=d, grid=grid)

	# Two words should be extracted
	words = cw.words
	assert len(words) == 2

	words_by_text = {w.word: w for w in words}
	assert "dog" in words_by_text and "cat" in words_by_text

	dog = words_by_text["dog"]
	assert dog.start_x == 0 and dog.start_y == 0
	assert dog.orientation.name == "HORIZONTAL"
	# Hint should come from dictionary
	assert dog.hint in ("bark",) or dog.hint.startswith("No hint available") is False

	cat = words_by_text["cat"]
	assert cat.start_x == 4 and cat.start_y == 1
	assert cat.orientation.name == "VERTICAL"

	# get_words_by_y_x_position uses x,y ordering
	by_pos = cw.get_words_by_y_x_position(0, 0)
	assert any(w.word == "dog" for w in by_pos)

	by_pos_cat = cw.get_words_by_y_x_position(4, 1)
	assert any(w.word == "cat" for w in by_pos_cat)


def test_place_letter_and_check_solved():
	grid = [
		["d", "o", "g", "#"],
		["#", "#", "#", "#"],
	]
	d = make_test_dictionary()
	cw = Crossword(dictionary=d, grid=grid)

	# Initially not solved
	dog = next(w for w in cw.words if w.word == "dog")
	assert dog.solved is False

	# Place letters one by one
	assert cw.place_letter(0, 0, "d") is True
	assert cw.place_letter(0, 1, "o") is True
	assert cw.place_letter(0, 2, "g") is True

	# Now the word should be marked solved
	assert dog.solved is True

	# Cannot place on a wall
	assert cw.place_letter(3, 0, "x") is False


def test_serialize_and_from_serialized_roundtrip():
	grid = [
		["d", "o", "g", "#"],
		["#", "#", "#", "#"],
	]
	d = make_test_dictionary()
	cw = Crossword(dictionary=d, grid=grid)

	data = cw.serialize()
	reconstructed = Crossword.from_serialized(data, dictionary=d)

	assert reconstructed is not None
	assert len(reconstructed.words) == len(cw.words)
	assert reconstructed.serialize()["solved_grid"] == cw.serialize()["solved_grid"]
