"""MCP tool implementations for ccontext."""

from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Optional

from .schema import (
    Context,
    ContextResponse,
    Milestone,
    MilestoneStatus,
    Note,
    Reference,
    Step,
    StepStatus,
    Task,
    TasksSummary,
    TaskStatus,
)
from .storage import ContextStorage


# =============================================================================
# Tool Descriptions - Critical for Agent adoption
# =============================================================================

TOOL_DESCRIPTIONS = {
    # Context - THE entry point
    "get_context": (
        "Your project's execution memory - call this FIRST before starting any work. "
        "Shows: where the project is (milestones), current work (active task), "
        "accumulated wisdom (notes), and key locations (references). "
        "Essential to avoid duplicating work or missing critical information."
    ),

    # Milestone tools - project timeline
    "create_milestone": (
        "Start a new project phase that persists across sessions. "
        "Use when beginning major work (e.g., 'Phase 1: Core Implementation'). "
        "Milestones create a timeline showing project evolution."
    ),
    "update_milestone": (
        "Modify a milestone's details or advance its status (pending→active→done). "
        "Use when scope changes or when starting work on a pending phase."
    ),
    "complete_milestone": (
        "Close a milestone and record what was accomplished. "
        "Outcomes become permanent project history visible to future sessions."
    ),
    "remove_milestone": (
        "Delete a cancelled or mistaken milestone. "
        "Completed milestones should be kept as project history."
    ),

    # Task tools - work tracking
    "list_tasks": (
        "Find tasks by status (planned/active/done) or assignee. "
        "Use to see what work exists and check progress across sessions."
    ),
    "create_task": (
        "Break down work into trackable steps that persist across sessions. "
        "Use when starting work that spans multiple actions or needs progress tracking."
    ),
    "update_task": (
        "Update task progress - change status, mark steps done, or modify details. "
        "Keeps execution state accurate for session handoffs and work resumption."
    ),
    "delete_task": (
        "Remove a cancelled or mistaken task. "
        "Completed tasks auto-archive after 7 days - no need to delete them."
    ),

    # Note tools - knowledge preservation
    "add_note": (
        "Preserve important knowledge for future sessions - lessons, warnings, decisions. "
        "High scores (50-100) persist longer; low scores decay and auto-archive. "
        "Example: 'API rate limit is 100/min - batch requests to avoid throttling'"
    ),
    "update_note": (
        "Refresh a note's score to prevent decay, or update its content. "
        "Use when a note is still valuable but its score is getting low."
    ),
    "remove_note": (
        "Delete an obsolete note immediately. "
        "Prefer letting low-value notes decay naturally via score system."
    ),

    # Reference tools - navigation bookmarks
    "add_reference": (
        "Bookmark important file paths or URLs for quick navigation. "
        "High scores persist longer. Use for: key source files, API docs, configs. "
        "Example: 'src/core/auth.py - OAuth implementation'"
    ),
    "update_reference": (
        "Refresh a reference's score to prevent decay, or update its details. "
        "Use when a reference is still valuable but its score is getting low."
    ),
    "remove_reference": (
        "Delete an obsolete reference immediately. "
        "Prefer letting outdated references decay naturally via score system."
    ),
}


