from __future__ import annotations

# standard
from pathlib import Path
from typing import Annotated, Literal

# third party
# custom
from sunwaee.config import get_default_workspace, get_module_dir, get_workspace_dir
from sunwaee.core.fs import find_md, list_md, make_slug, read_md, search_md, write_md
from sunwaee.core.git import ensure_git_repo, ws_commit
from sunwaee.core.tools import err, ok, tool
from sunwaee.modules.tasks.model import Task, TaskPriority, TaskStatus


def _ws(workspace: str | None) -> str:
    return workspace or get_default_workspace()


def _dir(workspace: str) -> Path:
    return get_module_dir(workspace, "tasks")


def _ws_dir(workspace: str) -> Path:
    return get_workspace_dir(workspace)


@tool(
    "Manage tasks. Actions: "
    "'create' (requires title), "
    "'list' (filter by status/priority/query), "
    "'read' (requires query, returns task data), "
    "'update' (requires query, updates only provided fields), "
    "'delete' (requires query, removes task + sub-tasks)."
)
def tasks(
    action: Annotated[
        Literal["create", "list", "read", "update", "delete"],
        "Action to perform",
    ],
    query: Annotated[
        str | None,
        "Task id, slug, or title — required for read/update/delete",
    ] = None,
    workspace: Annotated[str | None, "Workspace name (uses default if omitted)"] = None,
    title: Annotated[str | None, "Task title — required for create"] = None,
    status: Annotated[
        Literal["todo", "in_progress", "done"] | None, "Task status"
    ] = None,
    priority: Annotated[
        Literal["low", "medium", "high"] | None,
        "Task priority (default: medium for create)",
    ] = None,
    due_date: Annotated[str | None, "Due date in ISO format YYYY-MM-DD"] = None,
    tags: Annotated[list[str] | None, "List of tags"] = None,
    body: Annotated[str | None, "Task description / notes"] = None,
    parent_id: Annotated[str | None, "UUID of the parent task for sub-tasks"] = None,
) -> str:
    ws = _ws(workspace)
    d = _dir(ws)
    ws_d = _ws_dir(ws)
    ensure_git_repo(ws_d)

    if action == "create":
        if not title:
            return err("'title' is required for action='create'")
        task = Task(
            title=title,
            priority=TaskPriority(priority or "medium"),
            due_date=due_date,
            tags=tags or [],
            body=body or "",
            parent_id=parent_id,
        )
        path = d / f"{make_slug(title, task.id)}.md"
        write_md(path, task.to_frontmatter_meta(), task.body)
        diff = ws_commit(ws_d, f"tasks: create '{task.title}'")
        return ok(task.to_dict(), git_diff=diff)

    if action == "list":
        paths = search_md(query, d) if query else list_md(d)
        result = []
        for p in paths:
            meta, b = read_md(p)
            task = Task.from_dict(meta, b)
            if status and task.status.value != status:
                continue
            if priority and task.priority.value != priority:
                continue
            result.append(task.to_dict())
        return ok(result)

    # all remaining actions require query
    if not query:
        return err(f"'query' is required for action={action!r}")

    if action == "read":
        path = find_md(query, d)
        if not path:
            return err(f"Task not found: {query!r}")
        meta, b = read_md(path)
        return ok(Task.from_dict(meta, b).to_dict())

    if action == "update":
        path = find_md(query, d)
        if not path:
            return err(f"Task not found: {query!r}")
        meta, existing_body = read_md(path)
        task = Task.from_dict(meta, existing_body)
        if title is not None:  # pragma: no cover
            task.title = title
        if status is not None:
            task.status = TaskStatus(status)
        if priority is not None:  # pragma: no cover
            task.priority = TaskPriority(priority)
        if due_date is not None:  # pragma: no cover
            task.due_date = due_date
        if tags is not None:  # pragma: no cover
            task.tags = tags
        if body is not None:  # pragma: no cover
            task.body = body
        task.touch()
        write_md(path, task.to_frontmatter_meta(), task.body)
        diff = ws_commit(ws_d, f"tasks: update '{task.title}'")
        return ok(task.to_dict(), git_diff=diff)

    if action == "delete":
        path = find_md(query, d)
        if not path:
            return err(f"Task not found: {query!r}")
        meta, _ = read_md(path)
        task_id = meta.get("id")
        title_for_commit = meta.get("title", query)
        path.unlink()
        for p in list_md(d):
            m, _ = read_md(p)
            if m.get("parent_id") == task_id:
                p.unlink()
        diff = ws_commit(ws_d, f"tasks: delete '{title_for_commit}'")
        return ok({"deleted": query}, git_diff=diff)

    return err(f"Unknown action: {action!r}")  # pragma: no cover


TOOLS = [tasks._tool]
