# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from __future__ import annotations

from typing import Iterable

import httpx

from ...types import user_list_params, user_upsert_params
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
from ..._utils import maybe_transform, async_maybe_transform
from ..._compat import cached_property
from .deliveries import (
    DeliveriesResource,
    AsyncDeliveriesResource,
    DeliveriesResourceWithRawResponse,
    AsyncDeliveriesResourceWithRawResponse,
    DeliveriesResourceWithStreamingResponse,
    AsyncDeliveriesResourceWithStreamingResponse,
)
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
    to_raw_response_wrapper,
    to_streamed_response_wrapper,
    async_to_raw_response_wrapper,
    async_to_streamed_response_wrapper,
)
from .preferences import (
    PreferencesResource,
    AsyncPreferencesResource,
    PreferencesResourceWithRawResponse,
    AsyncPreferencesResourceWithRawResponse,
    PreferencesResourceWithStreamingResponse,
    AsyncPreferencesResourceWithStreamingResponse,
)
from ...pagination import SyncCursorPage, AsyncCursorPage
from ...types.user import User
from .notifications import (
    NotificationsResource,
    AsyncNotificationsResource,
    NotificationsResourceWithRawResponse,
    AsyncNotificationsResourceWithRawResponse,
    NotificationsResourceWithStreamingResponse,
    AsyncNotificationsResourceWithStreamingResponse,
)
from .subscriptions import (
    SubscriptionsResource,
    AsyncSubscriptionsResource,
    SubscriptionsResourceWithRawResponse,
    AsyncSubscriptionsResourceWithRawResponse,
    SubscriptionsResourceWithStreamingResponse,
    AsyncSubscriptionsResourceWithStreamingResponse,
)
from ..._base_client import AsyncPaginator, make_request_options
from .channel_settings import (
    ChannelSettingsResource,
    AsyncChannelSettingsResource,
    ChannelSettingsResourceWithRawResponse,
    AsyncChannelSettingsResourceWithRawResponse,
    ChannelSettingsResourceWithStreamingResponse,
    AsyncChannelSettingsResourceWithStreamingResponse,
)
from ...types.users.update_preferences_request_param import UpdatePreferencesRequestParam

__all__ = ["UsersResource", "AsyncUsersResource"]


