"""Integration tests for the idea backlog feature."""

from __future__ import annotations

import pytest

from loom.exceptions import TaskStateError
from loom.graph import store
from loom.graph.cache import add_to_ready_queue, get_ready_tasks, sync_task
from loom.graph.project import get_project_status
from loom.graph.task import Task, TaskStatus
from loom.ids import task_id as gen_task_id


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


async def _make_idea(pool, project_id, title="test idea", context=None):
    return await store.create_idea(pool, project_id, title, context)


async def _make_pending(pool, redis_conn, project_id, title="pending task"):
    task = Task(
        id=gen_task_id(), project_id=project_id, title=title,
        status=TaskStatus.PENDING, priority="p1",
    )
    task = await store.create_task(pool, task)
    await sync_task(redis_conn, task)
    await add_to_ready_queue(redis_conn, task)
    return task


# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_create_idea(pool, project, redis_conn):
    """Create an idea and verify its status; verify NOT in ready queue."""
    idea = await _make_idea(pool, project)
    assert idea.status == TaskStatus.IDEA
    assert idea.title == "test idea"

    ready = await get_ready_tasks(redis_conn, pool, project)
    assert idea.id not in [t.id for t in ready]


@pytest.mark.asyncio
async def test_list_ideas(pool, project):
    """Create 3 ideas, verify all returned."""
    for i in range(3):
        await _make_idea(pool, project, title=f"idea {i}")

    ideas = await store.list_ideas(pool, project)
    assert len(ideas) == 3
    assert all(i.status == TaskStatus.IDEA for i in ideas)


@pytest.mark.asyncio
async def test_drop_idea(pool, project):
    """Create an idea, drop it, verify gone."""
    idea = await _make_idea(pool, project)
    await store.drop_idea(pool, idea.id)

    ideas = await store.list_ideas(pool, project)
    assert len(ideas) == 0


@pytest.mark.asyncio
async def test_drop_non_idea_fails(pool, project, redis_conn):
    """Dropping a pending task raises TaskStateError."""
    task = await _make_pending(pool, redis_conn, project)
    with pytest.raises(TaskStateError, match="dropped"):
        await store.drop_idea(pool, task.id)


@pytest.mark.asyncio
async def test_idea_excluded_from_ready(pool, project, redis_conn):
    """Idea + pending task: only pending shows in ready queue."""
    idea = await _make_idea(pool, project)
    await sync_task(redis_conn, idea)
    pending = await _make_pending(pool, redis_conn, project)

    ready = await get_ready_tasks(redis_conn, pool, project)
    ready_ids = [t.id for t in ready]
    assert pending.id in ready_ids
    assert idea.id not in ready_ids


@pytest.mark.asyncio
async def test_idea_excluded_from_actionable(pool, project, redis_conn):
    """Idea is not returned by get_actionable_tasks."""
    idea = await _make_idea(pool, project)
    pending = await _make_pending(pool, redis_conn, project)

    actionable = await store.get_actionable_tasks(pool, project)
    actionable_ids = [t.id for t in actionable]
    assert pending.id in actionable_ids
    assert idea.id not in actionable_ids


@pytest.mark.asyncio
async def test_promote_idea(pool, project):
    """Promote idea → epic."""
    idea = await _make_idea(pool, project)
    promoted = await store.promote_idea(pool, idea.id)
    assert promoted.status == TaskStatus.EPIC
    assert promoted.id == idea.id


@pytest.mark.asyncio
async def test_promote_non_idea_fails(pool, project, redis_conn):
    """Promoting a pending task raises TaskStateError."""
    task = await _make_pending(pool, redis_conn, project)
    with pytest.raises(TaskStateError, match="promoted"):
        await store.promote_idea(pool, task.id)


@pytest.mark.asyncio
async def test_claim_idea_fails(pool, project):
    """Claiming an idea gives a helpful error."""
    idea = await _make_idea(pool, project)
    with pytest.raises(TaskStateError, match="promote it first"):
        await store.claim_task(pool, idea.id, "agent-1")


@pytest.mark.asyncio
async def test_idea_in_project_status(pool, project):
    """Idea count shows in get_project_status."""
    await _make_idea(pool, project, title="idea 1")
    await _make_idea(pool, project, title="idea 2")

    ps = await get_project_status(pool, project)
    assert ps.idea == 2


@pytest.mark.asyncio
async def test_ready_hint_when_ideas_exist(pool, project, redis_conn):
    """Empty ready queue + ideas → hint returned from store.list_ideas."""
    await _make_idea(pool, project)

    # Verify ready queue is empty
    ready = await get_ready_tasks(redis_conn, pool, project)
    assert len(ready) == 0

    # Verify ideas exist
    ideas = await store.list_ideas(pool, project)
    assert len(ideas) == 1
