"""Stripe connector — look up customers and charges, and issue refunds.

Reads (customer/charge lookups, balance) are non-sensitive; issuing a refund
moves money, so ``stripe.create_refund`` is sensitive and routes through the
policy engine / approval inbox. Uses the Stripe REST API directly (form-encoded
requests) through the shared HTTP seam so tests need no Stripe SDK or network.

This is distinct from the internal billing integration (``legate/billing``),
which handles the platform's own subscriptions; this connector lets an agent act
on a workspace's *own* Stripe account.
"""

from __future__ import annotations

import httpx

from legate.agent.tooling import ToolContext, ToolResult, ToolSpec
from legate.connectors.base import BaseConnector, ConnectionResult
from legate.connectors.runtime import new_async_client

_API_BASE = "https://api.stripe.com/v1"
_MAX_ITEMS = 50


class StripeConnector(BaseConnector):
    type = "stripe"
    title = "Stripe"
    config_schema = {"type": "object", "properties": {}}
    secret_schema = {
        "type": "object",
        "properties": {"api_key": {"type": "string", "description": "Stripe secret key (sk_...)."}},
        "required": ["api_key"],
    }

    def list_tools(self) -> list[ToolSpec]:
        return [
            ToolSpec(
                name="stripe.get_customer",
                description="Fetch a Stripe customer by id.",
                parameters={
                    "type": "object",
                    "properties": {"customer_id": {"type": "string"}},
                    "required": ["customer_id"],
                },
                is_sensitive=False,
                scopes=["stripe:read"],
            ),
            ToolSpec(
                name="stripe.list_charges",
                description="List recent charges, optionally for one customer.",
                parameters={
                    "type": "object",
                    "properties": {
                        "customer": {"type": "string"},
                        "limit": {"type": "integer"},
                    },
                },
                is_sensitive=False,
                scopes=["stripe:read"],
            ),
            ToolSpec(
                name="stripe.create_refund",
                description="Refund a charge or payment intent (optionally a partial amount).",
                parameters={
                    "type": "object",
                    "properties": {
                        "charge": {"type": "string", "description": "Charge id (ch_...)."},
                        "payment_intent": {"type": "string", "description": "PaymentIntent id."},
                        "amount": {
                            "type": "integer",
                            "description": "Amount in the smallest unit.",
                        },
                    },
                },
                is_sensitive=True,
                scopes=["stripe:write"],
            ),
        ]

    def _headers(self, secrets: dict) -> dict:
        return {"Authorization": f"Bearer {secrets.get('api_key', '')}"}

    async def _get(self, path: str, secrets: dict, params: dict | None = None) -> httpx.Response:
        async with new_async_client() as client:
            return await client.get(
                f"{_API_BASE}{path}", headers=self._headers(secrets), params=params
            )

    async def _post(self, path: str, secrets: dict, data: dict) -> httpx.Response:
        async with new_async_client() as client:
            return await client.post(
                f"{_API_BASE}{path}", headers=self._headers(secrets), data=data
            )

    async def test_connection(self, config: dict, secrets: dict) -> ConnectionResult:
        try:
            resp = await self._get("/balance", secrets)
        except httpx.HTTPError as exc:
            return ConnectionResult(ok=False, message=f"connection failed: {exc}")
        if resp.status_code == 200:
            return ConnectionResult(ok=True, message="Stripe key authenticated")
        return ConnectionResult(ok=False, message=f"HTTP {resp.status_code}")

    @staticmethod
    def _charge_summary(charge: dict) -> dict:
        return {
            "id": charge.get("id"),
            "amount": charge.get("amount"),
            "currency": charge.get("currency"),
            "status": charge.get("status"),
            "paid": charge.get("paid"),
            "refunded": charge.get("refunded"),
        }

    async def execute(
        self, tool: str, args: dict, config: dict, secrets: dict, ctx: ToolContext
    ) -> ToolResult:
        if not secrets.get("api_key"):
            return ToolResult(ok=False, error="missing api_key")

        try:
            if tool == "stripe.get_customer":
                resp = await self._get(f"/customers/{args['customer_id']}", secrets)
                if resp.status_code != 200:
                    return ToolResult(ok=False, error=f"HTTP {resp.status_code}")
                c = resp.json()
                return ToolResult(
                    ok=True,
                    data={"id": c.get("id"), "email": c.get("email"), "name": c.get("name")},
                )

            if tool == "stripe.list_charges":
                params = {"limit": min(int(args.get("limit", 10)), _MAX_ITEMS)}
                if args.get("customer"):
                    params["customer"] = args["customer"]
                resp = await self._get("/charges", secrets, params=params)
                if resp.status_code != 200:
                    return ToolResult(ok=False, error=f"HTTP {resp.status_code}")
                charges = [self._charge_summary(c) for c in resp.json().get("data", [])]
                return ToolResult(ok=True, data={"charges": charges})

            if tool == "stripe.create_refund":
                data: dict = {}
                if args.get("charge"):
                    data["charge"] = args["charge"]
                if args.get("payment_intent"):
                    data["payment_intent"] = args["payment_intent"]
                if not data:
                    return ToolResult(ok=False, error="charge or payment_intent is required")
                if args.get("amount") is not None:
                    data["amount"] = int(args["amount"])
                resp = await self._post("/refunds", secrets, data=data)
                if resp.status_code not in (200, 201):
                    return ToolResult(ok=False, error=f"HTTP {resp.status_code}")
                r = resp.json()
                return ToolResult(
                    ok=True,
                    data={"id": r.get("id"), "status": r.get("status"), "amount": r.get("amount")},
                )

            return ToolResult(ok=False, error=f"unknown stripe tool: {tool}")
        except httpx.HTTPError as exc:
            return ToolResult(ok=False, error=f"stripe request failed: {exc}")
