# This file was auto-generated by Fern from our API Definition.

import typing
from json.decoder import JSONDecodeError

from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.http_response import AsyncHttpResponse, HttpResponse
from ..core.jsonable_encoder import jsonable_encoder
from ..core.parse_error import ParsingError
from ..core.pydantic_utilities import parse_obj_as
from ..core.request_options import RequestOptions
from ..errors.bad_request_error import BadRequestError
from ..errors.internal_server_error import InternalServerError
from ..errors.service_unavailable_error import ServiceUnavailableError
from ..errors.unauthorized_error import UnauthorizedError
from ..types.entry import Entry
from ..types.payabli_api_response import PayabliApiResponse
from .types.card_status import CardStatus
from .types.create_ghost_card_response import CreateGhostCardResponse
from pydantic import ValidationError

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class RawGhostCardClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    def create_ghost_card(
        self,
        entry: Entry,
        *,
        vendor_id: int,
        expense_limit: float,
        expiration_date: typing.Optional[str] = OMIT,
        amount: typing.Optional[float] = OMIT,
        max_number_of_uses: typing.Optional[int] = OMIT,
        exact_amount: typing.Optional[bool] = OMIT,
        expense_limit_period: typing.Optional[str] = OMIT,
        billing_cycle: typing.Optional[str] = OMIT,
        billing_cycle_day: typing.Optional[str] = OMIT,
        daily_transaction_count: typing.Optional[int] = OMIT,
        daily_amount_limit: typing.Optional[float] = OMIT,
        transaction_amount_limit: typing.Optional[int] = OMIT,
        mcc: typing.Optional[str] = OMIT,
        tcc: typing.Optional[str] = OMIT,
        misc_1: typing.Optional[str] = OMIT,
        misc_2: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[CreateGhostCardResponse]:
        """
        Creates a ghost card, a multi-use virtual debit card issued to a vendor for recurring or discretionary spend.

        Unlike single-use virtual cards issued as part of a payout transaction, ghost cards aren't tied to a specific payout. They're issued directly to a vendor and can be reused up to a configurable number of times within the card's spending limits.

        Parameters
        ----------
        entry : Entry

        vendor_id : int
            ID of the vendor who receives the card. The vendor must belong to the paypoint and have an active status.

        expense_limit : float
            Spending limit for the card. Must be greater than `0` and can't exceed the paypoint's configured payout credit limit.

        expiration_date : typing.Optional[str]
            Requested expiration date for the card. If not provided, defaults to 30 days from creation.

        amount : typing.Optional[float]
            Initial load amount for the card. Defaults to `0`.

        max_number_of_uses : typing.Optional[int]
            Maximum number of times the card can be used. If `0` or negative, defaults to `9999`. Ignored and set to `1` when `exactAmount` is `true`.

        exact_amount : typing.Optional[bool]
            When `true`, restricts the card to a single use. `maxNumberOfUses` is automatically set to `1` regardless of any other value provided.

        expense_limit_period : typing.Optional[str]
            Time period over which `expenseLimit` applies (for example, `monthly` or `weekly`). No server-side enforcement.

        billing_cycle : typing.Optional[str]
            Billing cycle identifier.

        billing_cycle_day : typing.Optional[str]
            Day within the billing cycle.

        daily_transaction_count : typing.Optional[int]
            Maximum number of transactions allowed per day. Defaults to `0` (unlimited).

        daily_amount_limit : typing.Optional[float]
            Maximum total spend allowed per day. Defaults to `0` (unlimited).

        transaction_amount_limit : typing.Optional[int]
            Maximum spend allowed per single transaction. Defaults to `0` (unlimited).

        mcc : typing.Optional[str]
            Merchant Category Code to restrict where the card can be used. Must be a valid MCC if provided.

        tcc : typing.Optional[str]
            Transaction Category Code to restrict where the card can be used. Must be a valid TCC if provided.

        misc_1 : typing.Optional[str]
            Custom metadata field. Stored on the card record.

        misc_2 : typing.Optional[str]
            Custom metadata field. Stored on the card record.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[CreateGhostCardResponse]
            Success response with ghost card details.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"MoneyOutCard/GhostCard/{jsonable_encoder(entry)}",
            method="POST",
            json={
                "vendorId": vendor_id,
                "expenseLimit": expense_limit,
                "expirationDate": expiration_date,
                "amount": amount,
                "maxNumberOfUses": max_number_of_uses,
                "exactAmount": exact_amount,
                "expenseLimitPeriod": expense_limit_period,
                "billingCycle": billing_cycle,
                "billingCycleDay": billing_cycle_day,
                "dailyTransactionCount": daily_transaction_count,
                "dailyAmountLimit": daily_amount_limit,
                "transactionAmountLimit": transaction_amount_limit,
                "mcc": mcc,
                "tcc": tcc,
                "misc1": misc_1,
                "misc2": misc_2,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CreateGhostCardResponse,
                    parse_obj_as(
                        type_=CreateGhostCardResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 503:
                raise ServiceUnavailableError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        PayabliApiResponse,
                        parse_obj_as(
                            type_=PayabliApiResponse,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        except ValidationError as e:
            raise ParsingError(
                status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
            )
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def update_card(
        self,
        entry: Entry,
        *,
        card_token: str,
        status: typing.Optional[CardStatus] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[PayabliApiResponse]:
        """
        Updates the status of a virtual card (including ghost cards) under a paypoint.

        Parameters
        ----------
        entry : Entry

        card_token : str
            Token that uniquely identifies the card. This is the `ReferenceId` returned when the card was created.

        status : typing.Optional[CardStatus]
            The new status to set on the card.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[PayabliApiResponse]
            Success response.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"MoneyOutCard/card/{jsonable_encoder(entry)}",
            method="PATCH",
            json={
                "cardToken": card_token,
                "status": status,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    PayabliApiResponse,
                    parse_obj_as(
                        type_=PayabliApiResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 503:
                raise ServiceUnavailableError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        PayabliApiResponse,
                        parse_obj_as(
                            type_=PayabliApiResponse,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        except ValidationError as e:
            raise ParsingError(
                status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
            )
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)


class AsyncRawGhostCardClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def create_ghost_card(
        self,
        entry: Entry,
        *,
        vendor_id: int,
        expense_limit: float,
        expiration_date: typing.Optional[str] = OMIT,
        amount: typing.Optional[float] = OMIT,
        max_number_of_uses: typing.Optional[int] = OMIT,
        exact_amount: typing.Optional[bool] = OMIT,
        expense_limit_period: typing.Optional[str] = OMIT,
        billing_cycle: typing.Optional[str] = OMIT,
        billing_cycle_day: typing.Optional[str] = OMIT,
        daily_transaction_count: typing.Optional[int] = OMIT,
        daily_amount_limit: typing.Optional[float] = OMIT,
        transaction_amount_limit: typing.Optional[int] = OMIT,
        mcc: typing.Optional[str] = OMIT,
        tcc: typing.Optional[str] = OMIT,
        misc_1: typing.Optional[str] = OMIT,
        misc_2: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[CreateGhostCardResponse]:
        """
        Creates a ghost card, a multi-use virtual debit card issued to a vendor for recurring or discretionary spend.

        Unlike single-use virtual cards issued as part of a payout transaction, ghost cards aren't tied to a specific payout. They're issued directly to a vendor and can be reused up to a configurable number of times within the card's spending limits.

        Parameters
        ----------
        entry : Entry

        vendor_id : int
            ID of the vendor who receives the card. The vendor must belong to the paypoint and have an active status.

        expense_limit : float
            Spending limit for the card. Must be greater than `0` and can't exceed the paypoint's configured payout credit limit.

        expiration_date : typing.Optional[str]
            Requested expiration date for the card. If not provided, defaults to 30 days from creation.

        amount : typing.Optional[float]
            Initial load amount for the card. Defaults to `0`.

        max_number_of_uses : typing.Optional[int]
            Maximum number of times the card can be used. If `0` or negative, defaults to `9999`. Ignored and set to `1` when `exactAmount` is `true`.

        exact_amount : typing.Optional[bool]
            When `true`, restricts the card to a single use. `maxNumberOfUses` is automatically set to `1` regardless of any other value provided.

        expense_limit_period : typing.Optional[str]
            Time period over which `expenseLimit` applies (for example, `monthly` or `weekly`). No server-side enforcement.

        billing_cycle : typing.Optional[str]
            Billing cycle identifier.

        billing_cycle_day : typing.Optional[str]
            Day within the billing cycle.

        daily_transaction_count : typing.Optional[int]
            Maximum number of transactions allowed per day. Defaults to `0` (unlimited).

        daily_amount_limit : typing.Optional[float]
            Maximum total spend allowed per day. Defaults to `0` (unlimited).

        transaction_amount_limit : typing.Optional[int]
            Maximum spend allowed per single transaction. Defaults to `0` (unlimited).

        mcc : typing.Optional[str]
            Merchant Category Code to restrict where the card can be used. Must be a valid MCC if provided.

        tcc : typing.Optional[str]
            Transaction Category Code to restrict where the card can be used. Must be a valid TCC if provided.

        misc_1 : typing.Optional[str]
            Custom metadata field. Stored on the card record.

        misc_2 : typing.Optional[str]
            Custom metadata field. Stored on the card record.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[CreateGhostCardResponse]
            Success response with ghost card details.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"MoneyOutCard/GhostCard/{jsonable_encoder(entry)}",
            method="POST",
            json={
                "vendorId": vendor_id,
                "expenseLimit": expense_limit,
                "expirationDate": expiration_date,
                "amount": amount,
                "maxNumberOfUses": max_number_of_uses,
                "exactAmount": exact_amount,
                "expenseLimitPeriod": expense_limit_period,
                "billingCycle": billing_cycle,
                "billingCycleDay": billing_cycle_day,
                "dailyTransactionCount": daily_transaction_count,
                "dailyAmountLimit": daily_amount_limit,
                "transactionAmountLimit": transaction_amount_limit,
                "mcc": mcc,
                "tcc": tcc,
                "misc1": misc_1,
                "misc2": misc_2,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CreateGhostCardResponse,
                    parse_obj_as(
                        type_=CreateGhostCardResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 503:
                raise ServiceUnavailableError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        PayabliApiResponse,
                        parse_obj_as(
                            type_=PayabliApiResponse,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        except ValidationError as e:
            raise ParsingError(
                status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
            )
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def update_card(
        self,
        entry: Entry,
        *,
        card_token: str,
        status: typing.Optional[CardStatus] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[PayabliApiResponse]:
        """
        Updates the status of a virtual card (including ghost cards) under a paypoint.

        Parameters
        ----------
        entry : Entry

        card_token : str
            Token that uniquely identifies the card. This is the `ReferenceId` returned when the card was created.

        status : typing.Optional[CardStatus]
            The new status to set on the card.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[PayabliApiResponse]
            Success response.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"MoneyOutCard/card/{jsonable_encoder(entry)}",
            method="PATCH",
            json={
                "cardToken": card_token,
                "status": status,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    PayabliApiResponse,
                    parse_obj_as(
                        type_=PayabliApiResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 503:
                raise ServiceUnavailableError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        PayabliApiResponse,
                        parse_obj_as(
                            type_=PayabliApiResponse,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        except ValidationError as e:
            raise ParsingError(
                status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
            )
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