class UsersResource(SyncAPIResource):
    @cached_property
    def channel_settings(self) -> ChannelSettingsResource:
        return ChannelSettingsResource(self._client)

    @cached_property
    def deliveries(self) -> DeliveriesResource:
        return DeliveriesResource(self._client)

    @cached_property
    def notifications(self) -> NotificationsResource:
        return NotificationsResource(self._client)

    @cached_property
    def preferences(self) -> PreferencesResource:
        return PreferencesResource(self._client)

    @cached_property
    def subscriptions(self) -> SubscriptionsResource:
        return SubscriptionsResource(self._client)

    @cached_property
    def with_raw_response(self) -> UsersResourceWithRawResponse:
        """
        This property can be used as a prefix for any HTTP method call to return
        the raw response object instead of the parsed content.

        For more information, see https://www.github.com/stainless-sdks/notiflows-sdk-python#accessing-raw-response-data-eg-headers
        """
        return UsersResourceWithRawResponse(self)

    @cached_property
    def with_streaming_response(self) -> UsersResourceWithStreamingResponse:
        """
        An alternative to `.with_raw_response` that doesn't eagerly read the response body.

        For more information, see https://www.github.com/stainless-sdks/notiflows-sdk-python#with_streaming_response
        """
        return UsersResourceWithStreamingResponse(self)

    def retrieve(
        self,
        user_external_id: str,
        *,
        # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
        # The extra values given here take precedence over values defined on the client or passed to this method.
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = not_given,
    ) -> User:
        """Retrieve a single user by their external ID.

        The external ID is the identifier
        you assigned when creating the user.

        Args:
          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        if not user_external_id:
            raise ValueError(f"Expected a non-empty value for `user_external_id` but received {user_external_id!r}")
        return self._get(
            f"/users/{user_external_id}",
            options=make_request_options(
                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
            ),
            cast_to=User,
        )

    def list(
        self,
        *,
        after: str | Omit = omit,
        before: str | Omit = omit,
        created_after: str | Omit = omit,
        created_before: str | Omit = omit,
        limit: int | Omit = omit,
        # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
        # The extra values given here take precedence over values defined on the client or passed to this method.
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = not_given,
    ) -> SyncCursorPage[User]:
        """Retrieve a paginated list of users in your project.

        Users are sorted by creation
        date (newest first).

        Args:
          after: Cursor for fetching the next page

          before: Cursor for fetching the previous page

          created_after: Filter users created after this datetime (ISO 8601)

          created_before: Filter users created before this datetime (ISO 8601)

          limit: Number of items per page (default: 25, max: 1000)

          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        return self._get_api_list(
            "/users",
            page=SyncCursorPage[User],
            options=make_request_options(
                extra_headers=extra_headers,
                extra_query=extra_query,
                extra_body=extra_body,
                timeout=timeout,
                query=maybe_transform(
                    {
                        "after": after,
                        "before": before,
                        "created_after": created_after,
                        "created_before": created_before,
                        "limit": limit,
                    },
                    user_list_params.UserListParams,
                ),
            ),
            model=User,
        )

    def delete(
        self,
        user_external_id: str,
        *,
        # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
        # The extra values given here take precedence over values defined on the client or passed to this method.
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = not_given,
    ) -> None:
        """
        Permanently delete a user and all their associated data including:

        - Subscriptions
        - Preferences
        - Channel settings
        - Notifications and deliveries

        **This action cannot be undone.**

        Args:
          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        if not user_external_id:
            raise ValueError(f"Expected a non-empty value for `user_external_id` but received {user_external_id!r}")
        extra_headers = {"Accept": "*/*", **(extra_headers or {})}
        return self._delete(
            f"/users/{user_external_id}",
            options=make_request_options(
                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
            ),
            cast_to=NoneType,
        )

    def upsert(
        self,
        user_external_id: str,
        *,
        avatar: str | Omit = omit,
        channel_settings: Iterable[user_upsert_params.ChannelSetting] | Omit = omit,
        custom_fields: object | Omit = omit,
        email: str | Omit = omit,
        first_name: str | Omit = omit,
        last_name: str | Omit = omit,
        locale: str | Omit = omit,
        phone: str | Omit = omit,
        preferences: UpdatePreferencesRequestParam | Omit = omit,
        timezone: str | Omit = omit,
        # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
        # The extra values given here take precedence over values defined on the client or passed to this method.
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = not_given,
    ) -> User:
        """
        Create a new user or update an existing user by external ID (upsert operation).

        - If the user doesn't exist, they will be created with the provided attributes.
        - If the user exists, their attributes will be updated (merge behavior).
        - `custom_fields` are deep-merged with existing values.

        **Returns:**

        - `201 Created` when a new user is created
        - `200 OK` when an existing user is updated

        Args:
          avatar: URL to user's avatar image (must start with http:// or https://)

          channel_settings: Initial channel settings (e.g., device tokens for push)

          custom_fields: Custom attributes. Deep-merged with existing values on update.

          email: User's email address

          first_name: User's first name

          last_name: User's last name

          locale: BCP 47 locale code (e.g., en-US, es-ES, fr-FR)

          phone: User's phone number in E.164 format

          preferences: Request body for updating user preferences. Only provided fields are updated.

          timezone: IANA timezone (e.g., America/New_York, Europe/London)

          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        if not user_external_id:
            raise ValueError(f"Expected a non-empty value for `user_external_id` but received {user_external_id!r}")
        return self._put(
            f"/users/{user_external_id}",
            body=maybe_transform(
                {
                    "avatar": avatar,
                    "channel_settings": channel_settings,
                    "custom_fields": custom_fields,
                    "email": email,
                    "first_name": first_name,
                    "last_name": last_name,
                    "locale": locale,
                    "phone": phone,
                    "preferences": preferences,
                    "timezone": timezone,
                },
                user_upsert_params.UserUpsertParams,
            ),
            options=make_request_options(
                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
            ),
            cast_to=User,
        )


