"""Task tools: tms_list_tasks, tms_get_task, tms_create_task, tms_update_task, tms_delete_task."""
from __future__ import annotations

import asyncio
import json

from api_client import ApiError, client


def register(mcp):
    @mcp.tool()
    async def tms_list_tasks(
        project_id: int,
        status: str | None = None,
        priority: str | None = None,
        assigned_to_id: int | None = None,
        parent_task_id: int | None = None,
    ) -> str:
        """List tasks in a project with optional filters.

        Args:
            project_id: Required. The project to list tasks from.
            status: Filter by status (backlog, todo, in-progress, blocked, testing, in-review, done).
            priority: Filter by priority (low, medium, high, critical).
            assigned_to_id: Filter to tasks assigned to a specific user ID.
            parent_task_id: Filter to subtasks of a specific parent task. Use 'null' for top-level tasks only.
        """
        try:
            params = {"project_id": project_id}
            if parent_task_id is not None:
                params["parent_task_id"] = parent_task_id

            tasks = await client.get("/tasks/", **params)

            # Client-side filtering for fields the API doesn't support as query params
            if status:
                tasks = [t for t in tasks if t.get("status") == status]
            if priority:
                tasks = [t for t in tasks if t.get("priority") == priority]
            if assigned_to_id is not None:
                tasks = [t for t in tasks if t.get("assigned_to_id") == assigned_to_id]

            if not tasks:
                return "No tasks found matching the filters."
            return json.dumps(tasks, indent=2, default=str)
        except ApiError as e:
            return str(e)

    @mcp.tool()
    async def tms_get_task(task_id: int, include_audit_log: bool = False) -> str:
        """Get detailed information about a specific task, including its subtasks.

        Args:
            task_id: The task ID.
            include_audit_log: If True, also fetches the audit history for this task.
        """
        try:
            task = await client.get(f"/tasks/{task_id}")

            # Fetch subtasks (and optionally audit log) in parallel
            coros = [
                client.get("/tasks/", project_id=task["project_id"], parent_task_id=task_id),
            ]
            if include_audit_log:
                coros.append(client.get(f"/tasks/audit-log/by-task/{task_id}"))

            results = await asyncio.gather(*coros, return_exceptions=True)

            task["subtasks"] = results[0] if not isinstance(results[0], Exception) else []
            if include_audit_log and len(results) > 1:
                task["audit_log"] = results[1] if not isinstance(results[1], Exception) else []

            return json.dumps(task, indent=2, default=str)
        except ApiError as e:
            return str(e)

    @mcp.tool()
    async def tms_create_task(
        project_id: int,
        title: str,
        description: str | None = None,
        task_type: str = "task",
        category: str = "feature",
        priority: str = "medium",
        status: str = "todo",
        story_points: int | None = None,
        difficulty: int = 3,
        estimated_time: float | None = None,
        due_date: str | None = None,
        assigned_to_id: int | None = None,
        parent_task_id: int | None = None,
        order_index: int = 0,
    ) -> str:
        """Create a new task in a project.

        Args:
            project_id: The project to create the task in.
            title: Task title.
            description: Task description.
            task_type: Type of task (task, story, feature). Default: task.
            category: Category (feature, bug, improvement, maintenance). Default: feature.
            priority: Priority level (low, medium, high, critical). Default: medium.
            status: Initial status (backlog, todo, in-progress, blocked, testing, in-review, done). Default: todo.
            story_points: Story point estimate.
            difficulty: Difficulty 1-5. Default: 3.
            estimated_time: Estimated time in hours.
            due_date: Due date in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS).
            assigned_to_id: User ID to assign the task to.
            parent_task_id: Parent task ID if this is a subtask.
            order_index: Display order. Default: 0.
        """
        try:
            body = {"project_id": project_id, "title": title, "order_index": order_index,
                    "task_type": task_type, "category": category, "priority": priority,
                    "status": status, "difficulty": difficulty}
            if description is not None:
                body["description"] = description
            if story_points is not None:
                body["story_points"] = story_points
            if estimated_time is not None:
                body["estimated_time"] = estimated_time
            if due_date is not None:
                body["due_date"] = due_date
            if assigned_to_id is not None:
                body["assigned_to_id"] = assigned_to_id
            if parent_task_id is not None:
                body["parent_task_id"] = parent_task_id

            data = await client.post("/tasks/", body)
            return json.dumps(data, indent=2, default=str)
        except ApiError as e:
            return str(e)

    @mcp.tool()
    async def tms_update_task(
        task_id: int,
        title: str | None = None,
        description: str | None = None,
        task_type: str | None = None,
        category: str | None = None,
        priority: str | None = None,
        status: str | None = None,
        story_points: int | None = None,
        difficulty: int | None = None,
        estimated_time: float | None = None,
        actual_time: float | None = None,
        due_date: str | None = None,
        assigned_to_id: int | None = None,
        parent_task_id: int | None = None,
        order_index: int | None = None,
    ) -> str:
        """Update an existing task. Only provide the fields you want to change.

        Args:
            task_id: The task ID to update.
            title: New title.
            description: New description.
            task_type: New type (task, story, feature).
            category: New category (feature, bug, improvement, maintenance).
            priority: New priority (low, medium, high, critical).
            status: New status (backlog, todo, in-progress, blocked, testing, in-review, done).
            story_points: New story point estimate.
            difficulty: New difficulty 1-5.
            estimated_time: New estimated time in hours.
            actual_time: New actual time in hours.
            due_date: New due date in ISO format.
            assigned_to_id: New assignee user ID.
            parent_task_id: New parent task ID.
            order_index: New display order.
        """
        try:
            fields = {
                "title": title, "description": description, "task_type": task_type,
                "category": category, "priority": priority, "status": status,
                "story_points": story_points, "difficulty": difficulty,
                "estimated_time": estimated_time, "actual_time": actual_time,
                "due_date": due_date, "assigned_to_id": assigned_to_id,
                "parent_task_id": parent_task_id, "order_index": order_index,
            }
            body = {k: v for k, v in fields.items() if v is not None}
            if not body:
                return "No fields to update. Provide at least one field to change."

            data = await client.put(f"/tasks/{task_id}", body)
            return json.dumps(data, indent=2, default=str)
        except ApiError as e:
            return str(e)

    @mcp.tool()
    async def tms_delete_task(task_id: int) -> str:
        """Delete a task and all its subtasks.

        Args:
            task_id: The task ID to delete.
        """
        try:
            await client.delete(f"/tasks/{task_id}")
            return f"Task {task_id} deleted successfully."
        except ApiError as e:
            return str(e)
