# 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.pagination import AsyncPager, BaseHttpResponse, SyncPager
from ....core.request_options import RequestOptions
from ....core.serialization import convert_and_respect_annotation_metadata
from ....core.unchecked_base_model import construct_type
from ....requests.loyalty_promotion import LoyaltyPromotionParams
from ....types.cancel_loyalty_promotion_response import CancelLoyaltyPromotionResponse
from ....types.create_loyalty_promotion_response import CreateLoyaltyPromotionResponse
from ....types.get_loyalty_promotion_response import GetLoyaltyPromotionResponse
from ....types.list_loyalty_promotions_response import ListLoyaltyPromotionsResponse
from ....types.loyalty_promotion import LoyaltyPromotion
from ....types.loyalty_promotion_status import LoyaltyPromotionStatus

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


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

    def list(
        self,
        program_id: str,
        *,
        status: typing.Optional[LoyaltyPromotionStatus] = None,
        cursor: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> SyncPager[LoyaltyPromotion]:
        """
        Lists the loyalty promotions associated with a [loyalty program](entity:LoyaltyProgram).
        Results are sorted by the `created_at` date in descending order (newest to oldest).

        Parameters
        ----------
        program_id : str
            The ID of the base [loyalty program](entity:LoyaltyProgram). To get the program ID,
            call [RetrieveLoyaltyProgram](api-endpoint:Loyalty-RetrieveLoyaltyProgram) using the `main` keyword.

        status : typing.Optional[LoyaltyPromotionStatus]
            The status to filter the results by. If a status is provided, only loyalty promotions
            with the specified status are returned. Otherwise, all loyalty promotions associated with
            the loyalty program are returned.

        cursor : typing.Optional[str]
            The cursor returned in the paged response from the previous call to this endpoint.
            Provide this cursor to retrieve the next page of results for your original request.
            For more information, see [Pagination](https://developer.squareup.com/docs/build-basics/common-api-patterns/pagination).

        limit : typing.Optional[int]
            The maximum number of results to return in a single paged response.
            The minimum value is 1 and the maximum value is 30. The default value is 30.
            For more information, see [Pagination](https://developer.squareup.com/docs/build-basics/common-api-patterns/pagination).

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

        Returns
        -------
        SyncPager[LoyaltyPromotion]
            Success
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/loyalty/programs/{jsonable_encoder(program_id)}/promotions",
            method="GET",
            params={
                "status": status,
                "cursor": cursor,
                "limit": limit,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ListLoyaltyPromotionsResponse,
                    construct_type(
                        type_=ListLoyaltyPromotionsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.loyalty_promotions
                _parsed_next = _parsed_response.cursor
                _has_next = _parsed_next is not None and _parsed_next != ""
                _get_next = lambda: self.list(
                    program_id,
                    status=status,
                    cursor=_parsed_next,
                    limit=limit,
                    request_options=request_options,
                )
                return SyncPager(
                    has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def create(
        self,
        program_id: str,
        *,
        loyalty_promotion: LoyaltyPromotionParams,
        idempotency_key: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[CreateLoyaltyPromotionResponse]:
        """
        Creates a loyalty promotion for a [loyalty program](entity:LoyaltyProgram). A loyalty promotion
        enables buyers to earn points in addition to those earned from the base loyalty program.

        This endpoint sets the loyalty promotion to the `ACTIVE` or `SCHEDULED` status, depending on the
        `available_time` setting. A loyalty program can have a maximum of 10 loyalty promotions with an
        `ACTIVE` or `SCHEDULED` status.

        Parameters
        ----------
        program_id : str
            The ID of the [loyalty program](entity:LoyaltyProgram) to associate with the promotion.
            To get the program ID, call [RetrieveLoyaltyProgram](api-endpoint:Loyalty-RetrieveLoyaltyProgram)
            using the `main` keyword.

        loyalty_promotion : LoyaltyPromotionParams
            The loyalty promotion to create.

        idempotency_key : str
            A unique identifier for this request, which is used to ensure idempotency. For more information,
            see [Idempotency](https://developer.squareup.com/docs/build-basics/common-api-patterns/idempotency).

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

        Returns
        -------
        HttpResponse[CreateLoyaltyPromotionResponse]
            Success
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/loyalty/programs/{jsonable_encoder(program_id)}/promotions",
            method="POST",
            json={
                "loyalty_promotion": convert_and_respect_annotation_metadata(
                    object_=loyalty_promotion, annotation=LoyaltyPromotionParams, direction="write"
                ),
                "idempotency_key": idempotency_key,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CreateLoyaltyPromotionResponse,
                    construct_type(
                        type_=CreateLoyaltyPromotionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get(
        self, promotion_id: str, program_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[GetLoyaltyPromotionResponse]:
        """
        Retrieves a loyalty promotion.

        Parameters
        ----------
        promotion_id : str
            The ID of the [loyalty promotion](entity:LoyaltyPromotion) to retrieve.

        program_id : str
            The ID of the base [loyalty program](entity:LoyaltyProgram). To get the program ID,
            call [RetrieveLoyaltyProgram](api-endpoint:Loyalty-RetrieveLoyaltyProgram) using the `main` keyword.

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

        Returns
        -------
        HttpResponse[GetLoyaltyPromotionResponse]
            Success
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/loyalty/programs/{jsonable_encoder(program_id)}/promotions/{jsonable_encoder(promotion_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    GetLoyaltyPromotionResponse,
                    construct_type(
                        type_=GetLoyaltyPromotionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def cancel(
        self, promotion_id: str, program_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[CancelLoyaltyPromotionResponse]:
        """
        Cancels a loyalty promotion. Use this endpoint to cancel an `ACTIVE` promotion earlier than the
        end date, cancel an `ACTIVE` promotion when an end date is not specified, or cancel a `SCHEDULED` promotion.
        Because updating a promotion is not supported, you can also use this endpoint to cancel a promotion before
        you create a new one.

        This endpoint sets the loyalty promotion to the `CANCELED` state

        Parameters
        ----------
        promotion_id : str
            The ID of the [loyalty promotion](entity:LoyaltyPromotion) to cancel. You can cancel a
            promotion that has an `ACTIVE` or `SCHEDULED` status.

        program_id : str
            The ID of the base [loyalty program](entity:LoyaltyProgram).

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

        Returns
        -------
        HttpResponse[CancelLoyaltyPromotionResponse]
            Success
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/loyalty/programs/{jsonable_encoder(program_id)}/promotions/{jsonable_encoder(promotion_id)}/cancel",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CancelLoyaltyPromotionResponse,
                    construct_type(
                        type_=CancelLoyaltyPromotionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)


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

    async def list(
        self,
        program_id: str,
        *,
        status: typing.Optional[LoyaltyPromotionStatus] = None,
        cursor: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncPager[LoyaltyPromotion]:
        """
        Lists the loyalty promotions associated with a [loyalty program](entity:LoyaltyProgram).
        Results are sorted by the `created_at` date in descending order (newest to oldest).

        Parameters
        ----------
        program_id : str
            The ID of the base [loyalty program](entity:LoyaltyProgram). To get the program ID,
            call [RetrieveLoyaltyProgram](api-endpoint:Loyalty-RetrieveLoyaltyProgram) using the `main` keyword.

        status : typing.Optional[LoyaltyPromotionStatus]
            The status to filter the results by. If a status is provided, only loyalty promotions
            with the specified status are returned. Otherwise, all loyalty promotions associated with
            the loyalty program are returned.

        cursor : typing.Optional[str]
            The cursor returned in the paged response from the previous call to this endpoint.
            Provide this cursor to retrieve the next page of results for your original request.
            For more information, see [Pagination](https://developer.squareup.com/docs/build-basics/common-api-patterns/pagination).

        limit : typing.Optional[int]
            The maximum number of results to return in a single paged response.
            The minimum value is 1 and the maximum value is 30. The default value is 30.
            For more information, see [Pagination](https://developer.squareup.com/docs/build-basics/common-api-patterns/pagination).

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

        Returns
        -------
        AsyncPager[LoyaltyPromotion]
            Success
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/loyalty/programs/{jsonable_encoder(program_id)}/promotions",
            method="GET",
            params={
                "status": status,
                "cursor": cursor,
                "limit": limit,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ListLoyaltyPromotionsResponse,
                    construct_type(
                        type_=ListLoyaltyPromotionsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.loyalty_promotions
                _parsed_next = _parsed_response.cursor
                _has_next = _parsed_next is not None and _parsed_next != ""

                async def _get_next():
                    return await self.list(
                        program_id,
                        status=status,
                        cursor=_parsed_next,
                        limit=limit,
                        request_options=request_options,
                    )

                return AsyncPager(
                    has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def create(
        self,
        program_id: str,
        *,
        loyalty_promotion: LoyaltyPromotionParams,
        idempotency_key: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[CreateLoyaltyPromotionResponse]:
        """
        Creates a loyalty promotion for a [loyalty program](entity:LoyaltyProgram). A loyalty promotion
        enables buyers to earn points in addition to those earned from the base loyalty program.

        This endpoint sets the loyalty promotion to the `ACTIVE` or `SCHEDULED` status, depending on the
        `available_time` setting. A loyalty program can have a maximum of 10 loyalty promotions with an
        `ACTIVE` or `SCHEDULED` status.

        Parameters
        ----------
        program_id : str
            The ID of the [loyalty program](entity:LoyaltyProgram) to associate with the promotion.
            To get the program ID, call [RetrieveLoyaltyProgram](api-endpoint:Loyalty-RetrieveLoyaltyProgram)
            using the `main` keyword.

        loyalty_promotion : LoyaltyPromotionParams
            The loyalty promotion to create.

        idempotency_key : str
            A unique identifier for this request, which is used to ensure idempotency. For more information,
            see [Idempotency](https://developer.squareup.com/docs/build-basics/common-api-patterns/idempotency).

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

        Returns
        -------
        AsyncHttpResponse[CreateLoyaltyPromotionResponse]
            Success
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/loyalty/programs/{jsonable_encoder(program_id)}/promotions",
            method="POST",
            json={
                "loyalty_promotion": convert_and_respect_annotation_metadata(
                    object_=loyalty_promotion, annotation=LoyaltyPromotionParams, direction="write"
                ),
                "idempotency_key": idempotency_key,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CreateLoyaltyPromotionResponse,
                    construct_type(
                        type_=CreateLoyaltyPromotionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get(
        self, promotion_id: str, program_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[GetLoyaltyPromotionResponse]:
        """
        Retrieves a loyalty promotion.

        Parameters
        ----------
        promotion_id : str
            The ID of the [loyalty promotion](entity:LoyaltyPromotion) to retrieve.

        program_id : str
            The ID of the base [loyalty program](entity:LoyaltyProgram). To get the program ID,
            call [RetrieveLoyaltyProgram](api-endpoint:Loyalty-RetrieveLoyaltyProgram) using the `main` keyword.

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

        Returns
        -------
        AsyncHttpResponse[GetLoyaltyPromotionResponse]
            Success
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/loyalty/programs/{jsonable_encoder(program_id)}/promotions/{jsonable_encoder(promotion_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    GetLoyaltyPromotionResponse,
                    construct_type(
                        type_=GetLoyaltyPromotionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def cancel(
        self, promotion_id: str, program_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[CancelLoyaltyPromotionResponse]:
        """
        Cancels a loyalty promotion. Use this endpoint to cancel an `ACTIVE` promotion earlier than the
        end date, cancel an `ACTIVE` promotion when an end date is not specified, or cancel a `SCHEDULED` promotion.
        Because updating a promotion is not supported, you can also use this endpoint to cancel a promotion before
        you create a new one.

        This endpoint sets the loyalty promotion to the `CANCELED` state

        Parameters
        ----------
        promotion_id : str
            The ID of the [loyalty promotion](entity:LoyaltyPromotion) to cancel. You can cancel a
            promotion that has an `ACTIVE` or `SCHEDULED` status.

        program_id : str
            The ID of the base [loyalty program](entity:LoyaltyProgram).

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

        Returns
        -------
        AsyncHttpResponse[CancelLoyaltyPromotionResponse]
            Success
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/loyalty/programs/{jsonable_encoder(program_id)}/promotions/{jsonable_encoder(promotion_id)}/cancel",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CancelLoyaltyPromotionResponse,
                    construct_type(
                        type_=CancelLoyaltyPromotionResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
