# 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.pydantic_utilities import parse_obj_as
from ..core.request_options import RequestOptions
from ..errors.unprocessable_entity_error import UnprocessableEntityError
from ..types.bulk_operation_response import BulkOperationResponse
from ..types.complete_invitation_response import CompleteInvitationResponse
from ..types.invitation_details_response import InvitationDetailsResponse
from ..types.invitation_response import InvitationResponse

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


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

    def invite_user(
        self,
        organization_id: str,
        *,
        email: str,
        role_id: str,
        full_name: typing.Optional[str] = OMIT,
        send_invitation: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[InvitationResponse]:
        """
        Invite a new user to join the organization.

        Requires:
        - User must be authenticated
        - User must be a member of the organization (any role)

        Args:
            organization_id: Organization ID
            request: Invitation request with email, role_id, and send_invitation flag
            db: Database session
            auth: Authenticated user

        Returns:
            Created invitation details

        Raises:
            ConflictException: If user already exists in organization
            ValidationException: If invalid email or role

        Parameters
        ----------
        organization_id : str
            Organization ID

        email : str
            Email address to invite

        role_id : str
            Role ID to assign

        full_name : typing.Optional[str]
            Full name of the invited user. Set on the user record immediately if provided.

        send_invitation : typing.Optional[bool]
            Whether to send invitation email. Must be explicitly set to true to trigger the email.

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

        Returns
        -------
        HttpResponse[InvitationResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/invite-user",
            method="POST",
            json={
                "email": email,
                "role_id": role_id,
                "full_name": full_name,
                "send_invitation": send_invitation,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    InvitationResponse,
                    parse_obj_as(
                        type_=InvitationResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get_invitation(
        self,
        organization_id: str,
        *,
        email: typing.Optional[str] = None,
        user_id: typing.Optional[str] = None,
        invitation_id: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[InvitationDetailsResponse]:
        """
        Get pending invitation details including the invitation link.

        Provide ONE of: email, user_id, or invitation_id.

        Requires:
        - User must be authenticated
        - User must be a member of the organization

        Parameters
        ----------
        organization_id : str
            Organization ID

        email : typing.Optional[str]
            Email address to look up

        user_id : typing.Optional[str]
            User ID (usr_...)

        invitation_id : typing.Optional[str]
            Invitation ID (inv_...)

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

        Returns
        -------
        HttpResponse[InvitationDetailsResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/invitation",
            method="GET",
            params={
                "email": email,
                "user_id": user_id,
                "invitation_id": invitation_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    InvitationDetailsResponse,
                    parse_obj_as(
                        type_=InvitationDetailsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def resend_invitation(
        self, organization_id: str, user_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[None]:
        """
        Resend invitation to a single user.

        Accepts either:
        - User ID (usr_...)
        - OrganizationUser ID (orgusr_...)

        Requires:
        - User must be authenticated
        - User must be a member of the organization (any role)
        - Invitation must exist and not be accepted

        Args:
            organization_id: Organization ID
            user_id: User ID (usr_...) or OrganizationUser ID (orgusr_...)
            db: Database session
            auth: Authenticated user

        Raises:
            ResourceNotFoundException: If invitation not found
            ValidationException: If invitation already accepted

        Parameters
        ----------
        organization_id : str
            Organization ID

        user_id : str
            User ID (usr_...) or OrganizationUser ID (orgusr_...) to resend invitation to

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

        Returns
        -------
        HttpResponse[None]
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/users/{jsonable_encoder(user_id)}/resend-invitation",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return HttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def resend_invitations_bulk(
        self,
        organization_id: str,
        *,
        user_ids: typing.Sequence[str],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[BulkOperationResponse]:
        """
        Bulk resend invitations with partial failure handling.

        Requires:
        - User must be authenticated
        - User must be a member of the organization (any role)

        Args:
            organization_id: Organization ID
            request: List of user IDs to resend invitations to
            db: Database session
            auth: Authenticated user

        Returns:
            Bulk operation response with success/failure counts

        Parameters
        ----------
        organization_id : str
            Organization ID

        user_ids : typing.Sequence[str]
            List of user IDs to resend invitations

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

        Returns
        -------
        HttpResponse[BulkOperationResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/users/resend-invitation-bulk",
            method="POST",
            json={
                "user_ids": user_ids,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BulkOperationResponse,
                    parse_obj_as(
                        type_=BulkOperationResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def validate_invitation(
        self, email_key: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Dict[str, typing.Any]]:
        """
        Validate an invitation by email key.

        This endpoint is public (no authentication required) for users
        accepting invitations.

        Args:
            email_key: Unique email key from invitation link
            db: Database session

        Returns:
            Invitation validation details

        Raises:
            ResourceNotFoundException: If invitation not found
            ValidationException: If invitation expired or already used

        Parameters
        ----------
        email_key : str

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

        Returns
        -------
        HttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/invitations/validate/{jsonable_encoder(email_key)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def accept_invitation(
        self, *, email_key: str, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[typing.Dict[str, typing.Any]]:
        """
        Accept an invitation to join an organization.

        User must be authenticated.

        Args:
            email_key: Unique email key from invitation link
            db: Database session
            auth: Authenticated user

        Returns:
            Acceptance confirmation with organization details

        Raises:
            ResourceNotFoundException: If invitation not found
            ValidationException: If invitation expired or already used
            ConflictException: If user already member of organization

        Parameters
        ----------
        email_key : str
            Unique email key from invitation link

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

        Returns
        -------
        HttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/invitations/accept",
            method="POST",
            params={
                "email_key": email_key,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def complete_invitation(
        self, *, email_key: str, full_name: str, password: str, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[CompleteInvitationResponse]:
        """
        Complete invitation by registering user and returning auth tokens.

        This is a public endpoint (no authentication required) that atomically:
        1. Validates the invitation
        2. Updates user with name and password
        3. Marks invitation as used
        4. Activates organization membership
        5. Returns JWT tokens for immediate login

        Args:
            request: Invitation completion request with email_key, full_name, password
            db: Database session

        Returns:
            Auth tokens and user info

        Raises:
            ResourceNotFoundException: If invitation not found
            ValidationException: If invitation expired, already used, or invalid password

        Parameters
        ----------
        email_key : str
            Invitation email key from the URL

        full_name : str
            User's full name

        password : str
            User's password

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

        Returns
        -------
        HttpResponse[CompleteInvitationResponse]
            Successful Response
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/invitations/complete",
            method="POST",
            json={
                "email_key": email_key,
                "full_name": full_name,
                "password": password,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CompleteInvitationResponse,
                    parse_obj_as(
                        type_=CompleteInvitationResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def cancel_invitation(
        self, organization_id: str, invitation_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[None]:
        """
        Cancel a pending invitation.

        Requires:
        - User must be authenticated
        - User must be a member of the organization (any role)

        Args:
            organization_id: Organization ID
            invitation_id: Invitation ID to cancel
            db: Database session
            auth: Authenticated user

        Raises:
            ResourceNotFoundException: If invitation not found
            AuthorizationException: If user lacks permissions

        Parameters
        ----------
        organization_id : str
            Organization ID

        invitation_id : str
            Invitation ID to cancel

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

        Returns
        -------
        HttpResponse[None]
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/invitations/{jsonable_encoder(invitation_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return HttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)


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

    async def invite_user(
        self,
        organization_id: str,
        *,
        email: str,
        role_id: str,
        full_name: typing.Optional[str] = OMIT,
        send_invitation: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[InvitationResponse]:
        """
        Invite a new user to join the organization.

        Requires:
        - User must be authenticated
        - User must be a member of the organization (any role)

        Args:
            organization_id: Organization ID
            request: Invitation request with email, role_id, and send_invitation flag
            db: Database session
            auth: Authenticated user

        Returns:
            Created invitation details

        Raises:
            ConflictException: If user already exists in organization
            ValidationException: If invalid email or role

        Parameters
        ----------
        organization_id : str
            Organization ID

        email : str
            Email address to invite

        role_id : str
            Role ID to assign

        full_name : typing.Optional[str]
            Full name of the invited user. Set on the user record immediately if provided.

        send_invitation : typing.Optional[bool]
            Whether to send invitation email. Must be explicitly set to true to trigger the email.

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

        Returns
        -------
        AsyncHttpResponse[InvitationResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/invite-user",
            method="POST",
            json={
                "email": email,
                "role_id": role_id,
                "full_name": full_name,
                "send_invitation": send_invitation,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    InvitationResponse,
                    parse_obj_as(
                        type_=InvitationResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get_invitation(
        self,
        organization_id: str,
        *,
        email: typing.Optional[str] = None,
        user_id: typing.Optional[str] = None,
        invitation_id: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[InvitationDetailsResponse]:
        """
        Get pending invitation details including the invitation link.

        Provide ONE of: email, user_id, or invitation_id.

        Requires:
        - User must be authenticated
        - User must be a member of the organization

        Parameters
        ----------
        organization_id : str
            Organization ID

        email : typing.Optional[str]
            Email address to look up

        user_id : typing.Optional[str]
            User ID (usr_...)

        invitation_id : typing.Optional[str]
            Invitation ID (inv_...)

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

        Returns
        -------
        AsyncHttpResponse[InvitationDetailsResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/invitation",
            method="GET",
            params={
                "email": email,
                "user_id": user_id,
                "invitation_id": invitation_id,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    InvitationDetailsResponse,
                    parse_obj_as(
                        type_=InvitationDetailsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def resend_invitation(
        self, organization_id: str, user_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[None]:
        """
        Resend invitation to a single user.

        Accepts either:
        - User ID (usr_...)
        - OrganizationUser ID (orgusr_...)

        Requires:
        - User must be authenticated
        - User must be a member of the organization (any role)
        - Invitation must exist and not be accepted

        Args:
            organization_id: Organization ID
            user_id: User ID (usr_...) or OrganizationUser ID (orgusr_...)
            db: Database session
            auth: Authenticated user

        Raises:
            ResourceNotFoundException: If invitation not found
            ValidationException: If invitation already accepted

        Parameters
        ----------
        organization_id : str
            Organization ID

        user_id : str
            User ID (usr_...) or OrganizationUser ID (orgusr_...) to resend invitation to

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

        Returns
        -------
        AsyncHttpResponse[None]
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/users/{jsonable_encoder(user_id)}/resend-invitation",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return AsyncHttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def resend_invitations_bulk(
        self,
        organization_id: str,
        *,
        user_ids: typing.Sequence[str],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[BulkOperationResponse]:
        """
        Bulk resend invitations with partial failure handling.

        Requires:
        - User must be authenticated
        - User must be a member of the organization (any role)

        Args:
            organization_id: Organization ID
            request: List of user IDs to resend invitations to
            db: Database session
            auth: Authenticated user

        Returns:
            Bulk operation response with success/failure counts

        Parameters
        ----------
        organization_id : str
            Organization ID

        user_ids : typing.Sequence[str]
            List of user IDs to resend invitations

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

        Returns
        -------
        AsyncHttpResponse[BulkOperationResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/users/resend-invitation-bulk",
            method="POST",
            json={
                "user_ids": user_ids,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    BulkOperationResponse,
                    parse_obj_as(
                        type_=BulkOperationResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def validate_invitation(
        self, email_key: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Dict[str, typing.Any]]:
        """
        Validate an invitation by email key.

        This endpoint is public (no authentication required) for users
        accepting invitations.

        Args:
            email_key: Unique email key from invitation link
            db: Database session

        Returns:
            Invitation validation details

        Raises:
            ResourceNotFoundException: If invitation not found
            ValidationException: If invitation expired or already used

        Parameters
        ----------
        email_key : str

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

        Returns
        -------
        AsyncHttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/invitations/validate/{jsonable_encoder(email_key)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def accept_invitation(
        self, *, email_key: str, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[typing.Dict[str, typing.Any]]:
        """
        Accept an invitation to join an organization.

        User must be authenticated.

        Args:
            email_key: Unique email key from invitation link
            db: Database session
            auth: Authenticated user

        Returns:
            Acceptance confirmation with organization details

        Raises:
            ResourceNotFoundException: If invitation not found
            ValidationException: If invitation expired or already used
            ConflictException: If user already member of organization

        Parameters
        ----------
        email_key : str
            Unique email key from invitation link

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

        Returns
        -------
        AsyncHttpResponse[typing.Dict[str, typing.Any]]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/invitations/accept",
            method="POST",
            params={
                "email_key": email_key,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    typing.Dict[str, typing.Any],
                    parse_obj_as(
                        type_=typing.Dict[str, typing.Any],  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def complete_invitation(
        self, *, email_key: str, full_name: str, password: str, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[CompleteInvitationResponse]:
        """
        Complete invitation by registering user and returning auth tokens.

        This is a public endpoint (no authentication required) that atomically:
        1. Validates the invitation
        2. Updates user with name and password
        3. Marks invitation as used
        4. Activates organization membership
        5. Returns JWT tokens for immediate login

        Args:
            request: Invitation completion request with email_key, full_name, password
            db: Database session

        Returns:
            Auth tokens and user info

        Raises:
            ResourceNotFoundException: If invitation not found
            ValidationException: If invitation expired, already used, or invalid password

        Parameters
        ----------
        email_key : str
            Invitation email key from the URL

        full_name : str
            User's full name

        password : str
            User's password

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

        Returns
        -------
        AsyncHttpResponse[CompleteInvitationResponse]
            Successful Response
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/invitations/complete",
            method="POST",
            json={
                "email_key": email_key,
                "full_name": full_name,
                "password": password,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    CompleteInvitationResponse,
                    parse_obj_as(
                        type_=CompleteInvitationResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def cancel_invitation(
        self, organization_id: str, invitation_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[None]:
        """
        Cancel a pending invitation.

        Requires:
        - User must be authenticated
        - User must be a member of the organization (any role)

        Args:
            organization_id: Organization ID
            invitation_id: Invitation ID to cancel
            db: Database session
            auth: Authenticated user

        Raises:
            ResourceNotFoundException: If invitation not found
            AuthorizationException: If user lacks permissions

        Parameters
        ----------
        organization_id : str
            Organization ID

        invitation_id : str
            Invitation ID to cancel

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

        Returns
        -------
        AsyncHttpResponse[None]
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v1/organizations/{jsonable_encoder(organization_id)}/invitations/{jsonable_encoder(invitation_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return AsyncHttpResponse(response=_response, data=None)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        parse_obj_as(
                            type_=typing.Any,  # 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)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
