"""
Payout API resource.

Endpoints:
    POST /v1/payout
    GET  /v1/payout/:id
    GET  /v1/payouts/search
    POST /v1/payout-batch
    GET  /v1/payout-batch/:id
    POST /v1/payout/:id/reverse
    POST /v1/verify_recipient/
"""

import uuid
from typing import Any, Dict, List, Optional

from .session import WaveSession


def _generate_idempotency_key() -> str:
    """Generate a random UUID v4 as an idempotency key."""
    return str(uuid.uuid4())


class PayoutAPI:
    """
    Interact with the Wave Payout API.

    Send money from your business wallet to individuals via mobile phone number.
    Supports single payouts, batch payouts, reversals, and recipient verification.
    """

    def __init__(self, session: WaveSession):
        self._session = session

    def create(
        self,
        currency: str,
        receive_amount: str,
        mobile: str,
        name: Optional[str] = None,
        idempotency_key: Optional[str] = None,
        client_reference: Optional[str] = None,
        payment_reason: Optional[str] = None,
        national_id: Optional[str] = None,
        aggregated_merchant_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Send a single payout to a recipient identified by mobile number.

        This endpoint is **synchronous** — Wave attempts the transfer immediately
        and returns the result. Always use an idempotency key for safe retries.

        Args:
            currency: ISO 4217 currency code (e.g. "XOF").
            receive_amount: Net amount the recipient will receive (string).
            mobile: Recipient phone number in E.164 format (e.g. "+221555110219").
            name: Recipient name for verification (optional).
            idempotency_key: Unique key for safe retries. Auto-generated if not provided.
            client_reference: Your internal reference for this payout (optional).
            payment_reason: Message shown to recipient in the app (max 40 chars, optional).
            national_id: Recipient national ID for verification (optional).
            aggregated_merchant_id: Aggregated merchant ID (aggregators only).

        Returns:
            Payout object::

                {
                    "id": "pt-185b5e4b8100c",
                    "status": "succeeded",
                    "receive_amount": "500",
                    "fee": "5",
                    ...
                }

        Raises:
            WaveValidationError: If request data is invalid.
            WaveAPIError: If the payout fails (check payout_error field).

        Example::

            payout = client.payout.create(
                currency="XOF",
                receive_amount="5000",
                mobile="+221555110219",
                name="Fatou Ndiaye",
                payment_reason="Salary January",
            )
            if payout["status"] == "succeeded":
                print("Payout sent successfully!")
            elif payout.get("payout_error"):
                print("Failed:", payout["payout_error"]["error_message"])
        """
        payload: Dict[str, Any] = {
            "currency": currency,
            "receive_amount": receive_amount,
            "mobile": mobile,
        }
        if name:
            payload["name"] = name
        if client_reference:
            payload["client_reference"] = client_reference
        if payment_reason:
            payload["payment_reason"] = payment_reason
        if national_id:
            payload["national_id"] = national_id
        if aggregated_merchant_id:
            payload["aggregated_merchant_id"] = aggregated_merchant_id

        key = idempotency_key or _generate_idempotency_key()
        return self._session.post("/v1/payout", json=payload, idempotency_key=key)

    def get(self, payout_id: str) -> Dict[str, Any]:
        """
        Retrieve a single payout by its ID.

        Args:
            payout_id: Payout ID (starts with 'pt-').

        Returns:
            Payout object with current status.

        Raises:
            WaveNotFoundError: If the payout does not exist.

        Example::

            payout = client.payout.get("pt-185b5e4b8100c")
            print(payout["status"])
        """
        return self._session.get(f"/v1/payout/{payout_id}")

    def search(self, client_reference: str) -> List[Dict[str, Any]]:
        """
        Search payouts by your internal client reference.

        Args:
            client_reference: The client reference string you provided when creating the payout.

        Returns:
            List of matching payout objects (may be empty).

        Example::

            payouts = client.payout.search("invoice-2024-001")
            for p in payouts:
                print(p["id"], p["status"])
        """
        response = self._session.get(
            "/v1/payouts/search",
            params={"client_reference": client_reference},
        )
        return response.get("result", [])

    def create_batch(
        self,
        payouts: List[Dict[str, Any]],
        idempotency_key: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Submit a batch of payouts for asynchronous processing.

        Use ``get_batch()`` to poll the batch status after submission.

        Args:
            payouts: List of payout request dicts. Each dict accepts the same
                     parameters as ``create()`` (currency, receive_amount, mobile, etc.).
            idempotency_key: Unique key for safe retries. Auto-generated if not provided.

        Returns:
            Batch response with batch ID::

                {"id": "pb-185skxq8g1006"}

        Example::

            batch = client.payout.create_batch([
                {"currency": "XOF", "receive_amount": "1000",
                 "mobile": "+221555110219", "name": "Fatou Ndiaye"},
                {"currency": "XOF", "receive_amount": "2000",
                 "mobile": "+221555110233", "name": "Moustapha Mbaye"},
            ])
            batch_id = batch["id"]
        """
        key = idempotency_key or _generate_idempotency_key()
        return self._session.post(
            "/v1/payout-batch",
            json={"payouts": payouts},
            idempotency_key=key,
        )

    def get_batch(self, batch_id: str) -> Dict[str, Any]:
        """
        Retrieve a payout batch and its individual payout statuses.

        Poll this endpoint until ``status == "complete"``.

        Args:
            batch_id: Payout batch ID (starts with 'pb-').

        Returns:
            Batch object with status and list of individual payouts.

        Raises:
            WaveNotFoundError: If the batch does not exist.

        Example::

            import time
            while True:
                batch = client.payout.get_batch("pb-185skxq8g1006")
                if batch["status"] == "complete":
                    break
                time.sleep(2)

            for p in batch["payouts"]:
                print(p["id"], p["status"], p.get("payout_error"))
        """
        return self._session.get(f"/v1/payout-batch/{batch_id}")

    def reverse(self, payout_id: str, idempotency_key: Optional[str] = None) -> None:
        """
        Reverse a previously executed payout (within 3 days of creation).

        This is idempotent — reversing an already reversed payout returns
        success without creating a duplicate reversal.

        Args:
            payout_id: Payout ID (starts with 'pt-').
            idempotency_key: Unique key for safe retries. Auto-generated if not provided.

        Returns:
            None on success.

        Raises:
            WaveAPIError: If reversal fails (e.g., time limit exceeded, insufficient funds).

        Example::

            client.payout.reverse("pt-185b5e4b8100c")
        """
        key = idempotency_key or _generate_idempotency_key()
        return self._session.post(f"/v1/payout/{payout_id}/reverse", idempotency_key=key)

    def verify_recipient(
        self,
        mobile: str,
        name: Optional[str] = None,
        national_id: Optional[str] = None,
        amount: Optional[str] = None,
        currency: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Verify details of a payout recipient before sending money.

        Checks whether the mobile number corresponds to a Wave user,
        whether the name matches Wave's records, and whether the amount
        is within the user's limits.

        Args:
            mobile: Recipient phone number in E.164 format.
            name: Recipient name to check against Wave's records (optional).
            national_id: National ID to verify (optional, requires feature enablement).
            amount: Hypothetical payout amount to check against limits (optional).
            currency: Currency for the amount check (optional).

        Returns:
            Verification result::

                {
                    "within_limits": true,
                    "name_match": "MATCH",        # MATCH | NO_MATCH | NAME_NOT_KNOWN
                    "national_id_match": "NO_MATCH"  # if enabled
                }

        Example::

            result = client.payout.verify_recipient(
                mobile="+221555110219",
                name="Fatou Ndiaye",
                amount="5000",
                currency="XOF",
            )
            if result["name_match"] == "MATCH" and result["within_limits"]:
                # Safe to proceed
                pass
        """
        payload: Dict[str, Any] = {"mobile": mobile}
        if name:
            payload["name"] = name
        if national_id:
            payload["national_id"] = national_id
        if amount:
            payload["amount"] = amount
        if currency:
            payload["currency"] = currency

        return self._session.post("/v1/verify_recipient/", json=payload)
