"""Deterministic baseline for resolving user instructions to agent tools."""

from __future__ import annotations

import re
from typing import Any

from jsonschema import Draft202012Validator
from bridge_platform.agents.providers import AgentProviderError, ToolSelector


ABN_CANDIDATE = re.compile(r"(?<!\d)(?:\d[\s-]?){11}(?!\d)")


class AgentResolutionError(ValueError):
    """Safe error raised when no basic tool can handle an instruction."""

    def __init__(self, code: str, message: str) -> None:
        super().__init__(message)
        self.code = code


def resolve_basic_instruction(message: str, tools: list[dict[str, Any]]) -> dict[str, Any]:
    """Resolve the first supported read-only instruction without an LLM."""
    message = message.strip()
    if not message:
        raise AgentResolutionError("empty_instruction", "Enter an instruction for the agent.")
    if len(message) > 1000:
        raise AgentResolutionError("instruction_too_long", "The instruction exceeds 1000 characters.")

    abn_tool = next(
        (tool for tool in tools if tool.get("capability") == "business.au.abn.lookup"),
        None,
    )
    match = ABN_CANDIDATE.search(message)
    if abn_tool is None or match is None:
        raise AgentResolutionError(
            "no_matching_tool",
            "No available basic agent tool matched the instruction. Include an exact 11-digit ABN.",
        )

    abn = re.sub(r"\D", "", match.group(0))
    arguments = {"abn": abn, "include_history": False}
    schema = abn_tool.get("input_schema") or {}
    validation_errors = sorted(
        Draft202012Validator(schema).iter_errors(arguments), key=lambda error: list(error.path)
    )
    if validation_errors:
        raise AgentResolutionError(
            "invalid_tool_arguments",
            "The instruction produced arguments that do not satisfy the tool contract.",
        )

    execution = abn_tool.get("execution") or {}
    return {
        "resolver": "deterministic_v1",
        "capability": abn_tool["capability"],
        "app_id": abn_tool["app_id"],
        "action": execution.get("action"),
        "arguments": arguments,
        "risk": abn_tool.get("risk"),
        "confirmation": abn_tool.get("confirmation"),
        "required_permissions": abn_tool.get("required_permissions", []),
    }


def resolve_instruction(
    message: str,
    tools: list[dict[str, Any]],
    *,
    context: dict[str, Any] | None = None,
    provider: ToolSelector | None = None,
) -> dict[str, Any]:
    """Prefer the zero-cost deterministic resolver, then an optional provider."""
    try:
        return resolve_basic_instruction(message, tools)
    except AgentResolutionError as deterministic_error:
        if deterministic_error.code != "no_matching_tool" or provider is None:
            raise
    try:
        selected = provider.select(message, tools, context or {})
    except AgentProviderError as exc:
        raise AgentResolutionError("ai_selector_failed", str(exc)) from exc
    tool = selected["tool"]
    execution = tool.get("execution") or {}
    return {
        "resolver": selected.get("provider", provider.name),
        "model": selected.get("model"),
        "capability": tool["capability"],
        "app_id": tool["app_id"],
        "action": execution.get("action"),
        "arguments": selected["arguments"],
        "risk": tool.get("risk"),
        "confirmation": tool.get("confirmation"),
        "required_permissions": tool.get("required_permissions", []),
    }
