import pytest
from unittest.mock import MagicMock
from bson import ObjectId
from datetime import datetime
from app.repositories import QuizRepository
from app.models.entities import QuizEntity


@pytest.fixture
def mock_db():
    return MagicMock()


@pytest.fixture
def repository(mock_db):
    return QuizRepository(db=mock_db)


def generate_quiz_entity(**kwargs) -> QuizEntity:
    return QuizEntity(
        subject_id=kwargs.get("subject_id", "subject1"),
        title=kwargs.get("title", "Kuis Sejarah Indonesia"),
        description=kwargs.get(
            "description", "Soal mengenai peristiwa penting Indonesia"
        ),
        is_public=kwargs.get("is_public", True),
        date=kwargs.get("date", datetime.now()),
        total_quiz=kwargs.get("total_quiz", 10),
        limit_duration=kwargs.get("limit_duration", 30),
        total_user_playing=kwargs.get("total_user_playing", 100),
        question_listings=[],
        author_id=kwargs.get("author_id", "guru123"),
    )


def test_create_quiz(repository):
    quiz = generate_quiz_entity()
    insert_result = MagicMock(inserted_id=ObjectId())
    repository.collection.insert_one.return_value = insert_result

    result = repository.create(quiz)

    repository.collection.insert_one.assert_called_once()
    assert isinstance(result, str)


def test_get_by_id_found(repository):
    quiz_id = ObjectId()
    quiz_data = generate_quiz_entity().model_dump(by_alias=True)
    quiz_data["_id"] = quiz_id
    repository.collection.find_one.return_value = quiz_data

    result = repository.get_by_id(str(quiz_id))

    assert isinstance(result, QuizEntity)
    assert result.id == quiz_id


def test_get_by_id_not_found(repository):
    repository.collection.find_one.return_value = None

    result = repository.get_by_id(str(ObjectId()))

    assert result is None


def test_search_by_title_or_category(repository):
    mock_cursor = MagicMock()
    mock_cursor.skip.return_value = mock_cursor
    mock_cursor.limit.return_value = [generate_quiz_entity().model_dump(by_alias=True)]
    repository.collection.find.return_value = mock_cursor

    result = repository.search_by_title_or_category(
        keyword="Indonesia",
        subject_id=None,
        page=1,
        page_size=10,
    )

    assert len(result) == 1
    assert isinstance(result[0], QuizEntity)


def test_count_by_search(repository):
    repository.collection.count_documents.return_value = 3

    result = repository.count_by_search("sejarah")

    assert result == 3


def test_get_by_ids(repository):
    qid1, qid2 = ObjectId(), ObjectId()
    data = [generate_quiz_entity().model_dump(by_alias=True) for _ in range(2)]
    data[0]["_id"] = qid1
    data[1]["_id"] = qid2
    repository.collection.find.return_value = data

    result = repository.get_by_ids([str(qid1), str(qid2)])

    assert result is not None
    assert all(isinstance(q, QuizEntity) for q in result)


def test_get_by_user_id(repository):
    mock_cursor = MagicMock()
    mock_cursor.skip.return_value = mock_cursor
    mock_cursor.limit.return_value = [generate_quiz_entity().model_dump(by_alias=True)]
    repository.collection.find.return_value = mock_cursor

    result = repository.get_by_user_id("guru123")

    assert isinstance(result, list)
    assert isinstance(result[0], QuizEntity)


def test_get_all(repository):
    mock_cursor = MagicMock()
    mock_cursor.skip.return_value = mock_cursor
    mock_cursor.limit.return_value = [generate_quiz_entity().model_dump(by_alias=True)]
    repository.collection.find.return_value = mock_cursor

    result = repository.get_all()

    assert len(result) == 1
    assert isinstance(result[0], QuizEntity)


def test_update(repository):
    repository.collection.update_one.return_value = MagicMock(modified_count=1)

    result = repository.update(str(ObjectId()), {"title": "Update Title"})

    assert result is True


def test_update_user_playing(repository):
    repository.collection.update_one.return_value = MagicMock(modified_count=1)

    result = repository.update_user_playing(str(ObjectId()), 123)

    assert result is True


def test_delete(repository):
    repository.collection.delete_one.return_value = MagicMock(deleted_count=1)

    result = repository.delete(str(ObjectId()))

    assert result is True


def test_count_by_user_id(repository):
    repository.collection.count_documents.return_value = 7

    result = repository.count_by_user_id("guru123")

    assert result == 7


def test_get_top_played_quizzes(repository):
    mock_cursor = MagicMock()
    mock_cursor.sort.return_value = mock_cursor
    mock_cursor.skip.return_value = mock_cursor
    mock_cursor.limit.return_value = [generate_quiz_entity().model_dump(by_alias=True)]
    repository.collection.find.return_value = mock_cursor

    result = repository.get_top_played_quizzes()

    assert isinstance(result, list)
    assert isinstance(result[0], QuizEntity)
