# !!!
# WARNING: This file is autogenerated
# Only modify code within MANUAL() sections
# or your changes may be overwritten later!
# !!!

from __future__ import annotations

from typing import Any, Dict, Set, Union

from stytch.b2b.models.rbac import (
    OrgPolicy,
    Policy as B2BPolicy,
    PolicyResource,
)
from stytch.b2b.models.rbac_organizations import (
    GetOrgPolicyResponse,
    SetOrgPolicyResponse,
)
from stytch.core.api_base import ApiBase
from stytch.core.http.client import AsyncClient, SyncClient


class Organizations:
    def __init__(
        self, api_base: ApiBase, sync_client: SyncClient, async_client: AsyncClient
    ) -> None:
        self.api_base = api_base
        self.sync_client = sync_client
        self.async_client = async_client

    def get_org_policy(
        self,
        organization_id: str,
    ) -> GetOrgPolicyResponse:
        """Get the active RBAC Policy for a specific Organization within your Stytch Project. An Organization RBAC Policy contains the roles that have been defined specifically for that organization, allowing for organization-specific permissioning models.

        This endpoint returns the organization-scoped roles that supplement the project-level RBAC policy. Organization policies allow you to define custom roles that are specific to individual organizations within your project.

        When using the backend SDKs, the RBAC Policy will be cached to allow for local evaluations, eliminating the need for an extra request to Stytch. The policy will be refreshed if an authorization check is requested and the RBAC policy was last updated more than 5 minutes ago.

        Organization-specific roles can be created and managed through this API endpoint, providing fine-grained control over permissions at the organization level.

        Check out the [RBAC overview](https://stytch.com/docs/b2b/guides/rbac/overview) to learn more about Stytch's RBAC permissioning model and organization-scoped policies.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "organization_id": organization_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/rbac/organizations/{organization_id}", data
        )
        res = self.sync_client.get(url, data, headers)
        return GetOrgPolicyResponse.from_json(res.response.status_code, res.json)

    async def get_org_policy_async(
        self,
        organization_id: str,
    ) -> GetOrgPolicyResponse:
        """Get the active RBAC Policy for a specific Organization within your Stytch Project. An Organization RBAC Policy contains the roles that have been defined specifically for that organization, allowing for organization-specific permissioning models.

        This endpoint returns the organization-scoped roles that supplement the project-level RBAC policy. Organization policies allow you to define custom roles that are specific to individual organizations within your project.

        When using the backend SDKs, the RBAC Policy will be cached to allow for local evaluations, eliminating the need for an extra request to Stytch. The policy will be refreshed if an authorization check is requested and the RBAC policy was last updated more than 5 minutes ago.

        Organization-specific roles can be created and managed through this API endpoint, providing fine-grained control over permissions at the organization level.

        Check out the [RBAC overview](https://stytch.com/docs/b2b/guides/rbac/overview) to learn more about Stytch's RBAC permissioning model and organization-scoped policies.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "organization_id": organization_id,
        }

        url = self.api_base.url_for(
            "/v1/b2b/rbac/organizations/{organization_id}", data
        )
        res = await self.async_client.get(url, data, headers)
        return GetOrgPolicyResponse.from_json(res.response.status, res.json)

    def set_org_policy(
        self,
        organization_id: str,
        org_policy: Union[OrgPolicy, Dict[str, Any]],
    ) -> SetOrgPolicyResponse:
        """Set the RBAC Policy for a specific Organization within your Stytch Project. An Organization RBAC Policy allows you to define roles that are specific to that organization, providing fine-grained control over permissions at the organization level.

        This endpoint allows you to create, update, or replace the organization-scoped roles for a given organization. Organization policies supplement the project-level RBAC policy with additional roles that are only applicable within the context of that specific organization.

        The organization policy consists of roles, where each role defines:
        - A unique `role_id` to identify the role
        - A human-readable `description` of the role's purpose
        - A set of `permissions` that specify which actions can be performed on which resources

        When you set an organization policy, it will replace any existing organization-specific roles for that organization. The project-level RBAC policy remains unchanged.

        Organization-specific roles are useful for scenarios where different organizations within your project require different permission structures, such as:
        - Multi-tenant applications with varying access levels per tenant
        - Organizations with custom approval workflows
        - Different organizational hierarchies requiring unique role definitions

        Check out the [RBAC overview](https://stytch.com/docs/b2b/guides/rbac/overview) to learn more about Stytch's RBAC permissioning model and organization-scoped policies.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - org_policy: The organization-specific RBAC Policy that contains roles defined for this organization. Organization policies supplement the project-level RBAC policy with additional roles that are specific to the organization.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "org_policy": (
                org_policy if isinstance(org_policy, dict) else org_policy.dict()
            ),
        }

        url = self.api_base.url_for(
            "/v1/b2b/rbac/organizations/{organization_id}", data
        )
        res = self.sync_client.put(url, data, headers)
        return SetOrgPolicyResponse.from_json(res.response.status_code, res.json)

    async def set_org_policy_async(
        self,
        organization_id: str,
        org_policy: OrgPolicy,
    ) -> SetOrgPolicyResponse:
        """Set the RBAC Policy for a specific Organization within your Stytch Project. An Organization RBAC Policy allows you to define roles that are specific to that organization, providing fine-grained control over permissions at the organization level.

        This endpoint allows you to create, update, or replace the organization-scoped roles for a given organization. Organization policies supplement the project-level RBAC policy with additional roles that are only applicable within the context of that specific organization.

        The organization policy consists of roles, where each role defines:
        - A unique `role_id` to identify the role
        - A human-readable `description` of the role's purpose
        - A set of `permissions` that specify which actions can be performed on which resources

        When you set an organization policy, it will replace any existing organization-specific roles for that organization. The project-level RBAC policy remains unchanged.

        Organization-specific roles are useful for scenarios where different organizations within your project require different permission structures, such as:
        - Multi-tenant applications with varying access levels per tenant
        - Organizations with custom approval workflows
        - Different organizational hierarchies requiring unique role definitions

        Check out the [RBAC overview](https://stytch.com/docs/b2b/guides/rbac/overview) to learn more about Stytch's RBAC permissioning model and organization-scoped policies.

        Fields:
          - organization_id: Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
          - org_policy: The organization-specific RBAC Policy that contains roles defined for this organization. Organization policies supplement the project-level RBAC policy with additional roles that are specific to the organization.
        """  # noqa
        headers: Dict[str, str] = {}
        data: Dict[str, Any] = {
            "organization_id": organization_id,
            "org_policy": (
                org_policy if isinstance(org_policy, dict) else org_policy.dict()
            ),
        }

        url = self.api_base.url_for(
            "/v1/b2b/rbac/organizations/{organization_id}", data
        )
        res = await self.async_client.put(url, data, headers)
        return SetOrgPolicyResponse.from_json(res.response.status, res.json)

    # MANUAL(validate_org_policy)(SERVICE_METHOD)
    # ADDIMPORT: from stytch.b2b.models.rbac import Policy as B2BPolicy
    # ADDIMPORT: from typing import Set
    @staticmethod
    def validate_org_policy(project_policy: B2BPolicy, org_policy: OrgPolicy) -> None:
        project_roles = set({r.role_id for r in project_policy.roles})
        project_resources = {r.resource_id: r for r in project_policy.resources}

        org_roles: Set[str] = set()
        for role in org_policy.roles:
            org_role_id = role.role_id
            if org_role_id in org_roles:
                raise Exception(
                    f"Duplicate role {org_role_id} in Organization RBAC policy"
                )
            org_roles.add(org_role_id)

            if org_role_id in project_roles:
                raise Exception(
                    f"Role {org_role_id} already defined in Project RBAC policy"
                )

            for permission in role.permissions:
                resource_id = permission.resource_id
                if not resource_id in project_resources:
                    raise Exception(
                        f"Resource {resource_id} not defined in Project RBAC policy"
                    )

                if len(permission.actions) == 0:
                    raise Exception(
                        f"No actions defined for role {org_role_id}, resource {resource_id}"
                    )
                if len(permission.actions) == 1 and "*" == permission.actions[0]:
                    continue
                if len(permission.actions) > 1 and "*" in permission.actions:
                    raise Exception(
                        "Wildcard actions must be the only action defined for a role and resource"
                    )

                project_resource = project_resources[resource_id]
                for action in permission.actions:
                    if action.strip() == "":
                        raise Exception(
                            f"Empty action on resource {resource_id} is not permitted"
                        )

                    if not action in project_resource.actions:
                        raise Exception(
                            f"Unknown action {action} defined on resource {resource_id}"
                        )

        return

    # ENDMANUAL(validate_org_policy)
