"""Tests for brynq.tasks — task delegation lifecycle."""

import json
import pytest
from pathlib import Path


@pytest.fixture(autouse=True)
def _isolate_tasks(tmp_path, monkeypatch):
    """Redirect task storage to a temp directory."""
    tasks_dir = tmp_path / "tasks"
    tasks_dir.mkdir()
    channels_dir = tmp_path / "channels"
    channels_dir.mkdir()
    monkeypatch.setattr("brynq.tasks.TASKS_DIR", tasks_dir)
    monkeypatch.setattr("brynq.tasks.CHANNELS_DIR", channels_dir)


class TestCreateTask:
    def test_creates_task_file(self):
        from brynq.tasks import create_task, TASKS_DIR
        task_id = create_task("general", "Fix bug", "Something is broken")
        assert (TASKS_DIR / f"{task_id}.json").exists()

    def test_returns_task_id_string(self):
        from brynq.tasks import create_task
        task_id = create_task("general", "Test", "desc")
        assert isinstance(task_id, str)
        assert len(task_id) == 12

    def test_task_has_correct_fields(self):
        from brynq.tasks import create_task, _read_task
        task_id = create_task("dev", "Build feature", "Add button", priority="high")
        task = _read_task(task_id)
        assert task["title"] == "Build feature"
        assert task["description"] == "Add button"
        assert task["priority"] == "high"
        assert task["channel"] == "dev"
        assert task["status"] == "pending"
        assert task["assignee_session"] is None

    def test_invalid_priority_raises(self):
        from brynq.tasks import create_task
        with pytest.raises(ValueError, match="Invalid priority"):
            create_task("general", "Test", "desc", priority="critical")

    def test_default_priority_is_normal(self):
        from brynq.tasks import create_task, _read_task
        task_id = create_task("general", "Test", "desc")
        task = _read_task(task_id)
        assert task["priority"] == "normal"


class TestListTasks:
    def test_empty_list(self):
        from brynq.tasks import list_tasks
        assert list_tasks() == []

    def test_lists_created_tasks(self):
        from brynq.tasks import create_task, list_tasks
        create_task("ch", "T1", "d1")
        create_task("ch", "T2", "d2")
        tasks = list_tasks()
        assert len(tasks) == 2

    def test_filter_by_channel(self):
        from brynq.tasks import create_task, list_tasks
        create_task("alpha", "T1", "d1")
        create_task("beta", "T2", "d2")
        assert len(list_tasks(channel="alpha")) == 1
        assert list_tasks(channel="alpha")[0]["title"] == "T1"

    def test_filter_by_status(self):
        from brynq.tasks import create_task, accept_task, list_tasks
        t1 = create_task("ch", "Pending", "d")
        t2 = create_task("ch", "Accepted", "d")
        accept_task(t2)
        pending = list_tasks(status="pending")
        assert len(pending) == 1
        assert pending[0]["task_id"] == t1

    def test_invalid_status_raises(self):
        from brynq.tasks import list_tasks
        with pytest.raises(ValueError, match="Invalid status"):
            list_tasks(status="bogus")


class TestTaskLifecycle:
    def test_accept_task(self):
        from brynq.tasks import create_task, accept_task, _read_task
        task_id = create_task("ch", "Test", "desc")
        result = accept_task(task_id)
        assert result["status"] == "accepted"
        assert result["accepted_at"] is not None
        task = _read_task(task_id)
        assert task["status"] == "accepted"

    def test_accept_nonexistent_raises(self):
        from brynq.tasks import accept_task
        with pytest.raises(FileNotFoundError):
            accept_task("nonexistent")

    def test_accept_completed_raises(self):
        from brynq.tasks import create_task, accept_task, complete_task
        task_id = create_task("ch", "Test", "desc")
        accept_task(task_id)
        complete_task(task_id, "done")
        with pytest.raises(ValueError, match="Cannot accept"):
            accept_task(task_id)

    def test_complete_task(self):
        from brynq.tasks import create_task, accept_task, complete_task, _read_task
        task_id = create_task("ch", "Test", "desc")
        accept_task(task_id)
        result = complete_task(task_id, "All done")
        assert result["status"] == "completed"
        assert result["result"] == "All done"
        task = _read_task(task_id)
        assert task["completed_at"] is not None

    def test_complete_pending_raises(self):
        from brynq.tasks import create_task, complete_task
        task_id = create_task("ch", "Test", "desc")
        with pytest.raises(ValueError, match="must be 'accepted'"):
            complete_task(task_id, "done")

    def test_fail_task(self):
        from brynq.tasks import create_task, fail_task, _read_task
        task_id = create_task("ch", "Test", "desc")
        result = fail_task(task_id, "Could not do it")
        assert result["status"] == "failed"
        assert result["result"] == "Could not do it"

    def test_fail_completed_raises(self):
        from brynq.tasks import create_task, accept_task, complete_task, fail_task
        task_id = create_task("ch", "Test", "desc")
        accept_task(task_id)
        complete_task(task_id, "done")
        with pytest.raises(ValueError, match="Cannot fail"):
            fail_task(task_id, "oops")

    def test_task_status(self):
        from brynq.tasks import create_task, task_status
        task_id = create_task("ch", "Test", "desc")
        status = task_status(task_id)
        assert status["task_id"] == task_id
        assert status["title"] == "Test"

    def test_task_status_nonexistent_raises(self):
        from brynq.tasks import task_status
        with pytest.raises(FileNotFoundError):
            task_status("nonexistent")


class TestReviewFlow:
    def test_submit_for_review(self):
        from brynq.tasks import create_task, accept_task, submit_for_review, _read_task
        task_id = create_task("ch", "Test", "desc")
        accept_task(task_id)
        result = submit_for_review(task_id, "Here is my work")
        assert result["status"] == "in_review"
        task = _read_task(task_id)
        assert task["status"] == "in_review"
        assert task["deliverable"] == "Here is my work"

    def test_submit_pending_task_errors(self):
        from brynq.tasks import create_task, submit_for_review
        task_id = create_task("ch", "Test", "desc")
        result = submit_for_review(task_id)
        assert "error" in result
