"""Service for interacting with the bd CLI."""

import asyncio
import json
from pathlib import Path

from hangar.models.beads import BeadsTask, BeadsTaskList


class BeadsService:
    """Wraps the bd CLI for task management."""

    def __init__(self, project_path: Path) -> None:
        self.project_path = project_path

    async def _run_bd(self, *args: str) -> str:
        """Run a bd CLI command and return stdout."""
        proc = await asyncio.create_subprocess_exec(
            "bd",
            *args,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
            cwd=self.project_path,
        )
        stdout, stderr = await proc.communicate()

        if proc.returncode != 0:
            error_msg = stderr.decode().strip()
            raise RuntimeError(f"bd {' '.join(args)} failed: {error_msg}")

        return stdout.decode()

    def _parse_json(self, args: tuple[str, ...], output: str) -> dict | list:
        """Parse JSON from bd CLI output, raising descriptive errors."""
        try:
            return json.loads(output)
        except json.JSONDecodeError as e:
            raise RuntimeError(
                f"bd {' '.join(args)} returned invalid JSON: {output[:200]}"
            ) from e

    async def list_tasks(self) -> BeadsTaskList:
        """List all tasks (including closed) via bd list --all --json."""
        args = ("list", "--all", "--json")
        output = await self._run_bd(*args)
        data = self._parse_json(args, output)
        return BeadsTaskList.from_json(data)

    async def ready_tasks(self) -> BeadsTaskList:
        """List ready tasks via bd ready --json."""
        args = ("ready", "--json")
        output = await self._run_bd(*args)
        data = self._parse_json(args, output)
        return BeadsTaskList.from_json(data)

    async def show_task(self, task_id: str) -> BeadsTask:
        """Show a single task via bd show <id> --json."""
        args = ("show", task_id, "--json")
        output = await self._run_bd(*args)
        data = self._parse_json(args, output)
        # bd show returns an array with one element
        if isinstance(data, list):
            data = data[0]
        return BeadsTask.model_validate(data)

    async def claim_task(self, task_id: str) -> None:
        """Claim a task via bd update <id> --claim."""
        await self._run_bd("update", task_id, "--claim")

    async def close_task(self, task_id: str, reason: str) -> None:
        """Close a task via bd close <id> --reason."""
        await self._run_bd("close", task_id, "--reason", reason)

    async def update_status(self, task_id: str, status: str) -> None:
        """Update task status via bd update <id> --status <status>."""
        await self._run_bd("update", task_id, "--status", status)

    async def create_task(self, title: str) -> str:
        """Create a task and return raw output (contains beads ID)."""
        return await self._run_bd("create", title)

    async def add_dependency(self, blocker_id: str, blocked_id: str) -> None:
        """Wire a dependency: blocker blocks blocked."""
        await self._run_bd("dep", blocker_id, "--blocks", blocked_id)