class AsyncUsersResource(AsyncAPIResource):
    @cached_property
    def channel_settings(self) -> AsyncChannelSettingsResource:
        return AsyncChannelSettingsResource(self._client)

    @cached_property
    def deliveries(self) -> AsyncDeliveriesResource:
        return AsyncDeliveriesResource(self._client)

    @cached_property
    def notifications(self) -> AsyncNotificationsResource:
        return AsyncNotificationsResource(self._client)

    @cached_property
    def preferences(self) -> AsyncPreferencesResource:
        return AsyncPreferencesResource(self._client)

    @cached_property
    def subscriptions(self) -> AsyncSubscriptionsResource:
        return AsyncSubscriptionsResource(self._client)

    @cached_property
    def with_raw_response(self) -> AsyncUsersResourceWithRawResponse:
        """
        This property can be used as a prefix for any HTTP method call to return
        the raw response object instead of the parsed content.

        For more information, see https://www.github.com/stainless-sdks/notiflows-sdk-python#accessing-raw-response-data-eg-headers
        """
        return AsyncUsersResourceWithRawResponse(self)

    @cached_property
    def with_streaming_response(self) -> AsyncUsersResourceWithStreamingResponse:
        """
        An alternative to `.with_raw_response` that doesn't eagerly read the response body.

        For more information, see https://www.github.com/stainless-sdks/notiflows-sdk-python#with_streaming_response
        """
        return AsyncUsersResourceWithStreamingResponse(self)

    async def retrieve(
        self,
        user_external_id: str,
        *,
        # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
        # The extra values given here take precedence over values defined on the client or passed to this method.
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = not_given,
    ) -> User:
        """Retrieve a single user by their external ID.

        The external ID is the identifier
        you assigned when creating the user.

        Args:
          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        if not user_external_id:
            raise ValueError(f"Expected a non-empty value for `user_external_id` but received {user_external_id!r}")
        return await self._get(
            f"/users/{user_external_id}",
            options=make_request_options(
                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
            ),
            cast_to=User,
        )

    def list(
        self,
        *,
        after: str | Omit = omit,
        before: str | Omit = omit,
        created_after: str | Omit = omit,
        created_before: str | Omit = omit,
        limit: int | Omit = omit,
        # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
        # The extra values given here take precedence over values defined on the client or passed to this method.
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = not_given,
    ) -> AsyncPaginator[User, AsyncCursorPage[User]]:
        """Retrieve a paginated list of users in your project.

        Users are sorted by creation
        date (newest first).

        Args:
          after: Cursor for fetching the next page

          before: Cursor for fetching the previous page

          created_after: Filter users created after this datetime (ISO 8601)

          created_before: Filter users created before this datetime (ISO 8601)

          limit: Number of items per page (default: 25, max: 1000)

          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        return self._get_api_list(
            "/users",
            page=AsyncCursorPage[User],
            options=make_request_options(
                extra_headers=extra_headers,
                extra_query=extra_query,
                extra_body=extra_body,
                timeout=timeout,
                query=maybe_transform(
                    {
                        "after": after,
                        "before": before,
                        "created_after": created_after,
                        "created_before": created_before,
                        "limit": limit,
                    },
                    user_list_params.UserListParams,
                ),
            ),
            model=User,
        )

    async def delete(
        self,
        user_external_id: str,
        *,
        # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
        # The extra values given here take precedence over values defined on the client or passed to this method.
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = not_given,
    ) -> None:
        """
        Permanently delete a user and all their associated data including:

        - Subscriptions
        - Preferences
        - Channel settings
        - Notifications and deliveries

        **This action cannot be undone.**

        Args:
          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        if not user_external_id:
            raise ValueError(f"Expected a non-empty value for `user_external_id` but received {user_external_id!r}")
        extra_headers = {"Accept": "*/*", **(extra_headers or {})}
        return await self._delete(
            f"/users/{user_external_id}",
            options=make_request_options(
                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
            ),
            cast_to=NoneType,
        )

    async def upsert(
        self,
        user_external_id: str,
        *,
        avatar: str | Omit = omit,
        channel_settings: Iterable[user_upsert_params.ChannelSetting] | Omit = omit,
        custom_fields: object | Omit = omit,
        email: str | Omit = omit,
        first_name: str | Omit = omit,
        last_name: str | Omit = omit,
        locale: str | Omit = omit,
        phone: str | Omit = omit,
        preferences: UpdatePreferencesRequestParam | Omit = omit,
        timezone: str | Omit = omit,
        # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
        # The extra values given here take precedence over values defined on the client or passed to this method.
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = not_given,
    ) -> User:
        """
        Create a new user or update an existing user by external ID (upsert operation).

        - If the user doesn't exist, they will be created with the provided attributes.
        - If the user exists, their attributes will be updated (merge behavior).
        - `custom_fields` are deep-merged with existing values.

        **Returns:**

        - `201 Created` when a new user is created
        - `200 OK` when an existing user is updated

        Args:
          avatar: URL to user's avatar image (must start with http:// or https://)

          channel_settings: Initial channel settings (e.g., device tokens for push)

          custom_fields: Custom attributes. Deep-merged with existing values on update.

          email: User's email address

          first_name: User's first name

          last_name: User's last name

          locale: BCP 47 locale code (e.g., en-US, es-ES, fr-FR)

          phone: User's phone number in E.164 format

          preferences: Request body for updating user preferences. Only provided fields are updated.

          timezone: IANA timezone (e.g., America/New_York, Europe/London)

          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        if not user_external_id:
            raise ValueError(f"Expected a non-empty value for `user_external_id` but received {user_external_id!r}")
        return await self._put(
            f"/users/{user_external_id}",
            body=await async_maybe_transform(
                {
                    "avatar": avatar,
                    "channel_settings": channel_settings,
                    "custom_fields": custom_fields,
                    "email": email,
                    "first_name": first_name,
                    "last_name": last_name,
                    "locale": locale,
                    "phone": phone,
                    "preferences": preferences,
                    "timezone": timezone,
                },
                user_upsert_params.UserUpsertParams,
            ),
            options=make_request_options(
                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
            ),
            cast_to=User,
        )


