from __future__ import annotations

from typing import TYPE_CHECKING, Any

from gamma23.types import ExecuteResult, SpendIntent

if TYPE_CHECKING:
    from gamma23.client import Gamma23


class SpendIntents:
    """Manage spend intents — declared spending goals that go through policy
    evaluation before execution."""

    def __init__(self, client: Gamma23) -> None:
        self._client = client

    def create(
        self,
        *,
        goal: str,
        budget: float,
        currency: str = "GBP",
        merchant: str | None = None,
        category: str | None = None,
        domain: str | None = None,
        metadata: dict[str, Any] | None = None,
        idempotency_key: str | None = None,
    ) -> SpendIntent:
        """Create a new spend intent.

        Args:
            goal: Human-readable description of what the agent wants to buy.
            budget: Maximum amount the agent is willing to spend.
            currency: ISO 4217 currency code (default ``"GBP"``).
            merchant: Target merchant name.
            category: Spending category (e.g. ``"software"``, ``"travel"``).
            domain: Merchant domain for verification.
            metadata: Arbitrary key-value pairs attached to the intent.
            idempotency_key: Optional idempotency key to prevent duplicates.

        Returns:
            The created :class:`SpendIntent` with policy decision and
            spend token (if approved).
        """
        body: dict[str, Any] = {
            "goal": goal,
            "budget": budget,
            "currency": currency,
        }
        if merchant is not None:
            body["merchant"] = merchant
        if category is not None:
            body["category"] = category
        if domain is not None:
            body["domain"] = domain
        if metadata is not None:
            body["metadata"] = metadata

        headers: dict[str, str] = {}
        if idempotency_key is not None:
            headers["Idempotency-Key"] = idempotency_key

        data = self._client._request(
            "POST",
            "/api/v1/spend-intents",
            json=body,
            headers=headers or None,
        )
        return SpendIntent.from_dict(data)

    def get(self, intent_id: str) -> SpendIntent:
        """Retrieve an existing spend intent by ID.

        Args:
            intent_id: The spend intent identifier.

        Returns:
            The :class:`SpendIntent`.
        """
        data = self._client._request("GET", f"/api/v1/spend-intents/{intent_id}")
        return SpendIntent.from_dict(data)

    def cancel(self, intent_id: str) -> SpendIntent:
        """Cancel a pending spend intent.

        Args:
            intent_id: The spend intent identifier.

        Returns:
            The cancelled :class:`SpendIntent`.
        """
        data = self._client._request(
            "POST", f"/api/v1/spend-intents/{intent_id}/cancel"
        )
        return SpendIntent.from_dict(data)

    def execute(
        self,
        intent_id: str,
        *,
        spend_token: str,
        mode: str | None = None,
    ) -> ExecuteResult:
        """Execute an approved spend intent.

        Args:
            intent_id: The spend intent identifier.
            spend_token: The spend token received upon approval.
            mode: Optional execution mode (e.g. ``"live"``, ``"sandbox"``).

        Returns:
            An :class:`ExecuteResult` with transaction details.
        """
        body: dict[str, Any] = {"spend_token": spend_token}
        if mode is not None:
            body["mode"] = mode

        data = self._client._request(
            "POST",
            f"/api/v1/spend-intents/{intent_id}/execute",
            json=body,
        )
        return ExecuteResult.from_dict(data)
