"""ID parsers for the agent tools.

Accept either the tagged form (``[schedule:<uuid>]`` /
``[webhook:<uuid>]``) the catalog renders OR a bare UUID. The LLM can
paste back the tag verbatim or strip the brackets; both work. Returns
``None`` on parse failure so the tool layer can surface a tool-error
rather than raise.

Mirrors :func:`threetears.agent.skills.tools._parse_skill_id` so the
shape stays consistent across the 3tears tool surface.
"""

from __future__ import annotations

from uuid import UUID

from threetears.observe import get_logger

__all__ = [
    "parse_schedule_id",
    "parse_subscription_id",
]

log = get_logger(__name__)


def parse_schedule_id(raw: str) -> UUID | None:
    """Parse ``[schedule:<uuid>]`` or a bare UUID string into :class:`UUID`.

    :param raw: candidate id string supplied by the LLM
    :ptype raw: str
    :return: parsed UUID or ``None`` on failure
    :rtype: UUID | None
    """
    return _parse_tagged(raw, "schedule")


def parse_subscription_id(raw: str) -> UUID | None:
    """Parse ``[webhook:<uuid>]`` or a bare UUID string into :class:`UUID`.

    :param raw: candidate id string supplied by the LLM
    :ptype raw: str
    :return: parsed UUID or ``None`` on failure
    :rtype: UUID | None
    """
    return _parse_tagged(raw, "webhook")


def _parse_tagged(raw: str, tag: str) -> UUID | None:
    """Strip a ``[<tag>:<uuid>]`` wrapper if present, then parse to UUID.

    :param raw: candidate string
    :ptype raw: str
    :param tag: leading tag name (``schedule`` / ``webhook``)
    :ptype tag: str
    :return: parsed UUID or ``None`` on failure
    :rtype: UUID | None
    """
    if not raw or not isinstance(raw, str):
        return None
    candidate = raw.strip()
    prefix = f"[{tag}:"
    if candidate.startswith(prefix) and candidate.endswith("]"):
        candidate = candidate[len(prefix) : -1].strip()
    try:
        return UUID(candidate)
    except ValueError:
        # Malformed UUID literal (typo, wrong format, etc.). The caller turns None into a
        # tool-error the model sees; this line is for an operator asking why a lookup keeps missing.
        log.debug("id did not parse as a UUID", extra={"extra_data": {"tag": tag, "candidate": candidate[:64]}})
        return None