class UsersResourceWithRawResponse:
    def __init__(self, users: UsersResource) -> None:
        self._users = users

        self.retrieve = to_raw_response_wrapper(
            users.retrieve,
        )
        self.list = to_raw_response_wrapper(
            users.list,
        )
        self.delete = to_raw_response_wrapper(
            users.delete,
        )
        self.upsert = to_raw_response_wrapper(
            users.upsert,
        )

    @cached_property
    def channel_settings(self) -> ChannelSettingsResourceWithRawResponse:
        return ChannelSettingsResourceWithRawResponse(self._users.channel_settings)

    @cached_property
    def deliveries(self) -> DeliveriesResourceWithRawResponse:
        return DeliveriesResourceWithRawResponse(self._users.deliveries)

    @cached_property
    def notifications(self) -> NotificationsResourceWithRawResponse:
        return NotificationsResourceWithRawResponse(self._users.notifications)

    @cached_property
    def preferences(self) -> PreferencesResourceWithRawResponse:
        return PreferencesResourceWithRawResponse(self._users.preferences)

    @cached_property
    def subscriptions(self) -> SubscriptionsResourceWithRawResponse:
        return SubscriptionsResourceWithRawResponse(self._users.subscriptions)


class AsyncUsersResourceWithRawResponse:
    def __init__(self, users: AsyncUsersResource) -> None:
        self._users = users

        self.retrieve = async_to_raw_response_wrapper(
            users.retrieve,
        )
        self.list = async_to_raw_response_wrapper(
            users.list,
        )
        self.delete = async_to_raw_response_wrapper(
            users.delete,
        )
        self.upsert = async_to_raw_response_wrapper(
            users.upsert,
        )

    @cached_property
    def channel_settings(self) -> AsyncChannelSettingsResourceWithRawResponse:
        return AsyncChannelSettingsResourceWithRawResponse(self._users.channel_settings)

    @cached_property
    def deliveries(self) -> AsyncDeliveriesResourceWithRawResponse:
        return AsyncDeliveriesResourceWithRawResponse(self._users.deliveries)

    @cached_property
    def notifications(self) -> AsyncNotificationsResourceWithRawResponse:
        return AsyncNotificationsResourceWithRawResponse(self._users.notifications)

    @cached_property
    def preferences(self) -> AsyncPreferencesResourceWithRawResponse:
        return AsyncPreferencesResourceWithRawResponse(self._users.preferences)

    @cached_property
    def subscriptions(self) -> AsyncSubscriptionsResourceWithRawResponse:
        return AsyncSubscriptionsResourceWithRawResponse(self._users.subscriptions)


