"""Tests for modules/vocab.py — vocabulary search and flashcard logic."""

import json
import sys
from pathlib import Path
from unittest.mock import patch

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent))

SAMPLE_VOCAB = [
    {
        "id": "verb_go",
        "section": "High-frequency language",
        "category": "Common verbs",
        "english": "to go",
        "persian": "رفتن",
        "finglish": "raftan",
    },
    {
        "id": "verb_eat",
        "section": "High-frequency language",
        "category": "Common verbs",
        "english": "to eat",
        "persian": "خوردن",
        "finglish": "khordan",
    },
    {
        "id": "adj_big",
        "section": "High-frequency language",
        "category": "Common adjectives",
        "english": "big",
        "persian": "بزرگ",
        "finglish": "bozorg",
    },
    {
        "id": "colour_red",
        "section": "High-frequency language",
        "category": "Colours",
        "english": "red",
        "persian": "قرمز",
        "finglish": "ghermez",
    },
]


@pytest.fixture(autouse=True)
def mock_vocab_and_db(tmp_path):
    """Mock vocabulary loading and use temp DB."""
    import db as db_mod
    import modules.vocab as vocab_mod

    # Temp DB
    db_mod._conn = None
    db_mod.DB_PATH = tmp_path / "test.db"
    db_mod.init_db()

    # Mock vocab
    vocab_mod._vocab_data = SAMPLE_VOCAB

    yield vocab_mod

    db_mod.close()
    vocab_mod._vocab_data = None


def test_load_vocab(mock_vocab_and_db):
    """load_vocab should return the vocabulary data."""
    data = mock_vocab_and_db.load_vocab()
    assert len(data) == 4


def test_get_categories(mock_vocab_and_db):
    """get_categories should return unique sorted categories."""
    cats = mock_vocab_and_db.get_categories()
    assert "Colours" in cats
    assert "Common verbs" in cats
    assert "Common adjectives" in cats


def test_search_english(mock_vocab_and_db):
    """Search should find entries by English text."""
    results = mock_vocab_and_db.search("go")
    assert len(results) == 1
    assert results[0]["id"] == "verb_go"


def test_search_persian(mock_vocab_and_db):
    """Search should find entries by Persian text."""
    results = mock_vocab_and_db.search("رفتن")
    assert len(results) == 1
    assert results[0]["id"] == "verb_go"


def test_search_finglish(mock_vocab_and_db):
    """Search should find entries by Finglish text."""
    results = mock_vocab_and_db.search("raftan")
    assert len(results) == 1
    assert results[0]["id"] == "verb_go"


def test_search_empty(mock_vocab_and_db):
    """Empty search should return empty list."""
    assert mock_vocab_and_db.search("") == []
    assert mock_vocab_and_db.search(None) == []


def test_search_no_match(mock_vocab_and_db):
    """Search with no match should return empty list."""
    assert mock_vocab_and_db.search("zzzzz") == []


def test_get_random_word(mock_vocab_and_db):
    """get_random_word should return a valid entry."""
    word = mock_vocab_and_db.get_random_word()
    assert word is not None
    assert "id" in word
    assert "english" in word
    assert "persian" in word


def test_get_random_word_with_category(mock_vocab_and_db):
    """get_random_word with category filter should only return matching entries."""
    word = mock_vocab_and_db.get_random_word(category="Colours")
    assert word is not None
    assert word["category"] == "Colours"


def test_get_random_word_nonexistent_category(mock_vocab_and_db):
    """get_random_word with bad category should return None."""
    word = mock_vocab_and_db.get_random_word(category="Nonexistent")
    assert word is None


def test_check_answer_correct_en_to_fa(mock_vocab_and_db):
    """Correct Persian answer should be marked correct."""
    correct, answer, entry = mock_vocab_and_db.check_answer(
        "verb_go", "رفتن", direction="en_to_fa"
    )
    assert correct is True


def test_check_answer_incorrect_en_to_fa(mock_vocab_and_db):
    """Incorrect Persian answer should be marked incorrect with correct answer."""
    correct, answer, entry = mock_vocab_and_db.check_answer(
        "verb_go", "خوردن", direction="en_to_fa"
    )
    assert correct is False
    assert answer == "رفتن"


def test_check_answer_fa_to_en(mock_vocab_and_db):
    """Correct English answer (case-insensitive) should be marked correct."""
    correct, answer, entry = mock_vocab_and_db.check_answer(
        "verb_go", "To Go", direction="fa_to_en"
    )
    assert correct is True


def test_check_answer_nonexistent_word(mock_vocab_and_db):
    """Checking answer for nonexistent word should return False."""
    correct, answer, entry = mock_vocab_and_db.check_answer(
        "nonexistent", "test", direction="en_to_fa"
    )
    assert correct is False
    assert entry is None


def test_format_word_card(mock_vocab_and_db):
    """format_word_card should produce RTL HTML with correct content."""
    entry = SAMPLE_VOCAB[0]
    html = mock_vocab_and_db.format_word_card(entry, show_transliteration="Finglish")
    assert "رفتن" in html
    assert "to go" in html
    assert "raftan" in html


def test_format_word_card_no_transliteration(mock_vocab_and_db):
    """format_word_card with transliteration off should not show finglish."""
    entry = SAMPLE_VOCAB[0]
    html = mock_vocab_and_db.format_word_card(entry, show_transliteration="off")
    assert "raftan" not in html


def test_get_flashcard_batch(mock_vocab_and_db):
    """get_flashcard_batch should return a batch of entries."""
    batch = mock_vocab_and_db.get_flashcard_batch(count=2)
    assert len(batch) == 2
    assert all("id" in e for e in batch)


def test_get_word_status_new(mock_vocab_and_db):
    """Unreviewed word should have status 'new'."""
    assert mock_vocab_and_db.get_word_status("verb_go") == "new"


def test_get_word_status_learning(mock_vocab_and_db):
    """Recently reviewed word should have status 'learning'."""
    import db
    import fsrs

    db.update_word_progress("verb_go", fsrs.Rating.Good)
    assert mock_vocab_and_db.get_word_status("verb_go") == "learning"