class ContextTools:
    """Implementation of ccontext MCP tools."""

    def __init__(self, root: Optional[Path] = None):
        """Initialize tools with project root directory."""
        self.storage = ContextStorage(root)

    # =========================================================================
    # Context Tool
    # =========================================================================

    def get_context(self) -> dict[str, Any]:
        """Get the full execution context.

        Each call triggers score decay for notes and references.
        """
        context = self.storage.load_context()

        # Decay scores and archive if needed
        context, archived_notes, archived_refs = self.storage.decay_scores(context)

        if archived_notes or archived_refs:
            self.storage.archive_entries(archived_notes, archived_refs)

        self.storage.save_context(context)

        # Archive old tasks
        self.storage.archive_old_tasks()

        # Get milestones for response (limited done + all active/pending)
        milestones = self.storage.get_milestones_for_response(context)

        # Build tasks summary
        tasks = self.storage.list_tasks(include_archived=False)
        summary = TasksSummary(
            total=len(tasks),
            done=len([t for t in tasks if t.status == TaskStatus.DONE]),
            active=len([t for t in tasks if t.status == TaskStatus.ACTIVE]),
            planned=len([t for t in tasks if t.status == TaskStatus.PLANNED]),
        )

        # Find active task
        active_task = None
        for task in tasks:
            if task.status == TaskStatus.ACTIVE:
                active_task = self._task_to_dict(task)
                break

        # Build response
        response = ContextResponse(
            milestones=[
                {
                    "id": m.id,
                    "name": m.name,
                    "description": m.description,
                    "status": m.status.value,
                    "started": m.started,
                    "completed": m.completed,
                    "outcomes": m.outcomes,
                }
                for m in milestones
            ],
            notes=[
                {
                    "id": n.id,
                    "content": n.content,
                    "score": n.score,
                    "expiring": n.expiring,
                }
                for n in context.notes
            ],
            references=[
                {
                    "id": r.id,
                    "url": r.url,
                    "note": r.note,
                    "score": r.score,
                    "expiring": r.expiring,
                }
                for r in context.references
            ],
            tasks_summary=summary,
            active_task=active_task,
        )

        return response.model_dump()

    # =========================================================================
    # Milestone Tools
    # =========================================================================

    def create_milestone(
        self,
        name: str,
        description: str,
        status: str = "pending",
    ) -> dict[str, Any]:
        """Create a new milestone."""
        context = self.storage.load_context()

        # Generate ID
        milestone_id = self.storage.generate_milestone_id(context)

        # Validate status
        try:
            status_enum = MilestoneStatus(status)
        except ValueError:
            raise ValueError(f"Invalid status: {status}. Must be done/active/pending.")

        # Set started date for active milestones
        started = None
        if status_enum == MilestoneStatus.ACTIVE:
            started = datetime.now(timezone.utc).strftime("%Y-%m-%d")

        milestone = Milestone(
            id=milestone_id,
            name=name,
            description=description,
            status=status_enum,
            started=started,
        )

        context.milestones.append(milestone)
        self.storage.save_context(context)

        return {
            "id": milestone.id,
            "name": milestone.name,
            "description": milestone.description,
            "status": milestone.status.value,
            "started": milestone.started,
        }

    def update_milestone(
        self,
        milestone_id: str,
        name: Optional[str] = None,
        description: Optional[str] = None,
        status: Optional[str] = None,
    ) -> dict[str, Any]:
        """Update an existing milestone."""
        context = self.storage.load_context()

        milestone = self.storage.get_milestone(context, milestone_id)
        if milestone is None:
            raise ValueError(f"Milestone not found: {milestone_id}")

        if name is not None:
            milestone.name = name

        if description is not None:
            milestone.description = description

        if status is not None:
            try:
                new_status = MilestoneStatus(status)
            except ValueError:
                raise ValueError(f"Invalid status: {status}")

            # Set started date when becoming active
            if new_status == MilestoneStatus.ACTIVE and milestone.status != MilestoneStatus.ACTIVE:
                if milestone.started is None:
                    milestone.started = datetime.now(timezone.utc).strftime("%Y-%m-%d")

            milestone.status = new_status

        self.storage.save_context(context)

        return {
            "id": milestone.id,
            "name": milestone.name,
            "description": milestone.description,
            "status": milestone.status.value,
            "started": milestone.started,
            "completed": milestone.completed,
            "outcomes": milestone.outcomes,
        }

    def complete_milestone(
        self,
        milestone_id: str,
        outcomes: str,
    ) -> dict[str, Any]:
        """Complete a milestone with outcomes."""
        context = self.storage.load_context()

        milestone = self.storage.get_milestone(context, milestone_id)
        if milestone is None:
            raise ValueError(f"Milestone not found: {milestone_id}")

        milestone.status = MilestoneStatus.DONE
        milestone.completed = datetime.now(timezone.utc).strftime("%Y-%m-%d")
        milestone.outcomes = outcomes

        self.storage.save_context(context)

        return {
            "id": milestone.id,
            "name": milestone.name,
            "description": milestone.description,
            "status": milestone.status.value,
            "started": milestone.started,
            "completed": milestone.completed,
            "outcomes": milestone.outcomes,
        }

    def remove_milestone(self, milestone_id: str) -> dict[str, Any]:
        """Remove a milestone."""
        context = self.storage.load_context()

        milestone = self.storage.get_milestone(context, milestone_id)
        if milestone is None:
            raise ValueError(f"Milestone not found: {milestone_id}")

        context.milestones = [m for m in context.milestones if m.id != milestone_id]
        self.storage.save_context(context)

        return {"deleted": True, "milestone_id": milestone_id}

    # =========================================================================
    # Task Tools
    # =========================================================================

    def list_tasks(
        self,
        task_id: Optional[str] = None,
        status: Optional[str] = None,
        assignee: Optional[str] = None,
        include_archived: bool = False,
    ) -> dict[str, Any] | list[dict[str, Any]]:
        """List or get tasks."""
        if task_id:
            task = self.storage.load_task(task_id, include_archived=include_archived)
            if task is None:
                raise ValueError(f"Task not found: {task_id}")
            return self._task_to_dict(task)

        tasks = self.storage.list_tasks(include_archived=include_archived)

        if status:
            try:
                status_enum = TaskStatus(status)
                tasks = [t for t in tasks if t.status == status_enum]
            except ValueError:
                raise ValueError(f"Invalid status: {status}")

        if assignee:
            tasks = [t for t in tasks if t.assignee == assignee]

        return [self._task_to_dict(t) for t in tasks]

    def create_task(
        self,
        name: str,
        goal: str,
        steps: list[dict[str, str]],
        task_id: Optional[str] = None,
        assignee: Optional[str] = None,
    ) -> dict[str, Any]:
        """Create a new task."""
        if task_id is None:
            task_id = self.storage.generate_task_id()

        if not task_id.startswith("T") or not task_id[1:].isdigit():
            raise ValueError(f"Invalid task ID format: {task_id}")

        if self.storage.load_task(task_id) is not None:
            raise ValueError(f"Task already exists: {task_id}")

        task_steps = []
        for i, s in enumerate(steps, start=1):
            step = Step(
                id=f"S{i}",
                name=s.get("name", ""),
                done=s.get("done", ""),
                status=StepStatus.PENDING,
            )
            task_steps.append(step)

        task = Task(
            id=task_id,
            name=name,
            goal=goal,
            status=TaskStatus.PLANNED,
            assignee=assignee,
            created_at=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
            steps=task_steps,
        )

        self.storage.save_task(task)

        return self._task_to_dict(task)

    def update_task(
        self,
        task_id: str,
        status: Optional[str] = None,
        name: Optional[str] = None,
        goal: Optional[str] = None,
        assignee: Optional[str] = None,
        step_id: Optional[str] = None,
        step_status: Optional[str] = None,
        steps: Optional[list[dict[str, Any]]] = None,
    ) -> dict[str, Any]:
        """Update a task."""
        task = self.storage.load_task(task_id)
        if task is None:
            raise ValueError(f"Task not found: {task_id}")

        if status is not None:
            try:
                task.status = TaskStatus(status)
            except ValueError:
                raise ValueError(f"Invalid status: {status}")

        if name is not None:
            task.name = name

        if goal is not None:
            task.goal = goal

        if assignee is not None:
            task.assignee = assignee

        if step_id is not None and step_status is not None:
            found = False
            for step in task.steps:
                if step.id == step_id:
                    try:
                        step.status = StepStatus(step_status)
                    except ValueError:
                        raise ValueError(f"Invalid step status: {step_status}")
                    found = True
                    break
            if not found:
                raise ValueError(f"Step not found: {step_id}")

        if steps is not None:
            new_steps = []
            for s in steps:
                step = Step(
                    id=s.get("id", ""),
                    name=s.get("name", ""),
                    done=s.get("done", ""),
                    status=StepStatus(s.get("status", "pending")),
                )
                new_steps.append(step)
            task.steps = new_steps

        self.storage.save_task(task)

        return self._task_to_dict(task)

    def delete_task(self, task_id: str) -> dict[str, Any]:
        """Delete a task."""
        if not self.storage.delete_task(task_id):
            raise ValueError(f"Task not found: {task_id}")

        return {"deleted": True, "task_id": task_id}

    # =========================================================================
    # Note Tools
    # =========================================================================

    def add_note(self, content: str, score: int = 15) -> dict[str, Any]:
        """Add a note."""
        if score < 10 or score > 100:
            raise ValueError(f"Score must be between 10 and 100, got: {score}")

        context = self.storage.load_context()

        note_id = self.storage.generate_note_id(context)
        note = Note(id=note_id, content=content, score=score)
        context.notes.append(note)

        self.storage.save_context(context)

        return {"id": note_id, "content": content, "score": score}

    def update_note(
        self,
        note_id: str,
        content: Optional[str] = None,
        score: Optional[int] = None,
    ) -> dict[str, Any]:
        """Update a note."""
        context = self.storage.load_context()

        note = self.storage.get_note_by_id(context, note_id)
        if note is None:
            raise ValueError(f"Note not found: {note_id}")

        if content is not None:
            note.content = content

        if score is not None:
            if score < 10 or score > 100:
                raise ValueError(f"Score must be between 10 and 100, got: {score}")
            note.score = score

        self.storage.save_context(context)

        return {"id": note.id, "content": note.content, "score": note.score}

    def remove_note(self, note_id: str) -> dict[str, Any]:
        """Remove a note."""
        context = self.storage.load_context()

        note = self.storage.get_note_by_id(context, note_id)
        if note is None:
            raise ValueError(f"Note not found: {note_id}")

        context.notes = [n for n in context.notes if n.id != note_id]
        self.storage.save_context(context)

        return {"deleted": True, "note_id": note_id}

    # =========================================================================
    # Reference Tools
    # =========================================================================

    def add_reference(self, url: str, note: str, score: int = 15) -> dict[str, Any]:
        """Add a reference."""
        if score < 10 or score > 100:
            raise ValueError(f"Score must be between 10 and 100, got: {score}")

        context = self.storage.load_context()

        ref_id = self.storage.generate_reference_id(context)
        ref = Reference(id=ref_id, url=url, note=note, score=score)
        context.references.append(ref)

        self.storage.save_context(context)

        return {"id": ref_id, "url": url, "note": note, "score": score}

    def update_reference(
        self,
        reference_id: str,
        url: Optional[str] = None,
        note: Optional[str] = None,
        score: Optional[int] = None,
    ) -> dict[str, Any]:
        """Update a reference."""
        context = self.storage.load_context()

        ref = self.storage.get_reference_by_id(context, reference_id)
        if ref is None:
            raise ValueError(f"Reference not found: {reference_id}")

        if url is not None:
            ref.url = url

        if note is not None:
            ref.note = note

        if score is not None:
            if score < 10 or score > 100:
                raise ValueError(f"Score must be between 10 and 100, got: {score}")
            ref.score = score

        self.storage.save_context(context)

        return {"id": ref.id, "url": ref.url, "note": ref.note, "score": ref.score}

    def remove_reference(self, reference_id: str) -> dict[str, Any]:
        """Remove a reference."""
        context = self.storage.load_context()

        ref = self.storage.get_reference_by_id(context, reference_id)
        if ref is None:
            raise ValueError(f"Reference not found: {reference_id}")

        context.references = [r for r in context.references if r.id != reference_id]
        self.storage.save_context(context)

        return {"deleted": True, "reference_id": reference_id}

    # =========================================================================
    # Helpers
    # =========================================================================

    def _task_to_dict(self, task: Task) -> dict[str, Any]:
        """Convert a Task to a dict with computed fields."""
        current_step = task.current_step
        return {
            "id": task.id,
            "name": task.name,
            "goal": task.goal,
            "status": task.status.value,
            "assignee": task.assignee,
            "created_at": task.created_at,
            "steps": [
                {
                    "id": s.id,
                    "name": s.name,
                    "done": s.done,
                    "status": s.status.value,
                }
                for s in task.steps
            ],
            "current_step": current_step.id if current_step else None,
            "progress": task.progress,
        }
