import logging
from typing import Any, List

import boto3

from edpauthorization.dto import AuthorizationRequest
from edpauthorization.service.impl.cedar import BaseCedarAuthorizationService
from edpauthorization.service.impl.cedar.dto import CedarUidEntity

_DEFAULT_LOGGER = logging.getLogger(
    "edpauthorization.service.impl.cedar.cedarapi.VerifiedPermissionsAuthorizationServiceImpl"
)

# To Avoid Unnecessary batch call. It cost more
_MINIMAL_BATCH_REQUESTS = 30


class VerifiedPermissionsAuthorizationServiceImpl(BaseCedarAuthorizationService):
    def __init__(
        self,
        policy_store_id: str,
        schema_namespace: str | None = None,
        use_batch: bool = False,
    ):
        super().__init__(schema_namespace=schema_namespace)
        self.policy_store_id = policy_store_id
        self.use_batch = use_batch

        self._client = None

    def _get_client(self):
        if self._client is None:
            self._client = boto3.client("verifiedpermissions")

        return self._client

    def _call_is_authorized(self, request: dict, entities: list[CedarUidEntity]) -> bool:
        response = self._get_client().is_authorized(
            policyStoreId=self.policy_store_id,
            principal=request["principal"],
            action=request["action"],
            resource=request["resource"],
            context=self.get_cedar_json(request["context"]),
            entities=self.get_cedar_json(entities),
        )

        allow = response.get("decision") == "ALLOW"
        if not allow:
            _DEFAULT_LOGGER.info("is_authorized response: %s", response)

        return allow

    def _call_is_authorized_batch(self, requests: List[dict], entities: list) -> bool:
        if self.use_batch and len(requests) > _MINIMAL_BATCH_REQUESTS:
            authz_requests: list = [
                {
                    "principal": authz_request["principal"],
                    "action": authz_request["action"],
                    "resource": authz_request["resource"],
                    "context": self.get_cedar_json(authz_request["context"]),
                }
                for authz_request in requests
            ]

            response = self._get_client().batch_is_authorized(
                policyStoreId=self.policy_store_id,
                requests=authz_requests,
                entities=self.get_cedar_json(entities),
            )

            if "results" not in response:
                _DEFAULT_LOGGER.info("is_authorized_batch response: %s", response)

                return False

            for result in response["results"]:
                allow = result.get("decision") == "ALLOW"
                if not allow:
                    _DEFAULT_LOGGER.info("is_authorized response: %s", response)
                    return False

            return True

        for request in requests:
            if not self._call_is_authorized(request, entities):
                return False

        return True

    def get_cedar_json(self, payload: Any) -> dict:
        return {
            "cedarJson": self.get_encoded_json(payload),
        }

    def get_entity_ref(self, entity: CedarUidEntity) -> dict:
        return {
            "entityType": entity["uid"]["type"],
            "entityId": entity["uid"]["id"],
        }

    def build_action_ref(self, request: AuthorizationRequest):
        return {
            "actionType": "::".join([self.schema_namespace, "Action"]),
            "actionId": request["action_name"],
        }
