"""Milestone CRUD MCP tools — thin wrappers over services/milestones.py.

Mirrors existing fable-mcp milestone tool contracts: list/create/update. The
existing surface has no fable_get_milestone or fable_delete_milestone — kept
that way for parity.

Sentinels:
  - title="" / description="" / status="" → "leave unchanged" on update
  - order_index=-1 → "leave unchanged" on update (0 is a valid order_index)
  - status="active" default on create
"""
from __future__ import annotations

from scribe.mcp._context import current_user_id
from scribe.services import milestones as milestones_svc
from scribe.services import notes as notes_svc
from scribe.services import rulebooks as rulebooks_svc
from scribe.services import trash as trash_svc


async def list_milestones(project_id: int) -> dict:
    """List milestones for a Scribe project, ordered by order_index.

    Returns id, title, description, body (the plan/design), status
    (active/done), order_index, and task counts.
    """
    uid = current_user_id()
    rows = await milestones_svc.get_project_milestone_summary(uid, project_id)
    return {"milestones": rows}


async def get_milestone(milestone_id: int) -> dict:
    """Fetch a milestone (the plan container) with its step-tasks and rules.

    A milestone IS a plan: its `body` holds the design/intent, and its steps
    are the child tasks listed here. Use this to read a plan top-to-bottom —
    the body for the design, `steps` for the trackable units of work. Mirrors
    the planning context that start_planning returns (applicable rules), so the
    rules surface again on recall.

    Returns: milestone (incl. body), progress, steps (its tasks ordered by
    status then update), and applicable_rules / subscribed_rulebooks.
    """
    uid = current_user_id()
    milestone = await milestones_svc.get_milestone(uid, milestone_id)
    if milestone is None:
        raise ValueError(f"milestone {milestone_id} not found")
    progress = await milestones_svc.get_milestone_progress(milestone_id)
    steps, _ = await notes_svc.list_notes(
        uid, is_task=True, milestone_id=milestone_id, sort="status", limit=200,
    )
    applicable = await rulebooks_svc.get_applicable_rules(
        project_id=milestone.project_id, user_id=uid,
    )
    out = milestone.to_dict()
    out.update(progress)
    return {
        "milestone": out,
        "steps": [t.to_dict() for t in steps],
        **rulebooks_svc.rules_payload(applicable),
    }


async def create_milestone(
    project_id: int,
    title: str,
    description: str = "",
    body: str = "",
    status: str = "active",
) -> dict:
    """Create a milestone within a Scribe project.

    A milestone can serve as a plan container — put the design/intent in `body`
    and track each step as a child task (create_task(milestone_id=...)). For a
    fresh plan, prefer start_planning, which seeds the body template + surfaces
    the project's rules.

    Args:
        project_id: The project this milestone belongs to (required).
        title: Milestone name (required).
        description: Optional one-line summary of what this milestone covers.
        body: Optional plan/design (markdown) — the milestone's full plan text.
        status: active (default) or done.
    """
    uid = current_user_id()
    milestone = await milestones_svc.create_milestone(
        uid,
        project_id=project_id,
        title=title,
        description=description or None,
        body=body or None,
        status=status,
    )
    return milestone.to_dict()


async def update_milestone(
    project_id: int,
    milestone_id: int,
    title: str = "",
    description: str = "",
    body: str = "",
    status: str = "",
    order_index: int = -1,
) -> dict:
    """Update a Scribe milestone. Only explicitly provided fields are changed.

    Args:
        project_id: Project the milestone belongs to (preserved for API parity;
            ownership scoping is enforced by user_id at the service layer).
        milestone_id: ID of the milestone to update.
        title: New title, or omit to leave unchanged.
        description: New one-line summary, or omit to leave unchanged.
        body: New plan/design (markdown), or omit to leave unchanged.
        status: New status — active or done.
        order_index: New display position (0-based). Use -1 to leave unchanged.
    """
    uid = current_user_id()
    fields: dict = {}
    if title:
        fields["title"] = title
    if description:
        fields["description"] = description
    if body:
        fields["body"] = body
    if status:
        fields["status"] = status
    if order_index >= 0:
        fields["order_index"] = order_index
    milestone = await milestones_svc.update_milestone(uid, milestone_id, **fields)
    if milestone is None:
        raise ValueError(f"milestone {milestone_id} not found")
    return milestone.to_dict()


async def delete_milestone(milestone_id: int) -> dict:
    """Move a milestone to the trash (recoverable). Its tasks go with it as one batch.
    Restore via restore(batch_id)."""
    uid = current_user_id()
    batch = await trash_svc.delete(uid, "milestone", milestone_id)
    if batch is None:
        raise ValueError(f"milestone {milestone_id} not found")
    return {"deleted_batch_id": batch,
            "message": f"Milestone {milestone_id} + its tasks moved to trash. Restore with restore('{batch}')."}


def register(mcp) -> None:
    for fn in (
        list_milestones,
        get_milestone,
        create_milestone,
        update_milestone,
        delete_milestone,
    ):
        mcp.tool(name=fn.__name__)(fn)
