"""AgenticlyPay API client for Python - matches npm package interface."""

import httpx
from typing import Dict, Any, Optional, Literal
from pydantic import EmailStr


class AgenticlyPayError(Exception):
    """Custom error class for API errors."""

    def __init__(self, message: str, status_code: Optional[int] = None, response: Optional[Any] = None):
        super().__init__(message)
        self.status_code = status_code
        self.response = response


class AgenticlyPayClient:
    """Main AgenticlyPay API client - matches npm AgenticlyPay class."""

    def __init__(self, base_url: str = "https://api.agenticlypay.com", api_key: Optional[str] = None):
        """
        Initialize the API client.

        Args:
            base_url: Base URL for the API (defaults to production)
            api_key: Optional API token for authentication (takes precedence over email)
        """
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key

    def _request(
        self,
        endpoint: str,
        method: str = "GET",
        json_data: Optional[Dict[str, Any]] = None,
        email: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Make an HTTP request to the API.

        Args:
            endpoint: API endpoint (e.g., '/api/v1/connect/accounts')
            method: HTTP method (GET, POST, etc.)
            json_data: JSON payload for POST/PUT requests
            email: Developer email for X-Developer-Email header (used if api_key not provided)

        Returns:
            Response JSON as dictionary

        Raises:
            AgenticlyPayError: If request fails
        """
        url = f"{self.base_url}{endpoint}"
        headers = {"Content-Type": "application/json"}

        # API key takes precedence over email
        if self.api_key:
            headers["Authorization"] = f"Bearer {self.api_key}"
        elif email:
            headers["X-Developer-Email"] = email

        try:
            with httpx.Client() as client:
                response = client.request(
                    method=method,
                    url=url,
                    headers=headers,
                    json=json_data,
                    timeout=30.0,
                )

                if not response.is_success:
                    error_detail = f"HTTP {response.status_code}: {response.reason_phrase}"
                    try:
                        error_data = response.json()
                        error_detail = error_data.get("detail", error_detail)
                    except Exception:
                        pass
                    raise AgenticlyPayError(error_detail, response.status_code, response)

                return response.json()
        except AgenticlyPayError:
            raise
        except Exception as e:
            raise AgenticlyPayError(f"Request failed: {str(e)}")

    # Connect Account Methods

    def create_account(
        self,
        email: str,
        country: str = "US",
        metadata: Optional[Dict[str, str]] = None,
    ) -> Dict[str, Any]:
        """
        Create a new Stripe Connect account for a developer.

        Args:
            email: Developer email address
            country: Country code (default: US)
            metadata: Optional metadata

        Returns:
            Dictionary with account information
        """
        return self._request(
            "/api/v1/connect/accounts",
            method="POST",
            json_data={"email": email, "country": country, "metadata": metadata or {}},
            email=email,
        )

    def get_account(self, account_id: str, email: str) -> Dict[str, Any]:
        """
        Get account status.

        Args:
            account_id: Stripe Connect account ID
            email: Developer email address

        Returns:
            Dictionary with account status
        """
        return self._request(f"/api/v1/connect/accounts/{account_id}?email={email}", email=email)

    def create_onboarding_link(
        self,
        account_id: str,
        refresh_url: str,
        return_url: str,
        email: str,
    ) -> Dict[str, Any]:
        """
        Create an onboarding link for a developer account.

        Args:
            account_id: Stripe Connect account ID
            refresh_url: URL to redirect if link expires
            return_url: URL to redirect after onboarding completion
            email: Developer email address

        Returns:
            Dictionary with onboarding link URL
        """
        return self._request(
            "/api/v1/connect/onboarding",
            method="POST",
            json_data={
                "account_id": account_id,
                "refresh_url": refresh_url,
                "return_url": return_url,
            },
            email=email,
        )

    def configure_payout_schedule(
        self,
        account_id: str,
        interval: Literal["manual", "daily", "weekly", "monthly"],
        email: str,
        monthly_anchor: Optional[int] = None,
        weekly_anchor: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Configure payout schedule for a connected account.

        Args:
            account_id: Stripe Connect account ID
            interval: Payout interval
            email: Developer email address
            monthly_anchor: Day of month for monthly payouts (1-31)
            weekly_anchor: Day of week for weekly payouts

        Returns:
            Dictionary with payout schedule configuration
        """
        payload = {"account_id": account_id, "interval": interval}
        if monthly_anchor is not None:
            payload["monthly_anchor"] = monthly_anchor
        if weekly_anchor:
            payload["weekly_anchor"] = weekly_anchor

        return self._request("/api/v1/connect/payout-schedule", method="POST", json_data=payload, email=email)

    # Payment Methods

    def process_payment(
        self,
        email: str,
        protocol: Literal["ACP", "AP2", "x402", "AUTO"],
        amount: int,
        developer_account_id: str,
        currency: str = "usd",
        metadata: Optional[Dict[str, Any]] = None,
        description: Optional[str] = None,
        mandate: Optional[Dict[str, Any]] = None,
        resource_url: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Process a payment using the specified protocol.

        Args:
            email: Developer email address
            protocol: Payment protocol (ACP, AP2, x402, or AUTO)
            amount: Amount in cents
            developer_account_id: Connected account ID
            currency: Currency code (default: usd)
            metadata: Optional metadata
            description: Payment description
            mandate: Mandate for AP2 protocol
            resource_url: Resource URL for x402 protocol

        Returns:
            Dictionary with payment information
        """
        payload = {
            "email": email,
            "protocol": protocol,
            "amount": amount,
            "developer_account_id": developer_account_id,
            "currency": currency,
        }
        if metadata:
            payload["metadata"] = metadata
        if description:
            payload["description"] = description
        if mandate:
            payload["mandate"] = mandate
        if resource_url:
            payload["resource_url"] = resource_url

        return self._request("/api/v1/payments/process", method="POST", json_data=payload, email=email)

    def confirm_payment(
        self,
        email: str,
        protocol: Literal["AUTO"],
        payment_id: str,
        payment_method: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Confirm a payment.

        Args:
            email: Developer email address
            protocol: Payment protocol
            payment_id: Payment intent ID
            payment_method: Payment method ID (optional)

        Returns:
            Dictionary with payment confirmation result
        """
        payload = {
            "email": email,
            "protocol": protocol,
            "payment_id": payment_id,
        }
        if payment_method:
            payload["payment_method"] = payment_method

        return self._request("/api/v1/payments/confirm", method="POST", json_data=payload, email=email)

    def get_payment_status(
        self,
        email: str,
        protocol: Literal["AUTO"],
        payment_id: str,
    ) -> Dict[str, Any]:
        """
        Get the status of a payment.

        Args:
            email: Developer email address
            protocol: Payment protocol
            payment_id: Payment intent ID

        Returns:
            Dictionary with payment status
        """
        return self._request(
            f"/api/v1/payments/status/{protocol}/{payment_id}?email={email}",
            email=email,
        )

    def get_fee(self, email: str, amount: int) -> Dict[str, Any]:
        """
        Calculate fee for an amount.

        Args:
            email: Developer email address
            amount: Amount in cents

        Returns:
            Dictionary with fee breakdown
        """
        return self._request(f"/api/v1/payments/fee/{amount}?email={email}", email=email)

    # Payout Methods

    def get_monthly_earnings(
        self,
        email: str,
        account_id: str,
        year: int,
        month: int,
    ) -> Dict[str, Any]:
        """
        Get monthly earnings for a developer.

        Args:
            email: Developer email address
            account_id: Stripe Connect account ID
            year: Year (e.g., 2024)
            month: Month (1-12)

        Returns:
            Dictionary with monthly earnings
        """
        return self._request(
            f"/api/v1/payouts/earnings/{account_id}/{year}/{month}?email={email}",
            email=email,
        )

    def create_transfer(
        self,
        email: str,
        developer_account_id: str,
        amount: int,
        currency: str = "usd",
        reference: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Create a direct transfer to a connected account (manual payout).

        Args:
            email: Developer email address
            developer_account_id: Connected account ID
            amount: Amount in cents
            currency: Currency code (default: usd)
            reference: Reference for the transfer

        Returns:
            Dictionary with transfer information
        """
        payload = {
            "developer_account_id": developer_account_id,
            "amount": amount,
            "currency": currency,
            "developer_email": email,
        }
        if reference:
            payload["reference"] = reference

        return self._request("/api/v1/payouts/transfer", method="POST", json_data=payload, email=email)

    def get_annual_earnings(
        self,
        email: str,
        account_id: str,
        year: int,
    ) -> Dict[str, Any]:
        """
        Get annual earnings for tax reporting.

        Args:
            email: Developer email address
            account_id: Stripe Connect account ID
            year: Year (e.g., 2024)

        Returns:
            Dictionary with annual earnings
        """
        return self._request(f"/api/v1/tax/earnings/{account_id}/{year}?email={email}", email=email)

