"""Agent Profile v1 contracts and effective tool filtering."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any

from sqlalchemy import select

from config.control_plane import get_control_plane_session
from bridge_platform.apps.contracts import CAPABILITY, IDENTIFIER, SEMVER
from bridge_platform.tenants.models import AgentProfile


CONFIRMATION_VALUES = {"never", "policy", "always"}


@dataclass(frozen=True, slots=True)
class AgentProfileContract:
    agent_key: str
    name: str
    version: str
    allowed_capabilities: tuple[str, ...]
    allowed_roles: tuple[str, ...] = ()
    confirmation_policy: dict[str, str] = field(default_factory=dict)
    max_steps: int = 5
    monthly_token_limit: int | None = None
    instructions_version: str = "1.0.0"
    description: str = ""

    def validate(self) -> list[str]:
        errors: list[str] = []
        if not IDENTIFIER.fullmatch(self.agent_key):
            errors.append("agent_key is invalid")
        if not self.name.strip() or not self.description.strip():
            errors.append("agent profile name and description are required")
        if not SEMVER.fullmatch(self.version) or not SEMVER.fullmatch(self.instructions_version):
            errors.append("agent profile versions must use semantic versioning")
        if not self.allowed_capabilities:
            errors.append("agent profile requires at least one allowed capability")
        for capability in self.allowed_capabilities:
            if not CAPABILITY.fullmatch(capability):
                errors.append(f"invalid profile capability: {capability}")
        for role in self.allowed_roles:
            if not IDENTIFIER.fullmatch(role):
                errors.append(f"invalid profile role: {role}")
        for category, policy in self.confirmation_policy.items():
            if category not in {"read", "calculate", "write_reversible", "write_irreversible"}:
                errors.append(f"invalid profile confirmation category: {category}")
            if policy not in CONFIRMATION_VALUES:
                errors.append(f"invalid profile confirmation policy: {policy}")
        if not 1 <= self.max_steps <= 25:
            errors.append("agent profile max_steps must be between 1 and 25")
        if self.monthly_token_limit is not None and self.monthly_token_limit < 0:
            errors.append("agent profile monthly_token_limit cannot be negative")
        return errors

    def to_dict(self) -> dict[str, Any]:
        return {
            "agent_key": self.agent_key,
            "name": self.name,
            "version": self.version,
            "description": self.description,
            "allowed_capabilities": list(self.allowed_capabilities),
            "allowed_roles": list(self.allowed_roles),
            "confirmation_policy": dict(self.confirmation_policy),
            "limits": {
                "max_steps": self.max_steps,
                "monthly_token_limit": self.monthly_token_limit,
            },
            "instructions_version": self.instructions_version,
        }


def list_profiles(tenant_id: str, roles: tuple[str, ...] = ()) -> list[AgentProfileContract]:
    with get_control_plane_session() as session:
        rows = session.execute(select(AgentProfile).where(
            AgentProfile.tenant_id == tenant_id,
            AgentProfile.status == "active",
        ).order_by(AgentProfile.display_name)).scalars().all()
    profiles = [_contract_from_record(row) for row in rows]
    return [profile for profile in profiles if _roles_allow(profile, roles)]


def get_profile(tenant_id: str, agent_key: str, roles: tuple[str, ...] = ()) -> AgentProfileContract:
    profiles = list_profiles(tenant_id, roles)
    profile = next((item for item in profiles if item.agent_key == agent_key), None)
    if profile is None:
        raise PermissionError("Agent profile is unavailable for this tenant or role")
    return profile


def filter_profile_tools(profile: AgentProfileContract, tools: list[dict[str, Any]]) -> list[dict[str, Any]]:
    allowed = set(profile.allowed_capabilities)
    result = []
    for tool in tools:
        if tool.get("capability") not in allowed:
            continue
        effective = dict(tool)
        risk_category = (tool.get("risk") or {}).get("category")
        profile_confirmation = profile.confirmation_policy.get(str(risk_category))
        if profile_confirmation:
            effective["confirmation"] = _stricter_confirmation(
                str(tool.get("confirmation") or "never"), profile_confirmation,
            )
        result.append(effective)
    return result


def _contract_from_record(record: AgentProfile) -> AgentProfileContract:
    limits = record.limits_json or {}
    contract = AgentProfileContract(
        agent_key=record.agent_key,
        name=record.display_name,
        version=record.profile_version,
        description=record.description,
        allowed_capabilities=tuple(record.allowed_capabilities_json or ()),
        allowed_roles=tuple(record.allowed_roles_json or ()),
        confirmation_policy=dict(record.confirmation_policy_json or {}),
        max_steps=int(limits.get("max_steps", 5)),
        monthly_token_limit=limits.get("monthly_token_limit"),
        instructions_version=record.instructions_version,
    )
    errors = contract.validate()
    if errors:
        raise ValueError("Invalid stored Agent Profile: " + "; ".join(errors))
    return contract


def _roles_allow(profile: AgentProfileContract, roles: tuple[str, ...]) -> bool:
    return not profile.allowed_roles or bool(set(profile.allowed_roles).intersection(roles))


def _stricter_confirmation(tool_policy: str, profile_policy: str) -> str:
    order = {"never": 0, "policy": 1, "always": 2}
    return max((tool_policy, profile_policy), key=lambda value: order[value])
