"""Unit tests for the quest system.

Tests quest definitions, progress tracking, completion detection,
and token rewards.
"""

from __future__ import annotations

import pytest

from timmy.quest_system import (
    QuestDefinition,
    QuestProgress,
    QuestStatus,
    QuestType,
    _is_on_cooldown,
    claim_quest_reward,
    evaluate_quest_progress,
    get_or_create_progress,
    get_quest_definition,
    get_quest_leaderboard,
    load_quest_config,
    reset_quest_progress,
    update_quest_progress,
)


@pytest.fixture(autouse=True)
def clean_quest_state():
    """Reset quest progress between tests."""
    reset_quest_progress()
    yield
    reset_quest_progress()


@pytest.fixture
def sample_issue_count_quest():
    """Create a sample issue_count quest definition."""
    return QuestDefinition(
        id="test_close_issues",
        name="Test Issue Closer",
        description="Close 3 test issues",
        reward_tokens=100,
        quest_type=QuestType.ISSUE_COUNT,
        enabled=True,
        repeatable=False,
        cooldown_hours=0,
        criteria={"target_count": 3, "issue_labels": ["test"]},
        notification_message="Test quest complete! Earned {tokens} tokens.",
    )


@pytest.fixture
def sample_daily_run_quest():
    """Create a sample daily_run quest definition."""
    return QuestDefinition(
        id="test_daily_run",
        name="Test Daily Runner",
        description="Complete 5 sessions",
        reward_tokens=250,
        quest_type=QuestType.DAILY_RUN,
        enabled=True,
        repeatable=True,
        cooldown_hours=24,
        criteria={"min_sessions": 5},
        notification_message="Daily run quest complete! Earned {tokens} tokens.",
    )


# ── Quest Definition Tests ───────────────────────────────────────────────


class TestQuestDefinition:
    def test_from_dict_minimal(self):
        data = {"id": "test_quest", "name": "Test Quest"}
        quest = QuestDefinition.from_dict(data)
        assert quest.id == "test_quest"
        assert quest.name == "Test Quest"
        assert quest.quest_type == QuestType.CUSTOM
        assert quest.enabled is True

    def test_from_dict_full(self):
        data = {
            "id": "full_quest",
            "name": "Full Quest",
            "description": "A test quest",
            "reward_tokens": 500,
            "type": "issue_count",
            "enabled": False,
            "repeatable": True,
            "cooldown_hours": 12,
            "criteria": {"target_count": 5},
            "notification_message": "Done!",
        }
        quest = QuestDefinition.from_dict(data)
        assert quest.id == "full_quest"
        assert quest.reward_tokens == 500
        assert quest.quest_type == QuestType.ISSUE_COUNT
        assert quest.enabled is False
        assert quest.repeatable is True
        assert quest.cooldown_hours == 12


# ── Quest Progress Tests ─────────────────────────────────────────────────


class TestQuestProgress:
    def test_progress_creation(self):
        progress = QuestProgress(
            quest_id="test_quest",
            agent_id="test_agent",
            status=QuestStatus.NOT_STARTED,
        )
        assert progress.quest_id == "test_quest"
        assert progress.agent_id == "test_agent"
        assert progress.current_value == 0

    def test_progress_to_dict(self):
        progress = QuestProgress(
            quest_id="test_quest",
            agent_id="test_agent",
            status=QuestStatus.IN_PROGRESS,
            current_value=2,
            target_value=5,
        )
        data = progress.to_dict()
        assert data["quest_id"] == "test_quest"
        assert data["status"] == "in_progress"
        assert data["current_value"] == 2


# ── Quest Loading Tests ──────────────────────────────────────────────────


class TestQuestLoading:
    def test_load_quest_config(self):
        definitions, settings = load_quest_config()
        assert isinstance(definitions, dict)
        assert isinstance(settings, dict)

    def test_get_quest_definition_exists(self):
        # Should return None for non-existent quest in fresh state
        quest = get_quest_definition("nonexistent")
        # The function returns from loaded config, which may have quests
        # or be empty if config doesn't exist
        assert quest is None or isinstance(quest, QuestDefinition)

    def test_get_quest_definition_not_found(self):
        quest = get_quest_definition("definitely_not_a_real_quest_12345")
        assert quest is None


# ── Quest Progress Management Tests ─────────────────────────────────────


