"""Project CRUD operations."""

from __future__ import annotations

import asyncpg

from loom.exceptions import ProjectNotFoundError
from loom.graph.task import ProjectStatus


async def create_project(
    pool: asyncpg.Pool, name: str, description: str = "", project_id: str | None = None
) -> dict:
    """Create a new project. Returns the project record as a dict."""
    async with pool.acquire() as conn:
        if project_id:
            row = await conn.fetchrow(
                """
                INSERT INTO projects (id, name, description)
                VALUES ($1::uuid, $2, $3) RETURNING *
                """,
                project_id, name, description,
            )
        else:
            row = await conn.fetchrow(
                """
                INSERT INTO projects (name, description)
                VALUES ($1, $2) RETURNING *
                """,
                name, description,
            )
    return dict(row)


async def get_project(pool: asyncpg.Pool, project_id: str) -> dict:
    """Fetch a project by ID."""
    async with pool.acquire() as conn:
        row = await conn.fetchrow(
            "SELECT * FROM projects WHERE id = $1::uuid", project_id
        )
    if row is None:
        raise ProjectNotFoundError(project_id)
    return dict(row)


async def get_project_status(pool: asyncpg.Pool, project_id: str) -> ProjectStatus:
    """Get project status with task counts."""
    project = await get_project(pool, project_id)

    async with pool.acquire() as conn:
        counts = await conn.fetch(
            """
            SELECT status, COUNT(*) as cnt
            FROM tasks WHERE project_id = $1::uuid
            GROUP BY status
            """,
            project_id,
        )
        count_map = {r["status"]: r["cnt"] for r in counts}

        blocked_tasks = await conn.fetch(
            """
            SELECT id FROM tasks
            WHERE project_id = $1::uuid AND status = 'blocked'
            """,
            project_id,
        )

        open_esc = await conn.fetchval(
            """
            SELECT COUNT(*) FROM escalations
            WHERE project_id = $1::uuid AND resolved = FALSE
            """,
            project_id,
        )

        ready_count = await conn.fetchval(
            """
            SELECT COUNT(*) FROM tasks t
            WHERE t.project_id = $1::uuid
              AND t.status = 'pending'
              AND NOT EXISTS (
                  SELECT 1 FROM task_deps td
                  JOIN tasks dep ON dep.id = td.depends_on
                  WHERE td.task_id = t.id AND dep.status NOT IN ('done')
              )
            """,
            project_id,
        )

    return ProjectStatus(
        project_id=str(project["id"]),
        project_name=project["name"],
        total=sum(count_map.values()),
        pending=count_map.get("pending", 0),
        claimed=count_map.get("claimed", 0),
        done=count_map.get("done", 0),
        failed=count_map.get("failed", 0),
        blocked=count_map.get("blocked", 0),
        epic=count_map.get("epic", 0),
        idea=count_map.get("idea", 0),
        ready_count=ready_count or 0,
        blocked_tasks=[r["id"] for r in blocked_tasks],
        open_escalations=open_esc or 0,
    )


async def list_projects(pool: asyncpg.Pool, include_archived: bool = False) -> list[dict]:
    """List all projects, optionally including archived ones."""
    async with pool.acquire() as conn:
        if include_archived:
            rows = await conn.fetch("SELECT * FROM projects ORDER BY created_at")
        else:
            rows = await conn.fetch(
                "SELECT * FROM projects WHERE archived = FALSE ORDER BY created_at"
            )
    return [dict(r) for r in rows]


async def archive_project(pool: asyncpg.Pool, project_id: str) -> dict:
    """Soft-delete a project by setting archived=True."""
    async with pool.acquire() as conn:
        row = await conn.fetchrow(
            "UPDATE projects SET archived = TRUE WHERE id = $1::uuid RETURNING *",
            project_id,
        )
    if row is None:
        raise ProjectNotFoundError(project_id)
    return dict(row)


async def update_project(pool: asyncpg.Pool, project_id: str, **fields) -> dict:
    """Update project fields (name, description, config)."""
    allowed = {"name", "description", "config"}
    updates = {k: v for k, v in fields.items() if k in allowed and v is not None}
    if not updates:
        return await get_project(pool, project_id)

    set_parts = []
    values = []
    for i, (key, val) in enumerate(updates.items(), start=1):
        if key == "config":
            import json
            set_parts.append(f"{key} = ${i}::jsonb")
            values.append(json.dumps(val))
        else:
            set_parts.append(f"{key} = ${i}")
            values.append(val)
    values.append(project_id)

    query = f"UPDATE projects SET {', '.join(set_parts)} WHERE id = ${len(values)}::uuid RETURNING *"
    async with pool.acquire() as conn:
        row = await conn.fetchrow(query, *values)
    if row is None:
        raise ProjectNotFoundError(project_id)
    return dict(row)
