"""Allowlisted GridZen production pilot stdio MCP.
Install mcp==2.3.0 httpx==0.28.1 in a venv. Set GRIDZEN_API_KEY locally.
Never submit documents, faces or upstream credentials; KYC uses a hosted URL.
"""

import os
import inspect
from uuid import UUID
from typing import Any
import httpx
import logging

# Provider lookup URLs contain subject identifiers; never emit HTTP client traces.
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("httpcore").setLevel(logging.WARNING)
from mcp.server import MCPServer
from mcp.shared.exceptions import MCPError
from mcp.types import ToolAnnotations

BASE = "https://gridzen.ai/console/api/production"


class ProductionAPI:
    def __init__(self, key, transport=None):
        if not key or not key.startswith("gz_test_"):
            raise ValueError(
                "Configure merchant GRIDZEN_API_KEY; production permission is separate"
            )
        self.key, self.transport = key, transport

    async def call(self, method, path, body=None):
        try:
            async with httpx.AsyncClient(
                timeout=30, follow_redirects=False, transport=self.transport
            ) as client:
                response = await client.request(
                    method,
                    BASE + path,
                    headers={"Authorization": "Bearer " + self.key},
                    json=body,
                )
                if response.status_code >= 400:
                    raise MCPError(
                        code=-32000,
                        message=f"GridZen production API HTTP {response.status_code}; preserve idempotency key and read existing record before retry",
                    )
                return response.json()
        except (httpx.HTTPError, ValueError):
            raise MCPError(
                code=-32000,
                message="Connection uncertain; preserve idempotency key, never create a replacement request automatically",
            ) from None


def make_server(api):
    tools = {}

    async def validate(ctx, call_next):
        if ctx.method == "tools/call" and isinstance(ctx.params, dict):
            args = ctx.params.get("arguments", {})
            name = ctx.params.get("name")
            fn = tools.get(name) if isinstance(name, str) else None
            if fn and (
                not isinstance(args, dict)
                or set(args) - set(inspect.signature(fn).parameters)
                or any(
                    (
                        type(v) is not bool
                        if k == "consent_to_verify"
                        else not isinstance(v, str) or len(v) > 100
                    )
                    for k, v in args.items()
                )
            ):
                raise MCPError(
                    code=-32602,
                    message="Only documented arguments accepted; do not submit identity documents or credentials",
                )
        return await call_next(ctx)

    server = MCPServer(
        "GridZen Production Pilot",
        version="0.1.0",
        middleware=[validate],
        instructions="Allowlisted live pilot, confirmed free routes only. First read capabilities. Mutations require explicit subject consent and intended user action. Phone checks validity only, not possession. Identity documents and faces go directly to hosted provider. Return provider evidence, never infer fraud or approve a customer automatically. Reuse idempotency UUID on retry; no automatic replacement after ambiguous submission.",
    )
    read = ToolAnnotations(
        readOnlyHint=True,
        destructiveHint=False,
        idempotentHint=True,
        openWorldHint=True,
    )
    write = ToolAnnotations(
        readOnlyHint=False,
        destructiveHint=False,
        idempotentHint=True,
        openWorldHint=True,
    )

    def consent(key, reference, approved):
        if approved is not True:
            raise MCPError(code=-32602, message="Explicit subject consent required")
        try:
            return str(UUID(key)), str(UUID(reference))
        except (ValueError, TypeError, AttributeError):
            raise MCPError(
                code=-32602, message="Use UUID request and consent references"
            ) from None

    @server.tool(annotations=read, structured_output=True)
    async def production_capabilities() -> dict[str, Any]:
        return await api.call("GET", "/capabilities")

    @server.tool(annotations=write, structured_output=True)
    async def check_signup_phone(
        phone: str,
        idempotency_key: str,
        consent_reference: str,
        consent_to_verify: bool,
    ) -> dict[str, Any]:
        """Free format/validity check of an explicitly authorized E.164 number. No OTP, ownership or paid line intelligence."""
        key, ref = consent(idempotency_key, consent_reference, consent_to_verify)
        return await api.call(
            "POST",
            "/verifications",
            {
                "mode": "production",
                "capability": "phone_validation",
                "phone": phone,
                "idempotency_key": key,
                "consent_reference": ref,
                "consent_to_verify": True,
            },
        )

    @server.tool(annotations=write, structured_output=True)
    async def start_identity_onboarding(
        idempotency_key: str, consent_reference: str, consent_to_verify: bool
    ) -> dict[str, Any]:
        """Create one hosted real identity flow after consent. User must personally open URL and complete document/face steps."""
        key, ref = consent(idempotency_key, consent_reference, consent_to_verify)
        return await api.call(
            "POST",
            "/verifications",
            {
                "mode": "production",
                "capability": "identity_verification",
                "idempotency_key": key,
                "consent_reference": ref,
                "consent_to_verify": True,
            },
        )

    @server.tool(annotations=read, structured_output=True)
    async def get_production_verification(record_id: str) -> dict[str, Any]:
        return await api.call("GET", "/verifications/" + str(UUID(record_id)))

    @server.tool(annotations=write, structured_output=True)
    async def refresh_production_verification(record_id: str) -> dict[str, Any]:
        return await api.call(
            "POST", "/verifications/" + str(UUID(record_id)) + "/refresh", {}
        )

    tools.update(
        {
            fn.__name__: fn
            for fn in (
                production_capabilities,
                check_signup_phone,
                start_identity_onboarding,
                get_production_verification,
                refresh_production_verification,
            )
        }
    )
    return server


if __name__ == "__main__":
    make_server(ProductionAPI(os.environ.get("GRIDZEN_API_KEY", ""))).run(
        transport="stdio"
    )