class TestQuestProgressManagement:
    def test_get_or_create_progress_new(self):
        # First create a quest definition
        quest = QuestDefinition(
            id="progress_test",
            name="Progress Test",
            description="Test quest",
            reward_tokens=100,
            quest_type=QuestType.ISSUE_COUNT,
            enabled=True,
            repeatable=False,
            cooldown_hours=0,
            criteria={"target_count": 3},
            notification_message="Done!",
        )

        # Need to inject into the definitions dict
        from timmy.quest_system import _quest_definitions

        _quest_definitions["progress_test"] = quest

        progress = get_or_create_progress("progress_test", "agent1")
        assert progress.quest_id == "progress_test"
        assert progress.agent_id == "agent1"
        assert progress.status == QuestStatus.NOT_STARTED
        assert progress.target_value == 3

        del _quest_definitions["progress_test"]

    def test_update_quest_progress(self):
        quest = QuestDefinition(
            id="update_test",
            name="Update Test",
            description="Test quest",
            reward_tokens=100,
            quest_type=QuestType.ISSUE_COUNT,
            enabled=True,
            repeatable=False,
            cooldown_hours=0,
            criteria={"target_count": 3},
            notification_message="Done!",
        )

        from timmy.quest_system import _quest_definitions

        _quest_definitions["update_test"] = quest

        # Create initial progress
        progress = get_or_create_progress("update_test", "agent1")
        assert progress.current_value == 0

        # Update progress
        updated = update_quest_progress("update_test", "agent1", 2)
        assert updated.current_value == 2
        assert updated.status == QuestStatus.NOT_STARTED

        # Complete the quest
        completed = update_quest_progress("update_test", "agent1", 3)
        assert completed.current_value == 3
        assert completed.status == QuestStatus.COMPLETED
        assert completed.completed_at != ""

        del _quest_definitions["update_test"]


# ── Quest Evaluation Tests ───────────────────────────────────────────────


class TestQuestEvaluation:
    def test_evaluate_issue_count_quest(self):
        quest = QuestDefinition(
            id="eval_test",
            name="Eval Test",
            description="Test quest",
            reward_tokens=100,
            quest_type=QuestType.ISSUE_COUNT,
            enabled=True,
            repeatable=False,
            cooldown_hours=0,
            criteria={"target_count": 2, "issue_labels": ["test"]},
            notification_message="Done!",
        )

        from timmy.quest_system import _quest_definitions

        _quest_definitions["eval_test"] = quest

        # Simulate closed issues
        closed_issues = [
            {"id": 1, "labels": [{"name": "test"}]},
            {"id": 2, "labels": [{"name": "test"}, {"name": "bug"}]},
            {"id": 3, "labels": [{"name": "other"}]},
        ]

        context = {"closed_issues": closed_issues}
        progress = evaluate_quest_progress("eval_test", "agent1", context)

        assert progress is not None
        assert progress.current_value == 2  # Two issues with 'test' label

        del _quest_definitions["eval_test"]

    def test_evaluate_issue_reduce_quest(self):
        quest = QuestDefinition(
            id="reduce_test",
            name="Reduce Test",
            description="Test quest",
            reward_tokens=200,
            quest_type=QuestType.ISSUE_REDUCE,
            enabled=True,
            repeatable=False,
            cooldown_hours=0,
            criteria={"target_reduction": 2},
            notification_message="Done!",
        )

        from timmy.quest_system import _quest_definitions

        _quest_definitions["reduce_test"] = quest

        context = {"previous_issue_count": 10, "current_issue_count": 7}
        progress = evaluate_quest_progress("reduce_test", "agent1", context)

        assert progress is not None
        assert progress.current_value == 3  # Reduced by 3

        del _quest_definitions["reduce_test"]

    def test_evaluate_daily_run_quest(self):
        quest = QuestDefinition(
            id="daily_test",
            name="Daily Test",
            description="Test quest",
            reward_tokens=250,
            quest_type=QuestType.DAILY_RUN,
            enabled=True,
            repeatable=True,
            cooldown_hours=24,
            criteria={"min_sessions": 5},
            notification_message="Done!",
        )

        from timmy.quest_system import _quest_definitions

        _quest_definitions["daily_test"] = quest

        context = {"sessions_completed": 5}
        progress = evaluate_quest_progress("daily_test", "agent1", context)

        assert progress is not None
        assert progress.current_value == 5
        assert progress.status == QuestStatus.COMPLETED

        del _quest_definitions["daily_test"]


# ── Quest Cooldown Tests ─────────────────────────────────────────────────


class TestQuestCooldown:
    def test_is_on_cooldown_no_cooldown(self):
        quest = QuestDefinition(
            id="cooldown_test",
            name="Cooldown Test",
            description="Test quest",
            reward_tokens=100,
            quest_type=QuestType.ISSUE_COUNT,
            enabled=True,
            repeatable=True,
            cooldown_hours=24,
            criteria={},
            notification_message="Done!",
        )

        progress = QuestProgress(
            quest_id="cooldown_test",
            agent_id="agent1",
            status=QuestStatus.CLAIMED,
        )

        # No last_completed_at means no cooldown
        assert _is_on_cooldown(progress, quest) is False


