"""MCP tool for pinning named context to a session."""

import logging
import re
from typing import Annotated, Any

from arcade_tdk import ToolContext, tool

from cade_mcp_local.errors import InvalidInputError, PathNotFoundError
from cade_mcp_local.utils import resolve_path

log = logging.getLogger(__name__)

MAX_PIN_BYTES = 100_000  # 100KB cap
PREVIEW_CHARS = 2000


@tool(
    name="PinContext",
    desc=(
        "Pin named reference material to the current session. The user can later "
        "ask about it and you can retrieve the full content via RetrieveToolResult. "
        "Use when the user provides specs, docs, notes, or other material they want "
        "you to remember. Provide either content (inline text) or file_path (read from file)."
    ),
)
def pin_context_tool(
    context: ToolContext,
    name: Annotated[
        str,
        "Short identifier for this context (e.g., 'api-spec', 'design-notes'). "
        "Letters, numbers, hyphens, underscores only.",
    ],
    thread_id: Annotated[str, "The current thread/session ID."],
    content: Annotated[
        str,
        "The text content to pin. Provide this OR file_path, not both.",
    ] = "",
    file_path: Annotated[
        str,
        "Path to a file to read and pin. Relative to project root.",
    ] = "",
) -> Annotated[dict[str, Any], "Pin result with preview."]:
    """Pin named reference material to the session."""
    if not name or not re.match(r"^[a-zA-Z0-9_-]+$", name):
        raise InvalidInputError("name", "must be alphanumeric with hyphens/underscores only")

    if not thread_id:
        raise InvalidInputError("thread_id", "thread ID is required")

    source = "inline"
    source_path = ""

    if file_path and content:
        raise InvalidInputError("content/file_path", "provide one or the other, not both")

    if file_path:
        resolved = resolve_path(file_path, confine_to_root=True)
        if not resolved.is_file():
            raise PathNotFoundError(file_path)
        source = "file"
        source_path = file_path
        content = resolved.read_text(encoding="utf-8", errors="replace")

    if not content or not content.strip():
        raise InvalidInputError("content", "cannot be empty")

    if len(content) > MAX_PIN_BYTES:
        raise InvalidInputError("content", f"exceeds {MAX_PIN_BYTES // 1024}KB limit")

    from cadecoder.execution.tool_result_store import ToolResultStore

    store = ToolResultStore()

    # Check for duplicate name
    existing = store.get_user_context_by_name(thread_id, name)
    if existing:
        raise InvalidInputError(
            "name",
            f"'{name}' already pinned (id={existing.record_id}). Use /unpin to remove first.",
        )

    record = store.store_user_context(
        thread_id=thread_id,
        name=name,
        content=content,
        source=source,
        source_path=source_path,
    )

    # Build preview
    preview = content[:PREVIEW_CHARS]
    if len(content) > PREVIEW_CHARS:
        preview += f"\n... ({len(content) - PREVIEW_CHARS} more chars)"

    return {
        "status": "pinned",
        "name": name,
        "record_id": record.record_id,
        "chars": len(content),
        "source": source,
        "source_path": source_path or None,
        "preview": preview,
        "message": (
            f"Pinned as '{name}' ({len(content):,} chars). "
            f'Use Local_RetrieveToolResult(record_id="{record.record_id}") '
            f"for full content."
        ),
    }
