"""Tests for task reset functionality (Phase 7, Stream A)."""

import pytest

from loom.graph.store import reset_task, claim_task, fail_task, get_task, create_task, mark_dead_letter
from loom.graph.task import Task, TaskStatus, Priority
from loom.ids import task_id as gen_task_id
from loom.bus.events import EventType


@pytest.fixture
async def sample_task(pool, project):
    """Create a pending task for testing."""
    task = Task(
        id=gen_task_id(),
        project_id=project,
        title="Test task for reset",
        status=TaskStatus.PENDING,
        priority=Priority.P1,
        context={"key": "value"},
    )
    return await create_task(pool, task)


class TestResetTask:
    async def test_reset_from_claimed(self, pool, project, sample_task):
        """Reset a claimed task back to pending."""
        claimed = await claim_task(pool, sample_task.id, "agent-1", ttl_seconds=600)
        assert claimed.status == TaskStatus.CLAIMED
        assert claimed.assignee == "agent-1"

        reset = await reset_task(pool, sample_task.id)
        assert reset.status == TaskStatus.PENDING
        assert reset.assignee is None
        assert reset.claimed_at is None
        assert reset.claim_expires_at is None
        assert reset.retry_count == 0

    async def test_reset_from_failed(self, pool, project, sample_task):
        """Reset a failed task back to pending."""
        claimed = await claim_task(pool, sample_task.id, "agent-1")
        failed = await fail_task(pool, sample_task.id, "some error")
        assert failed.status == TaskStatus.FAILED

        reset = await reset_task(pool, sample_task.id)
        assert reset.status == TaskStatus.PENDING
        assert reset.assignee is None
        assert reset.retry_count == 0
        assert reset.last_failed_at is None
        assert reset.retry_after is None

    async def test_reset_from_dead_letter(self, pool, project, sample_task):
        """Reset a dead-lettered task back to pending."""
        claimed = await claim_task(pool, sample_task.id, "agent-1")
        failed = await fail_task(pool, sample_task.id, "error")
        dl = await mark_dead_letter(pool, sample_task.id, "too many retries")
        assert dl.dead_letter is True

        reset = await reset_task(pool, sample_task.id)
        assert reset.status == TaskStatus.PENDING
        assert reset.dead_letter is False
        assert reset.dead_letter_reason is None

    async def test_reset_rejects_pending(self, pool, project, sample_task):
        """Cannot reset an already-pending task."""
        from loom.exceptions import TaskStateError
        with pytest.raises(TaskStateError):
            await reset_task(pool, sample_task.id)

    async def test_reset_rejects_done(self, pool, project, sample_task):
        """Cannot reset a completed task."""
        from loom.graph.store import complete_task
        claimed = await claim_task(pool, sample_task.id, "agent-1")
        await complete_task(pool, sample_task.id, {"result": "ok"})
        from loom.exceptions import TaskStateError
        with pytest.raises(TaskStateError):
            await reset_task(pool, sample_task.id)

    async def test_reset_clear_output(self, pool, project, sample_task):
        """Reset with clear_output=True clears the output field."""
        claimed = await claim_task(pool, sample_task.id, "agent-1")
        failed = await fail_task(pool, sample_task.id, "error")

        reset = await reset_task(pool, sample_task.id, clear_output=True)
        assert reset.status == TaskStatus.PENDING
        assert reset.output == {}

    async def test_reset_preserves_output_by_default(self, pool, project, sample_task):
        """Reset without clear_output preserves existing output."""
        claimed = await claim_task(pool, sample_task.id, "agent-1")
        failed = await fail_task(pool, sample_task.id, "error")

        reset = await reset_task(pool, sample_task.id, clear_output=False)
        assert reset.status == TaskStatus.PENDING
        # output should still have whatever was there

    async def test_reset_nonexistent_task(self, pool, project):
        """Reset a task that doesn't exist raises error."""
        from loom.exceptions import TaskNotFoundError
        with pytest.raises(TaskNotFoundError):
            await reset_task(pool, "nonexistent-id")


class TestTaskResetEventType:
    def test_task_reset_event_exists(self):
        """TASK_RESET event type is defined."""
        assert EventType.TASK_RESET == "task.reset"
