"""Solution for the multi-tenant MCP practice."""
import math

ISSUER = "http://localhost:8788"
AUDIENCE = "http://localhost:8787/mcp"
TOOL_SCOPES = {"search_contacts": "crm:read", "create_contact": "crm:write", "delete_contact": "crm:write"}
WRITE_TOOLS = {"create_contact", "delete_contact"}


def authenticate(signature_ok: bool, claims: dict | None, now: float) -> tuple[int, dict | None]:
    if claims is None or not signature_ok:
        return 401, None
    if claims.get("iss") != ISSUER or claims.get("aud") != AUDIENCE:
        return 401, None
    if claims.get("exp", 0) <= now or not isinstance(claims.get("org_id"), str):
        return 401, None
    return 200, {"tenant": claims["org_id"], "client": claims["client_id"],
                 "scopes": claims.get("scope", "").split()}


class RateLimiter:
    def __init__(self, per_minute: int = 120, burst: int = 20):
        self.per_minute, self.burst = per_minute, burst
        self.buckets: dict[str, tuple[float, float]] = {}

    def allow(self, tenant: str, now: float) -> tuple[bool, int]:
        refill = self.per_minute / 60
        tokens, updated = self.buckets.get(tenant, (self.burst, now))
        tokens = min(self.burst, tokens + (now - updated) * refill)
        if tokens < 1:
            self.buckets[tenant] = (tokens, now)
            return False, math.ceil((1 - tokens) / refill)
        self.buckets[tenant] = (tokens - 1, now)
        return True, 0


def handle(signature_ok: bool, claims: dict | None, tool: str, limiter: RateLimiter, now: float,
           audit: list[dict]) -> int:
    status, auth = authenticate(signature_ok, claims, now)
    if status != 200:
        return 401
    allowed, _ = limiter.allow(auth["tenant"], now)
    if not allowed:
        return 429
    needed = TOOL_SCOPES.get(tool)
    if needed and needed not in auth["scopes"]:
        if tool in WRITE_TOOLS:
            audit.append({"tenant": auth["tenant"], "client": auth["client"], "tool": tool, "outcome": "denied"})
        return 403
    return 200


def error_rate(responses: list[tuple[int, bool]]) -> float:
    if not responses:
        return 0.0
    failed = sum(1 for status, is_error in responses if status >= 400 or is_error)
    return failed / len(responses)
