"""
TodoTool - Task management for tracking work progress

Following OpenCode architecture, provides todo list management with:
- Add, update, complete, delete tasks
- Priority and status tracking
- Persistent storage in JSON
- Type-safe implementation
"""

import json
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Any, Optional

from groknroll.tools.base_tool import BaseTool


class TodoStatus(Enum):
    """Status of a todo item"""

    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    CANCELLED = "cancelled"


class TodoPriority(Enum):
    """Priority of a todo item"""

    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"


@dataclass
class TodoItem:
    """A single todo item"""

    id: str
    content: str
    status: TodoStatus = TodoStatus.PENDING
    priority: TodoPriority = TodoPriority.MEDIUM
    created_at: datetime = field(default_factory=datetime.now)
    updated_at: datetime = field(default_factory=datetime.now)
    completed_at: Optional[datetime] = None
    tags: list[str] = field(default_factory=list)
    active_form: Optional[str] = None  # Present tense form for display

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for JSON serialization"""
        return {
            "id": self.id,
            "content": self.content,
            "status": self.status.value,
            "priority": self.priority.value,
            "created_at": self.created_at.isoformat(),
            "updated_at": self.updated_at.isoformat(),
            "completed_at": self.completed_at.isoformat() if self.completed_at else None,
            "tags": self.tags,
            "active_form": self.active_form,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "TodoItem":
        """Create from dictionary"""
        return cls(
            id=data["id"],
            content=data["content"],
            status=TodoStatus(data.get("status", "pending")),
            priority=TodoPriority(data.get("priority", "medium")),
            created_at=datetime.fromisoformat(data["created_at"])
            if "created_at" in data
            else datetime.now(),
            updated_at=datetime.fromisoformat(data["updated_at"])
            if "updated_at" in data
            else datetime.now(),
            completed_at=datetime.fromisoformat(data["completed_at"])
            if data.get("completed_at")
            else None,
            tags=data.get("tags", []),
            active_form=data.get("active_form"),
        )

    def format_line(self) -> str:
        """Format as a single line for display"""
        status_icons = {
            TodoStatus.PENDING: "[ ]",
            TodoStatus.IN_PROGRESS: "[>]",
            TodoStatus.COMPLETED: "[x]",
            TodoStatus.CANCELLED: "[-]",
        }
        priority_marks = {
            TodoPriority.LOW: "",
            TodoPriority.MEDIUM: "",
            TodoPriority.HIGH: "!",
            TodoPriority.CRITICAL: "!!",
        }
        icon = status_icons[self.status]
        priority = priority_marks[self.priority]
        tags_str = " ".join(f"#{t}" for t in self.tags) if self.tags else ""

        line = f"{icon} {self.content}"
        if priority:
            line = f"{line} {priority}"
        if tags_str:
            line = f"{line} {tags_str}"

        return line


@dataclass
class TodoList:
    """A collection of todo items"""

    items: list[TodoItem] = field(default_factory=list)
    name: str = "default"
    created_at: datetime = field(default_factory=datetime.now)

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary"""
        return {
            "name": self.name,
            "created_at": self.created_at.isoformat(),
            "items": [item.to_dict() for item in self.items],
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "TodoList":
        """Create from dictionary"""
        return cls(
            name=data.get("name", "default"),
            created_at=datetime.fromisoformat(data["created_at"])
            if "created_at" in data
            else datetime.now(),
            items=[TodoItem.from_dict(item) for item in data.get("items", [])],
        )

    def get_by_id(self, item_id: str) -> Optional[TodoItem]:
        """Get item by ID"""
        for item in self.items:
            if item.id == item_id:
                return item
        return None

    def get_by_status(self, status: TodoStatus) -> list[TodoItem]:
        """Get items by status"""
        return [item for item in self.items if item.status == status]

    def format_output(self) -> str:
        """Format as human-readable output"""
        if not self.items:
            return "No todos"

        lines = [f"Todo List: {self.name}", ""]

        # Group by status
        groups = {
            "In Progress": self.get_by_status(TodoStatus.IN_PROGRESS),
            "Pending": self.get_by_status(TodoStatus.PENDING),
            "Completed": self.get_by_status(TodoStatus.COMPLETED),
            "Cancelled": self.get_by_status(TodoStatus.CANCELLED),
        }

        for group_name, items in groups.items():
            if items:
                lines.append(f"## {group_name}")
                for item in items:
                    lines.append(f"  {item.format_line()}")
                lines.append("")

        return "\n".join(lines)


class TodoWriteTool(BaseTool):
    """
    Tool for writing/managing todo items

    Accepts:
        action: str - "add", "update", "complete", "delete", "clear"
        content: str (for add) - Todo content
        id: str (for update/complete/delete) - Todo item ID
        status: str (for update) - New status
        priority: str (for add/update) - Priority level
        tags: list[str] (for add/update) - Tags
        todos: list[dict] (for bulk set) - Complete todo list

    Returns:
        dict with operation result

    Example:
        tool = TodoWriteTool()

        # Add a todo
        result = await tool.execute(
            action="add",
            content="Implement feature",
            priority="high"
        )

        # Complete a todo
        result = await tool.execute(action="complete", id="todo-1")

        # Bulk update
        result = await tool.execute(
            todos=[
                {"content": "Task 1", "status": "pending"},
                {"content": "Task 2", "status": "in_progress"},
            ]
        )
    """

    def __init__(self, workspace_root: Optional[str] = None):
        """
        Initialize TodoWriteTool

        Args:
            workspace_root: Optional root directory for storage.
                           Defaults to current working directory.
        """
        self._workspace_root = Path(workspace_root) if workspace_root else Path.cwd()
        self._storage_path = self._workspace_root / ".groknroll" / "todos.json"
        self._next_id = 1
        self._todo_list: Optional[TodoList] = None

    @property
    def name(self) -> str:
        """Tool identifier"""
        return "todowrite"

    @property
    def description(self) -> str:
        """Human-readable description"""
        return "Manage todo items (add, update, complete, delete)"

    def _ensure_loaded(self) -> TodoList:
        """Ensure todo list is loaded"""
        if self._todo_list is None:
            self._todo_list = self._load()
        return self._todo_list

    def _load(self) -> TodoList:
        """Load todo list from storage"""
        if self._storage_path.exists():
            try:
                data = json.loads(self._storage_path.read_text(encoding="utf-8"))
                todo_list = TodoList.from_dict(data)
                # Update next ID
                for item in todo_list.items:
                    try:
                        item_num = int(item.id.replace("todo-", ""))
                        self._next_id = max(self._next_id, item_num + 1)
                    except ValueError:
                        pass
                return todo_list
            except Exception:
                return TodoList()
        return TodoList()

    def _save(self, todo_list: TodoList) -> None:
        """Save todo list to storage"""
        self._storage_path.parent.mkdir(parents=True, exist_ok=True)
        self._storage_path.write_text(json.dumps(todo_list.to_dict(), indent=2), encoding="utf-8")
        self._todo_list = todo_list

    def _generate_id(self) -> str:
        """Generate a new todo ID"""
        todo_id = f"todo-{self._next_id}"
        self._next_id += 1
        return todo_id

    def validate_params(self, **kwargs) -> dict[str, Any]:
        """Validate parameters"""
        # If 'todos' is provided, it's a bulk set operation
        if "todos" in kwargs:
            todos = kwargs["todos"]
            if not isinstance(todos, list):
                raise ValueError(f"todos must be a list, got {type(todos).__name__}")
            return kwargs

        # Otherwise, action is required
        action = kwargs.get("action")
        if not action:
            raise ValueError("action parameter is required (or use 'todos' for bulk set)")

        valid_actions = {"add", "update", "complete", "delete", "clear"}
        if action not in valid_actions:
            raise ValueError(f"action must be one of {valid_actions}, got {action}")

        # Validate action-specific requirements
        if action == "add" and not kwargs.get("content"):
            raise ValueError("content is required for add action")

        if action in ("update", "complete", "delete") and not kwargs.get("id"):
            raise ValueError(f"id is required for {action} action")

        return kwargs

    async def execute(self, **kwargs) -> dict[str, Any]:
        """Execute todo write operation"""
        validated = self.validate_params(**kwargs)

        # Handle bulk set
        if "todos" in validated:
            return self._bulk_set(validated["todos"])

        action = validated["action"]

        if action == "add":
            return self._add(
                content=validated["content"],
                priority=validated.get("priority", "medium"),
                tags=validated.get("tags", []),
                active_form=validated.get("active_form"),
            )
        elif action == "update":
            return self._update(
                item_id=validated["id"],
                content=validated.get("content"),
                status=validated.get("status"),
                priority=validated.get("priority"),
                tags=validated.get("tags"),
                active_form=validated.get("active_form"),
            )
        elif action == "complete":
            return self._complete(validated["id"])
        elif action == "delete":
            return self._delete(validated["id"])
        elif action == "clear":
            return self._clear()

        return {"success": False, "error": f"Unknown action: {action}"}

    def _add(
        self,
        content: str,
        priority: str = "medium",
        tags: list[str] = None,
        active_form: Optional[str] = None,
    ) -> dict[str, Any]:
        """Add a new todo item"""
        todo_list = self._ensure_loaded()

        item = TodoItem(
            id=self._generate_id(),
            content=content,
            priority=TodoPriority(priority),
            tags=tags or [],
            active_form=active_form,
        )
        todo_list.items.append(item)
        self._save(todo_list)

        return {
            "success": True,
            "action": "add",
            "item": item.to_dict(),
        }

    def _update(
        self,
        item_id: str,
        content: Optional[str] = None,
        status: Optional[str] = None,
        priority: Optional[str] = None,
        tags: Optional[list[str]] = None,
        active_form: Optional[str] = None,
    ) -> dict[str, Any]:
        """Update a todo item"""
        todo_list = self._ensure_loaded()
        item = todo_list.get_by_id(item_id)

        if not item:
            return {"success": False, "error": f"Todo not found: {item_id}"}

        if content is not None:
            item.content = content
        if status is not None:
            item.status = TodoStatus(status)
            if item.status == TodoStatus.COMPLETED:
                item.completed_at = datetime.now()
        if priority is not None:
            item.priority = TodoPriority(priority)
        if tags is not None:
            item.tags = tags
        if active_form is not None:
            item.active_form = active_form

        item.updated_at = datetime.now()
        self._save(todo_list)

        return {
            "success": True,
            "action": "update",
            "item": item.to_dict(),
        }

    def _complete(self, item_id: str) -> dict[str, Any]:
        """Mark a todo as completed"""
        return self._update(item_id, status="completed")

    def _delete(self, item_id: str) -> dict[str, Any]:
        """Delete a todo item"""
        todo_list = self._ensure_loaded()
        original_count = len(todo_list.items)
        todo_list.items = [item for item in todo_list.items if item.id != item_id]

        if len(todo_list.items) == original_count:
            return {"success": False, "error": f"Todo not found: {item_id}"}

        self._save(todo_list)
        return {"success": True, "action": "delete", "id": item_id}

    def _clear(self) -> dict[str, Any]:
        """Clear all completed todos"""
        todo_list = self._ensure_loaded()
        original_count = len(todo_list.items)
        todo_list.items = [item for item in todo_list.items if item.status != TodoStatus.COMPLETED]

        cleared = original_count - len(todo_list.items)
        self._save(todo_list)

        return {"success": True, "action": "clear", "cleared": cleared}

    def _bulk_set(self, todos: list[dict[str, Any]]) -> dict[str, Any]:
        """Set the complete todo list from a list of dicts"""
        todo_list = self._ensure_loaded()

        # Clear existing and add new
        todo_list.items = []

        for todo_data in todos:
            content = todo_data.get("content", "")
            status = todo_data.get("status", "pending")
            priority = todo_data.get("priority", "medium")
            active_form = todo_data.get("activeForm") or todo_data.get("active_form")

            item = TodoItem(
                id=self._generate_id(),
                content=content,
                status=TodoStatus(status),
                priority=TodoPriority(priority),
                active_form=active_form,
            )
            todo_list.items.append(item)

        self._save(todo_list)

        return {
            "success": True,
            "action": "bulk_set",
            "count": len(todo_list.items),
        }


class TodoReadTool(BaseTool):
    """
    Tool for reading todo items

    Accepts:
        id: str (optional) - Get specific todo by ID
        status: str (optional) - Filter by status
        format: str (optional) - Output format: "json" or "text"

    Returns:
        dict with todo list or single item

    Example:
        tool = TodoReadTool()

        # Get all todos
        result = await tool.execute()

        # Get by status
        result = await tool.execute(status="in_progress")

        # Get formatted output
        result = await tool.execute(format="text")
    """

    def __init__(self, workspace_root: Optional[str] = None):
        """
        Initialize TodoReadTool

        Args:
            workspace_root: Optional root directory for storage.
                           Defaults to current working directory.
        """
        self._workspace_root = Path(workspace_root) if workspace_root else Path.cwd()
        self._storage_path = self._workspace_root / ".groknroll" / "todos.json"

    @property
    def name(self) -> str:
        """Tool identifier"""
        return "todoread"

    @property
    def description(self) -> str:
        """Human-readable description"""
        return "Read todo items"

    def _load(self) -> TodoList:
        """Load todo list from storage"""
        if self._storage_path.exists():
            try:
                data = json.loads(self._storage_path.read_text(encoding="utf-8"))
                return TodoList.from_dict(data)
            except Exception:
                return TodoList()
        return TodoList()

    def validate_params(self, **kwargs) -> dict[str, Any]:
        """Validate parameters"""
        status = kwargs.get("status")
        if status:
            valid_statuses = {"pending", "in_progress", "completed", "cancelled"}
            if status not in valid_statuses:
                raise ValueError(f"status must be one of {valid_statuses}, got {status}")

        output_format = kwargs.get("format", "json")
        if output_format not in ("json", "text"):
            raise ValueError(f"format must be 'json' or 'text', got {output_format}")

        return kwargs

    async def execute(self, **kwargs) -> dict[str, Any]:
        """Execute todo read operation"""
        validated = self.validate_params(**kwargs)

        item_id = validated.get("id")
        status_filter = validated.get("status")
        output_format = validated.get("format", "json")

        todo_list = self._load()

        # Get specific item
        if item_id:
            item = todo_list.get_by_id(item_id)
            if not item:
                return {"success": False, "error": f"Todo not found: {item_id}"}
            return {"success": True, "item": item.to_dict()}

        # Filter by status
        if status_filter:
            items = todo_list.get_by_status(TodoStatus(status_filter))
        else:
            items = todo_list.items

        # Format output
        if output_format == "text":
            return {
                "success": True,
                "output": todo_list.format_output(),
                "count": len(items),
            }

        return {
            "success": True,
            "items": [item.to_dict() for item in items],
            "count": len(items),
        }
