"""Note tools for the MCP server.

Notes are managed via the /api/notes endpoints in the Personal Assistant API.
"""

import time

import httpx

from personal_assistant_mcp.client import AuthClient

# Simple in-memory cache for tag name → id resolution
_tag_cache: dict = {"data": {}, "fetched_at": 0.0}
_TAG_CACHE_TTL = 60  # seconds


async def _get_tag_map(client: AuthClient) -> dict[str, str]:
    """Return a lowercase tag-name → id mapping, cached for 60s."""
    now = time.time()
    if _tag_cache["data"] and (now - _tag_cache["fetched_at"]) < _TAG_CACHE_TTL:
        return _tag_cache["data"]

    resp = await client.get("/api/tags")
    all_tags = resp.json()
    mapping = {t["name"].lower(): str(t["id"]) for t in all_tags}
    _tag_cache["data"] = mapping
    _tag_cache["fetched_at"] = now
    return mapping


async def _resolve_tag_names(
    client: AuthClient, tag_names: list[str],
) -> tuple[list[str], list[str]]:
    """Resolve tag names to UUIDs.

    Returns:
        Tuple of (resolved_ids, unresolved_names).
    """
    name_to_id = await _get_tag_map(client)
    ids = []
    not_found = []
    for name in tag_names:
        tag_id = name_to_id.get(name.lower())
        if tag_id:
            ids.append(tag_id)
        else:
            not_found.append(name)
    return ids, not_found


def _format_note(n: dict) -> dict:
    """Extract note-relevant fields from a NoteResponse or NoteSummaryResponse."""
    result: dict = {
        "id": str(n["id"]),
        "title": n.get("title", ""),
        "tags": [t["name"] for t in n.get("tags", [])],
    }
    if "content" in n:
        result["content"] = n["content"] or ""
    if "excerpt" in n:
        result["excerpt"] = n["excerpt"] or ""
    if "created_at" in n:
        result["created_at"] = n["created_at"]
    if "updated_at" in n:
        result["updated_at"] = n["updated_at"]
    return result


async def list_notes(
    client: AuthClient,
    tag: str | None = None,
    q: str | None = None,
    page: int = 1,
) -> dict:
    """List notes with optional filters.

    Args:
        client: Authenticated API client.
        tag: Optional tag name to filter by.
        q: Optional search query.
        page: Page number (default 1).

    Returns:
        Dict with notes list, total, page, page_size, has_more.
    """
    try:
        params: dict = {"page": page}
        if tag:
            name_to_id = await _get_tag_map(client)
            tag_id = name_to_id.get(tag.lower())
            if tag_id:
                params["tag_id"] = tag_id
            else:
                return {"notes": [], "total": 0, "page": page, "page_size": 20,
                        "has_more": False, "warning": f"Tag '{tag}' not found"}
        if q:
            params["q"] = q

        resp = await client.get("/api/notes", params=params)
        data = resp.json()

        return {
            "notes": [_format_note(n) for n in data["notes"]],
            "total": data["total"],
            "page": data["page"],
            "page_size": data["page_size"],
            "has_more": data["has_more"],
        }
    except httpx.HTTPStatusError as e:
        return {"error": f"API error: {e.response.status_code} {e.response.text}"}
    except httpx.HTTPError as e:
        return {"error": f"Connection error: {e}"}


async def get_note(client: AuthClient, note_id: str) -> dict:
    """Get a note's full content.

    Args:
        client: Authenticated API client.
        note_id: UUID of the note.

    Returns:
        Note dict with id, title, content, tags, created_at, updated_at.
    """
    try:
        resp = await client.get(f"/api/notes/{note_id}")
        return _format_note(resp.json())
    except httpx.HTTPStatusError as e:
        return {"error": f"API error: {e.response.status_code} {e.response.text}"}
    except httpx.HTTPError as e:
        return {"error": f"Connection error: {e}"}


async def create_note(
    client: AuthClient,
    title: str,
    content: str = "",
    tag_names: list[str] | None = None,
) -> dict:
    """Create a new note.

    Args:
        client: Authenticated API client.
        title: Note title.
        content: Markdown content.
        tag_names: Optional list of tag names to associate.

    Returns:
        Created note dict. Includes 'warning' key if some tags were not found.
    """
    try:
        body: dict = {"title": title, "content": content}
        warning = None
        if tag_names:
            tag_ids, not_found = await _resolve_tag_names(client, tag_names)
            if tag_ids:
                body["tag_ids"] = tag_ids
            if not_found:
                warning = f"Tags not found: {', '.join(not_found)}"

        resp = await client.post("/api/notes", json=body)
        result = _format_note(resp.json())
        if warning:
            result["warning"] = warning
        return result
    except httpx.HTTPStatusError as e:
        return {"error": f"API error: {e.response.status_code} {e.response.text}"}
    except httpx.HTTPError as e:
        return {"error": f"Connection error: {e}"}


async def update_note(
    client: AuthClient,
    note_id: str,
    title: str | None = None,
    content: str | None = None,
    tag_names: list[str] | None = None,
) -> dict:
    """Update an existing note.

    Args:
        client: Authenticated API client.
        note_id: UUID of the note.
        title: New title (optional).
        content: New content (optional).
        tag_names: New tag names (optional).

    Returns:
        Updated note dict. Includes 'warning' key if some tags were not found.
    """
    try:
        body: dict = {}
        warning = None
        if title is not None:
            body["title"] = title
        if content is not None:
            body["content"] = content
        if tag_names is not None:
            tag_ids, not_found = await _resolve_tag_names(client, tag_names)
            body["tag_ids"] = tag_ids
            if not_found:
                warning = f"Tags not found: {', '.join(not_found)}"

        resp = await client.put(f"/api/notes/{note_id}", json=body)
        result = _format_note(resp.json())
        if warning:
            result["warning"] = warning
        return result
    except httpx.HTTPStatusError as e:
        return {"error": f"API error: {e.response.status_code} {e.response.text}"}
    except httpx.HTTPError as e:
        return {"error": f"Connection error: {e}"}