# ── Quest Reward Tests ───────────────────────────────────────────────────


class TestQuestReward:
    def test_claim_quest_reward_not_completed(self):
        quest = QuestDefinition(
            id="reward_test",
            name="Reward Test",
            description="Test quest",
            reward_tokens=100,
            quest_type=QuestType.ISSUE_COUNT,
            enabled=True,
            repeatable=False,
            cooldown_hours=0,
            criteria={"target_count": 3},
            notification_message="Done!",
        )

        from timmy.quest_system import _quest_definitions, _quest_progress

        _quest_definitions["reward_test"] = quest

        # Create progress but don't complete
        progress = get_or_create_progress("reward_test", "agent1")
        _quest_progress["agent1:reward_test"] = progress

        # Try to claim - should fail
        reward = claim_quest_reward("reward_test", "agent1")
        assert reward is None

        del _quest_definitions["reward_test"]


# ── Leaderboard Tests ────────────────────────────────────────────────────


class TestQuestLeaderboard:
    def test_get_quest_leaderboard_empty(self):
        reset_quest_progress()
        leaderboard = get_quest_leaderboard()
        assert leaderboard == []

    def test_get_quest_leaderboard_with_data(self):
        # Create and complete a quest for two agents
        quest = QuestDefinition(
            id="leaderboard_test",
            name="Leaderboard Test",
            description="Test quest",
            reward_tokens=100,
            quest_type=QuestType.ISSUE_COUNT,
            enabled=True,
            repeatable=True,
            cooldown_hours=0,
            criteria={"target_count": 1},
            notification_message="Done!",
        )

        from timmy.quest_system import _quest_definitions, _quest_progress

        _quest_definitions["leaderboard_test"] = quest

        # Create progress for agent1 with 2 completions
        progress1 = QuestProgress(
            quest_id="leaderboard_test",
            agent_id="agent1",
            status=QuestStatus.NOT_STARTED,
            completion_count=2,
        )
        _quest_progress["agent1:leaderboard_test"] = progress1

        # Create progress for agent2 with 1 completion
        progress2 = QuestProgress(
            quest_id="leaderboard_test",
            agent_id="agent2",
            status=QuestStatus.NOT_STARTED,
            completion_count=1,
        )
        _quest_progress["agent2:leaderboard_test"] = progress2

        leaderboard = get_quest_leaderboard()

        assert len(leaderboard) == 2
        # agent1 should be first (more tokens)
        assert leaderboard[0]["agent_id"] == "agent1"
        assert leaderboard[0]["total_tokens"] == 200
        assert leaderboard[1]["agent_id"] == "agent2"
        assert leaderboard[1]["total_tokens"] == 100

        del _quest_definitions["leaderboard_test"]


# ── Quest Reset Tests ─────────────────────────────────────────────────────


class TestQuestReset:
    def test_reset_quest_progress_all(self):
        # Create some progress entries
        progress1 = QuestProgress(
            quest_id="quest1", agent_id="agent1", status=QuestStatus.NOT_STARTED
        )
        progress2 = QuestProgress(
            quest_id="quest2", agent_id="agent2", status=QuestStatus.NOT_STARTED
        )

        from timmy.quest_system import _quest_progress

        _quest_progress["agent1:quest1"] = progress1
        _quest_progress["agent2:quest2"] = progress2

        assert len(_quest_progress) == 2

        count = reset_quest_progress()
        assert count == 2
        assert len(_quest_progress) == 0

    def test_reset_quest_progress_specific_quest(self):
        progress1 = QuestProgress(
            quest_id="quest1", agent_id="agent1", status=QuestStatus.NOT_STARTED
        )
        progress2 = QuestProgress(
            quest_id="quest2", agent_id="agent1", status=QuestStatus.NOT_STARTED
        )

        from timmy.quest_system import _quest_progress

        _quest_progress["agent1:quest1"] = progress1
        _quest_progress["agent1:quest2"] = progress2

        count = reset_quest_progress(quest_id="quest1")
        assert count == 1
        assert "agent1:quest1" not in _quest_progress
        assert "agent1:quest2" in _quest_progress

    def test_reset_quest_progress_specific_agent(self):
        progress1 = QuestProgress(
            quest_id="quest1", agent_id="agent1", status=QuestStatus.NOT_STARTED
        )
        progress2 = QuestProgress(
            quest_id="quest1", agent_id="agent2", status=QuestStatus.NOT_STARTED
        )

        from timmy.quest_system import _quest_progress

        _quest_progress["agent1:quest1"] = progress1
        _quest_progress["agent2:quest1"] = progress2

        count = reset_quest_progress(agent_id="agent1")
        assert count == 1
        assert "agent1:quest1" not in _quest_progress
        assert "agent2:quest1" in _quest_progress
