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

import typing
from ...core.client_wrapper import SyncClientWrapper
from ...core.request_options import RequestOptions
from ...types.paginated_lse_organization_member_list_list import PaginatedLseOrganizationMemberListList
from ...core.jsonable_encoder import jsonable_encoder
from ...core.unchecked_base_model import construct_type
from json.decoder import JSONDecodeError
from ...core.api_error import ApiError
from ...types.role9e7enum import Role9E7Enum
from ...types.lse_organization_member_list import LseOrganizationMemberList
from ...types.organization_member import OrganizationMember
from ...errors.forbidden_error import ForbiddenError
from ...errors.not_found_error import NotFoundError
from ...errors.method_not_allowed_error import MethodNotAllowedError
from ...core.client_wrapper import AsyncClientWrapper

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


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

    def list(
        self,
        id: int,
        *,
        exclude_project_id: typing.Optional[int] = None,
        exclude_workspace_id: typing.Optional[int] = None,
        ordering: typing.Optional[str] = None,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        role: typing.Optional[str] = None,
        search: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> PaginatedLseOrganizationMemberListList:
        """
        <Card href="https://humansignal.com/goenterprise">
                <img style="pointer-events: none; margin-left: 0px; margin-right: 0px;" src="https://docs.humansignal.com/images/badge.svg" alt="Label Studio Enterprise badge"/>
                <p style="margin-top: 10px; font-size: 14px;">
                    This endpoint is not available in Label Studio Community Edition. [Learn more about Label Studio Enterprise](https://humansignal.com/goenterprise)
                </p>
            </Card>
        Retrieve a list of all users and roles in a specific organization.

        Parameters
        ----------
        id : int
            A unique integer value identifying this organization.

        exclude_project_id : typing.Optional[int]
            Project ID to exclude users who are already associated with this project (direct members, workspace members, or implicit admin/owner access).

        exclude_workspace_id : typing.Optional[int]
            Workspace ID to exclude users who are already associated with this workspace (direct workspace members or implicit admin/owner access).

        ordering : typing.Optional[str]
            Which field to use when ordering the results.

        page : typing.Optional[int]
            A page number within the paginated result set.

        page_size : typing.Optional[int]
            Number of results to return per page.

        role : typing.Optional[str]
            Filter members by organization role. Accepts single role or comma-separated list of roles.

            **Format:**
            - Single role: `?role=RE`
            - Multiple roles: `?role=AN,RE` (users with ANY of these roles)

            **Role Codes:**
            - `OW` = Owner
            - `AD` = Administrator
            - `MA` = Manager
            - `RE` = Reviewer
            - `AN` = Annotator
            - `NO` = Not Activated
            - `DI` = Disabled

        search : typing.Optional[str]
            A search term.

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

        Returns
        -------
        PaginatedLseOrganizationMemberListList


        Examples
        --------
        from label_studio_sdk import LabelStudio

        client = LabelStudio(
            api_key="YOUR_API_KEY",
        )
        client.organizations.members.list(
            id=1,
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"api/organizations/{jsonable_encoder(id)}/memberships",
            method="GET",
            params={
                "exclude_project_id": exclude_project_id,
                "exclude_workspace_id": exclude_workspace_id,
                "ordering": ordering,
                "page": page,
                "page_size": page_size,
                "role": role,
                "search": search,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    PaginatedLseOrganizationMemberListList,
                    construct_type(
                        type_=PaginatedLseOrganizationMemberListList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            _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 update(
        self,
        id: int,
        *,
        role: typing.Optional[Role9E7Enum] = OMIT,
        user_id: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> LseOrganizationMemberList:
        """
        <Card href="https://humansignal.com/goenterprise">
                <img style="pointer-events: none; margin-left: 0px; margin-right: 0px;" src="https://docs.humansignal.com/images/badge.svg" alt="Label Studio Enterprise badge"/>
                <p style="margin-top: 10px; font-size: 14px;">
                    This endpoint is not available in Label Studio Community Edition. [Learn more about Label Studio Enterprise](https://humansignal.com/goenterprise)
                </p>
            </Card>
        Update organization membership or role for a specific user ID.

        **User Rotation Best Practices for API Usage**

        To maintain compliance with our licensing terms and ensure optimal performance of HumanSignal's APIs, please consider the following guidelines when managing user assignments:

        * **Maintain a 7-Day Minimum Assignment**: Once a licensed seat is assigned to a user, maintain that assignment for at least seven consecutive days before rotating it to another user.

        * **Automate, Monitor, and Log Rotations**: Implement automated scheduling and logging mechanisms to track the timing of user rotations. This helps ensure that rotations adhere to the seven-day minimum period.

        * **Adhere to API Update Frequency and Wait Periods**: When updating user assignments via our APIs, follow the recommended frequency and wait period guidelines provided in the HumanSignal API documentation. Avoid sending rapid, successive requests that might overload the endpoint. Instead, incorporate appropriate delays between calls as specified in the documentation.

        * **Avoid Overloading the API Endpoint**: Design your integration to batch or schedule updates where possible, and implement backoff strategies if the API indicates rate limiting. This helps prevent service disruptions and ensures a smooth operation.

        Parameters
        ----------
        id : int
            A unique integer value identifying this organization.

        role : typing.Optional[Role9E7Enum]

        user_id : typing.Optional[int]

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

        Returns
        -------
        LseOrganizationMemberList


        Examples
        --------
        from label_studio_sdk import LabelStudio

        client = LabelStudio(
            api_key="YOUR_API_KEY",
        )
        client.organizations.members.update(
            id=1,
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"api/organizations/{jsonable_encoder(id)}/memberships",
            method="PATCH",
            json={
                "role": role,
                "user_id": user_id,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    LseOrganizationMemberList,
                    construct_type(
                        type_=LseOrganizationMemberList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            _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, id: int, user_pk: int, *, request_options: typing.Optional[RequestOptions] = None
    ) -> OrganizationMember:
        """
        Get organization member details by user ID.

        Parameters
        ----------
        id : int

        user_pk : int
            A unique integer value identifying the user to get organization details for.

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

        Returns
        -------
        OrganizationMember


        Examples
        --------
        from label_studio_sdk import LabelStudio

        client = LabelStudio(
            api_key="YOUR_API_KEY",
        )
        client.organizations.members.get(
            id=1,
            user_pk=1,
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"api/organizations/{jsonable_encoder(id)}/memberships/{jsonable_encoder(user_pk)}/",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    OrganizationMember,
                    construct_type(
                        type_=OrganizationMember,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            _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 delete(self, id: int, user_pk: int, *, request_options: typing.Optional[RequestOptions] = None) -> None:
        """
        Soft delete a member from the organization.

        Parameters
        ----------
        id : int

        user_pk : int
            A unique integer value identifying the user to be deleted from the organization.

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

        Returns
        -------
        None

        Examples
        --------
        from label_studio_sdk import LabelStudio

        client = LabelStudio(
            api_key="YOUR_API_KEY",
        )
        client.organizations.members.delete(
            id=1,
            user_pk=1,
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"api/organizations/{jsonable_encoder(id)}/memberships/{jsonable_encoder(user_pk)}/",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        construct_type(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        construct_type(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 405:
                raise MethodNotAllowedError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        construct_type(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _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 AsyncMembersClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def list(
        self,
        id: int,
        *,
        exclude_project_id: typing.Optional[int] = None,
        exclude_workspace_id: typing.Optional[int] = None,
        ordering: typing.Optional[str] = None,
        page: typing.Optional[int] = None,
        page_size: typing.Optional[int] = None,
        role: typing.Optional[str] = None,
        search: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> PaginatedLseOrganizationMemberListList:
        """
        <Card href="https://humansignal.com/goenterprise">
                <img style="pointer-events: none; margin-left: 0px; margin-right: 0px;" src="https://docs.humansignal.com/images/badge.svg" alt="Label Studio Enterprise badge"/>
                <p style="margin-top: 10px; font-size: 14px;">
                    This endpoint is not available in Label Studio Community Edition. [Learn more about Label Studio Enterprise](https://humansignal.com/goenterprise)
                </p>
            </Card>
        Retrieve a list of all users and roles in a specific organization.

        Parameters
        ----------
        id : int
            A unique integer value identifying this organization.

        exclude_project_id : typing.Optional[int]
            Project ID to exclude users who are already associated with this project (direct members, workspace members, or implicit admin/owner access).

        exclude_workspace_id : typing.Optional[int]
            Workspace ID to exclude users who are already associated with this workspace (direct workspace members or implicit admin/owner access).

        ordering : typing.Optional[str]
            Which field to use when ordering the results.

        page : typing.Optional[int]
            A page number within the paginated result set.

        page_size : typing.Optional[int]
            Number of results to return per page.

        role : typing.Optional[str]
            Filter members by organization role. Accepts single role or comma-separated list of roles.

            **Format:**
            - Single role: `?role=RE`
            - Multiple roles: `?role=AN,RE` (users with ANY of these roles)

            **Role Codes:**
            - `OW` = Owner
            - `AD` = Administrator
            - `MA` = Manager
            - `RE` = Reviewer
            - `AN` = Annotator
            - `NO` = Not Activated
            - `DI` = Disabled

        search : typing.Optional[str]
            A search term.

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

        Returns
        -------
        PaginatedLseOrganizationMemberListList


        Examples
        --------
        import asyncio

        from label_studio_sdk import AsyncLabelStudio

        client = AsyncLabelStudio(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.organizations.members.list(
                id=1,
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"api/organizations/{jsonable_encoder(id)}/memberships",
            method="GET",
            params={
                "exclude_project_id": exclude_project_id,
                "exclude_workspace_id": exclude_workspace_id,
                "ordering": ordering,
                "page": page,
                "page_size": page_size,
                "role": role,
                "search": search,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    PaginatedLseOrganizationMemberListList,
                    construct_type(
                        type_=PaginatedLseOrganizationMemberListList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            _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 update(
        self,
        id: int,
        *,
        role: typing.Optional[Role9E7Enum] = OMIT,
        user_id: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> LseOrganizationMemberList:
        """
        <Card href="https://humansignal.com/goenterprise">
                <img style="pointer-events: none; margin-left: 0px; margin-right: 0px;" src="https://docs.humansignal.com/images/badge.svg" alt="Label Studio Enterprise badge"/>
                <p style="margin-top: 10px; font-size: 14px;">
                    This endpoint is not available in Label Studio Community Edition. [Learn more about Label Studio Enterprise](https://humansignal.com/goenterprise)
                </p>
            </Card>
        Update organization membership or role for a specific user ID.

        **User Rotation Best Practices for API Usage**

        To maintain compliance with our licensing terms and ensure optimal performance of HumanSignal's APIs, please consider the following guidelines when managing user assignments:

        * **Maintain a 7-Day Minimum Assignment**: Once a licensed seat is assigned to a user, maintain that assignment for at least seven consecutive days before rotating it to another user.

        * **Automate, Monitor, and Log Rotations**: Implement automated scheduling and logging mechanisms to track the timing of user rotations. This helps ensure that rotations adhere to the seven-day minimum period.

        * **Adhere to API Update Frequency and Wait Periods**: When updating user assignments via our APIs, follow the recommended frequency and wait period guidelines provided in the HumanSignal API documentation. Avoid sending rapid, successive requests that might overload the endpoint. Instead, incorporate appropriate delays between calls as specified in the documentation.

        * **Avoid Overloading the API Endpoint**: Design your integration to batch or schedule updates where possible, and implement backoff strategies if the API indicates rate limiting. This helps prevent service disruptions and ensures a smooth operation.

        Parameters
        ----------
        id : int
            A unique integer value identifying this organization.

        role : typing.Optional[Role9E7Enum]

        user_id : typing.Optional[int]

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

        Returns
        -------
        LseOrganizationMemberList


        Examples
        --------
        import asyncio

        from label_studio_sdk import AsyncLabelStudio

        client = AsyncLabelStudio(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.organizations.members.update(
                id=1,
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"api/organizations/{jsonable_encoder(id)}/memberships",
            method="PATCH",
            json={
                "role": role,
                "user_id": user_id,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    LseOrganizationMemberList,
                    construct_type(
                        type_=LseOrganizationMemberList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            _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, id: int, user_pk: int, *, request_options: typing.Optional[RequestOptions] = None
    ) -> OrganizationMember:
        """
        Get organization member details by user ID.

        Parameters
        ----------
        id : int

        user_pk : int
            A unique integer value identifying the user to get organization details for.

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

        Returns
        -------
        OrganizationMember


        Examples
        --------
        import asyncio

        from label_studio_sdk import AsyncLabelStudio

        client = AsyncLabelStudio(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.organizations.members.get(
                id=1,
                user_pk=1,
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"api/organizations/{jsonable_encoder(id)}/memberships/{jsonable_encoder(user_pk)}/",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    OrganizationMember,
                    construct_type(
                        type_=OrganizationMember,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            _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 delete(self, id: int, user_pk: int, *, request_options: typing.Optional[RequestOptions] = None) -> None:
        """
        Soft delete a member from the organization.

        Parameters
        ----------
        id : int

        user_pk : int
            A unique integer value identifying the user to be deleted from the organization.

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

        Returns
        -------
        None

        Examples
        --------
        import asyncio

        from label_studio_sdk import AsyncLabelStudio

        client = AsyncLabelStudio(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.organizations.members.delete(
                id=1,
                user_pk=1,
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"api/organizations/{jsonable_encoder(id)}/memberships/{jsonable_encoder(user_pk)}/",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        construct_type(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        construct_type(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 405:
                raise MethodNotAllowedError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        construct_type(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _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)
