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

import typing
from ...core.client_wrapper import SyncClientWrapper
from ...requests.loyalty_account import LoyaltyAccountParams
from ...core.request_options import RequestOptions
from ...core.http_response import HttpResponse
from ...types.create_loyalty_account_response import CreateLoyaltyAccountResponse
from ...core.serialization import convert_and_respect_annotation_metadata
from ...core.unchecked_base_model import construct_type
from json.decoder import JSONDecodeError
from ...core.api_error import ApiError
from ...requests.search_loyalty_accounts_request_loyalty_account_query import (
    SearchLoyaltyAccountsRequestLoyaltyAccountQueryParams,
)
from ...types.search_loyalty_accounts_response import SearchLoyaltyAccountsResponse
from ...types.get_loyalty_account_response import GetLoyaltyAccountResponse
from ...core.jsonable_encoder import jsonable_encoder
from ...requests.loyalty_event_accumulate_points import LoyaltyEventAccumulatePointsParams
from ...types.accumulate_loyalty_points_response import AccumulateLoyaltyPointsResponse
from ...requests.loyalty_event_adjust_points import LoyaltyEventAdjustPointsParams
from ...types.adjust_loyalty_points_response import AdjustLoyaltyPointsResponse
from ...core.client_wrapper import AsyncClientWrapper
from ...core.http_response import AsyncHttpResponse

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


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

    def create(
        self,
        *,
        loyalty_account: LoyaltyAccountParams,
        idempotency_key: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[CreateLoyaltyAccountResponse]:
        """
        Creates a loyalty account. To create a loyalty account, you must provide the `program_id` and a `mapping` with the `phone_number` of the buyer.

        Parameters
        ----------
        loyalty_account : LoyaltyAccountParams
            The loyalty account to create.

        idempotency_key : str
            A unique string that identifies this `CreateLoyaltyAccount` request.
            Keys can be any valid string, but must be unique for every request.

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

        Returns
        -------
        HttpResponse[CreateLoyaltyAccountResponse]
            Success
        """
        _response = self._client_wrapper.httpx_client.request(
            "v2/loyalty/accounts",
            method="POST",
            json={
                "loyalty_account": convert_and_respect_annotation_metadata(
                    object_=loyalty_account, annotation=LoyaltyAccountParams, 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(
                    CreateLoyaltyAccountResponse,
                    construct_type(
                        type_=CreateLoyaltyAccountResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def search(
        self,
        *,
        query: typing.Optional[SearchLoyaltyAccountsRequestLoyaltyAccountQueryParams] = OMIT,
        limit: typing.Optional[int] = OMIT,
        cursor: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[SearchLoyaltyAccountsResponse]:
        """
        Searches for loyalty accounts in a loyalty program.

        You can search for a loyalty account using the phone number or customer ID associated with the account. To return all loyalty accounts, specify an empty `query` object or omit it entirely.

        Search results are sorted by `created_at` in ascending order.

        Parameters
        ----------
        query : typing.Optional[SearchLoyaltyAccountsRequestLoyaltyAccountQueryParams]
            The search criteria for the request.

        limit : typing.Optional[int]
            The maximum number of results to include in the response. The default value is 30.

        cursor : typing.Optional[str]
            A pagination cursor returned by a previous call to
            this endpoint. Provide this to retrieve the next set of
            results for the original query.

            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
        -------
        HttpResponse[SearchLoyaltyAccountsResponse]
            Success
        """
        _response = self._client_wrapper.httpx_client.request(
            "v2/loyalty/accounts/search",
            method="POST",
            json={
                "query": convert_and_respect_annotation_metadata(
                    object_=query, annotation=SearchLoyaltyAccountsRequestLoyaltyAccountQueryParams, direction="write"
                ),
                "limit": limit,
                "cursor": cursor,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    SearchLoyaltyAccountsResponse,
                    construct_type(
                        type_=SearchLoyaltyAccountsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def get(
        self, account_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[GetLoyaltyAccountResponse]:
        """
        Retrieves a loyalty account.

        Parameters
        ----------
        account_id : str
            The ID of the [loyalty account](entity:LoyaltyAccount) to retrieve.

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

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

    def accumulate_points(
        self,
        account_id: str,
        *,
        accumulate_points: LoyaltyEventAccumulatePointsParams,
        idempotency_key: str,
        location_id: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[AccumulateLoyaltyPointsResponse]:
        """
        Adds points earned from a purchase to a [loyalty account](entity:LoyaltyAccount).

        - If you are using the Orders API to manage orders, provide the `order_id`. Square reads the order
        to compute the points earned from both the base loyalty program and an associated
        [loyalty promotion](entity:LoyaltyPromotion). For purchases that qualify for multiple accrual
        rules, Square computes points based on the accrual rule that grants the most points.
        For purchases that qualify for multiple promotions, Square computes points based on the most
        recently created promotion. A purchase must first qualify for program points to be eligible for promotion points.

        - If you are not using the Orders API to manage orders, provide `points` with the number of points to add.
        You must first perform a client-side computation of the points earned from the loyalty program and
        loyalty promotion. For spend-based and visit-based programs, you can call [CalculateLoyaltyPoints](api-endpoint:Loyalty-CalculateLoyaltyPoints)
        to compute the points earned from the base loyalty program. For information about computing points earned from a loyalty promotion, see
        [Calculating promotion points](https://developer.squareup.com/docs/loyalty-api/loyalty-promotions#calculate-promotion-points).

        Parameters
        ----------
        account_id : str
            The ID of the target [loyalty account](entity:LoyaltyAccount).

        accumulate_points : LoyaltyEventAccumulatePointsParams
            The points to add to the account.
            If you are using the Orders API to manage orders, specify the order ID.
            Otherwise, specify the points to add.

        idempotency_key : str
            A unique string that identifies the `AccumulateLoyaltyPoints` request.
            Keys can be any valid string but must be unique for every request.

        location_id : str
            The [location](entity:Location) where the purchase was made.

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

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

    def adjust(
        self,
        account_id: str,
        *,
        idempotency_key: str,
        adjust_points: LoyaltyEventAdjustPointsParams,
        allow_negative_balance: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[AdjustLoyaltyPointsResponse]:
        """
        Adds points to or subtracts points from a buyer's account.

        Use this endpoint only when you need to manually adjust points. Otherwise, in your application flow, you call
        [AccumulateLoyaltyPoints](api-endpoint:Loyalty-AccumulateLoyaltyPoints)
        to add points when a buyer pays for the purchase.

        Parameters
        ----------
        account_id : str
            The ID of the target [loyalty account](entity:LoyaltyAccount).

        idempotency_key : str
            A unique string that identifies this `AdjustLoyaltyPoints` request.
            Keys can be any valid string, but must be unique for every request.

        adjust_points : LoyaltyEventAdjustPointsParams
            The points to add or subtract and the reason for the adjustment. To add points, specify a positive integer.
            To subtract points, specify a negative integer.

        allow_negative_balance : typing.Optional[bool]
            Indicates whether to allow a negative adjustment to result in a negative balance. If `true`, a negative
            balance is allowed when subtracting points. If `false`, Square returns a `BAD_REQUEST` error when subtracting
            the specified number of points would result in a negative balance. The default value is `false`.

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

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


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

    async def create(
        self,
        *,
        loyalty_account: LoyaltyAccountParams,
        idempotency_key: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[CreateLoyaltyAccountResponse]:
        """
        Creates a loyalty account. To create a loyalty account, you must provide the `program_id` and a `mapping` with the `phone_number` of the buyer.

        Parameters
        ----------
        loyalty_account : LoyaltyAccountParams
            The loyalty account to create.

        idempotency_key : str
            A unique string that identifies this `CreateLoyaltyAccount` request.
            Keys can be any valid string, but must be unique for every request.

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

        Returns
        -------
        AsyncHttpResponse[CreateLoyaltyAccountResponse]
            Success
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v2/loyalty/accounts",
            method="POST",
            json={
                "loyalty_account": convert_and_respect_annotation_metadata(
                    object_=loyalty_account, annotation=LoyaltyAccountParams, 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(
                    CreateLoyaltyAccountResponse,
                    construct_type(
                        type_=CreateLoyaltyAccountResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def search(
        self,
        *,
        query: typing.Optional[SearchLoyaltyAccountsRequestLoyaltyAccountQueryParams] = OMIT,
        limit: typing.Optional[int] = OMIT,
        cursor: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[SearchLoyaltyAccountsResponse]:
        """
        Searches for loyalty accounts in a loyalty program.

        You can search for a loyalty account using the phone number or customer ID associated with the account. To return all loyalty accounts, specify an empty `query` object or omit it entirely.

        Search results are sorted by `created_at` in ascending order.

        Parameters
        ----------
        query : typing.Optional[SearchLoyaltyAccountsRequestLoyaltyAccountQueryParams]
            The search criteria for the request.

        limit : typing.Optional[int]
            The maximum number of results to include in the response. The default value is 30.

        cursor : typing.Optional[str]
            A pagination cursor returned by a previous call to
            this endpoint. Provide this to retrieve the next set of
            results for the original query.

            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
        -------
        AsyncHttpResponse[SearchLoyaltyAccountsResponse]
            Success
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v2/loyalty/accounts/search",
            method="POST",
            json={
                "query": convert_and_respect_annotation_metadata(
                    object_=query, annotation=SearchLoyaltyAccountsRequestLoyaltyAccountQueryParams, direction="write"
                ),
                "limit": limit,
                "cursor": cursor,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    SearchLoyaltyAccountsResponse,
                    construct_type(
                        type_=SearchLoyaltyAccountsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def get(
        self, account_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[GetLoyaltyAccountResponse]:
        """
        Retrieves a loyalty account.

        Parameters
        ----------
        account_id : str
            The ID of the [loyalty account](entity:LoyaltyAccount) to retrieve.

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

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

    async def accumulate_points(
        self,
        account_id: str,
        *,
        accumulate_points: LoyaltyEventAccumulatePointsParams,
        idempotency_key: str,
        location_id: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[AccumulateLoyaltyPointsResponse]:
        """
        Adds points earned from a purchase to a [loyalty account](entity:LoyaltyAccount).

        - If you are using the Orders API to manage orders, provide the `order_id`. Square reads the order
        to compute the points earned from both the base loyalty program and an associated
        [loyalty promotion](entity:LoyaltyPromotion). For purchases that qualify for multiple accrual
        rules, Square computes points based on the accrual rule that grants the most points.
        For purchases that qualify for multiple promotions, Square computes points based on the most
        recently created promotion. A purchase must first qualify for program points to be eligible for promotion points.

        - If you are not using the Orders API to manage orders, provide `points` with the number of points to add.
        You must first perform a client-side computation of the points earned from the loyalty program and
        loyalty promotion. For spend-based and visit-based programs, you can call [CalculateLoyaltyPoints](api-endpoint:Loyalty-CalculateLoyaltyPoints)
        to compute the points earned from the base loyalty program. For information about computing points earned from a loyalty promotion, see
        [Calculating promotion points](https://developer.squareup.com/docs/loyalty-api/loyalty-promotions#calculate-promotion-points).

        Parameters
        ----------
        account_id : str
            The ID of the target [loyalty account](entity:LoyaltyAccount).

        accumulate_points : LoyaltyEventAccumulatePointsParams
            The points to add to the account.
            If you are using the Orders API to manage orders, specify the order ID.
            Otherwise, specify the points to add.

        idempotency_key : str
            A unique string that identifies the `AccumulateLoyaltyPoints` request.
            Keys can be any valid string but must be unique for every request.

        location_id : str
            The [location](entity:Location) where the purchase was made.

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

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

    async def adjust(
        self,
        account_id: str,
        *,
        idempotency_key: str,
        adjust_points: LoyaltyEventAdjustPointsParams,
        allow_negative_balance: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[AdjustLoyaltyPointsResponse]:
        """
        Adds points to or subtracts points from a buyer's account.

        Use this endpoint only when you need to manually adjust points. Otherwise, in your application flow, you call
        [AccumulateLoyaltyPoints](api-endpoint:Loyalty-AccumulateLoyaltyPoints)
        to add points when a buyer pays for the purchase.

        Parameters
        ----------
        account_id : str
            The ID of the target [loyalty account](entity:LoyaltyAccount).

        idempotency_key : str
            A unique string that identifies this `AdjustLoyaltyPoints` request.
            Keys can be any valid string, but must be unique for every request.

        adjust_points : LoyaltyEventAdjustPointsParams
            The points to add or subtract and the reason for the adjustment. To add points, specify a positive integer.
            To subtract points, specify a negative integer.

        allow_negative_balance : typing.Optional[bool]
            Indicates whether to allow a negative adjustment to result in a negative balance. If `true`, a negative
            balance is allowed when subtracting points. If `false`, Square returns a `BAD_REQUEST` error when subtracting
            the specified number of points would result in a negative balance. The default value is `false`.

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

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