class UsersResourceWithStreamingResponse:
    def __init__(self, users: UsersResource) -> None:
        self._users = users

        self.retrieve = to_streamed_response_wrapper(
            users.retrieve,
        )
        self.list = to_streamed_response_wrapper(
            users.list,
        )
        self.delete = to_streamed_response_wrapper(
            users.delete,
        )
        self.upsert = to_streamed_response_wrapper(
            users.upsert,
        )

    @cached_property
    def channel_settings(self) -> ChannelSettingsResourceWithStreamingResponse:
        return ChannelSettingsResourceWithStreamingResponse(self._users.channel_settings)

    @cached_property
    def deliveries(self) -> DeliveriesResourceWithStreamingResponse:
        return DeliveriesResourceWithStreamingResponse(self._users.deliveries)

    @cached_property
    def notifications(self) -> NotificationsResourceWithStreamingResponse:
        return NotificationsResourceWithStreamingResponse(self._users.notifications)

    @cached_property
    def preferences(self) -> PreferencesResourceWithStreamingResponse:
        return PreferencesResourceWithStreamingResponse(self._users.preferences)

    @cached_property
    def subscriptions(self) -> SubscriptionsResourceWithStreamingResponse:
        return SubscriptionsResourceWithStreamingResponse(self._users.subscriptions)


class AsyncUsersResourceWithStreamingResponse:
    def __init__(self, users: AsyncUsersResource) -> None:
        self._users = users

        self.retrieve = async_to_streamed_response_wrapper(
            users.retrieve,
        )
        self.list = async_to_streamed_response_wrapper(
            users.list,
        )
        self.delete = async_to_streamed_response_wrapper(
            users.delete,
        )
        self.upsert = async_to_streamed_response_wrapper(
            users.upsert,
        )

    @cached_property
    def channel_settings(self) -> AsyncChannelSettingsResourceWithStreamingResponse:
        return AsyncChannelSettingsResourceWithStreamingResponse(self._users.channel_settings)

    @cached_property
    def deliveries(self) -> AsyncDeliveriesResourceWithStreamingResponse:
        return AsyncDeliveriesResourceWithStreamingResponse(self._users.deliveries)

    @cached_property
    def notifications(self) -> AsyncNotificationsResourceWithStreamingResponse:
        return AsyncNotificationsResourceWithStreamingResponse(self._users.notifications)

    @cached_property
    def preferences(self) -> AsyncPreferencesResourceWithStreamingResponse:
        return AsyncPreferencesResourceWithStreamingResponse(self._users.preferences)

    @cached_property
    def subscriptions(self) -> AsyncSubscriptionsResourceWithStreamingResponse:
        return AsyncSubscriptionsResourceWithStreamingResponse(self._users.subscriptions)
