import re
from json import JSONEncoder
from typing import Any

from edpauthorization.dto import AuthorizationPrincipal, AuthorizationRequest
from ..dto import CedarEntity, CedarUidEntity


def build_cedar_type_ref(*args) -> str:
    return "::".join([arg for arg in args if arg is not None])


def build_cedar_uid_ref(*args) -> str:
    values = args
    if len(values) > 1:
        values = args[0:-1]
        last_value = args[-1]
        values = [*values, f'"{last_value}"']
    return "::".join(values)


def get_entity_ref(entity: CedarUidEntity) -> str:
    return build_cedar_uid_ref(entity["uid"]["type"], entity["uid"]["id"])


def _get_id(value: Any) -> str:
    value = str(value)
    value = re.sub(r"[^a-zA-Z0-9\s]", "-", value)
    return value


def build_principal_entity(schema_namespace: str, principal: AuthorizationPrincipal) -> CedarUidEntity:
    return CedarUidEntity(
        uid=CedarEntity(
            type=build_cedar_type_ref(schema_namespace, principal["type"]),
            id=_get_id(principal["id"]),
        ),
        attrs=_build_attributes(principal["attributes"]),
        parents=[],
    )


def _build_attributes(value: dict) -> dict:
    return {k: v for k, v in value.items() if v is not None}


def build_resource_entity(schema_namespace: str, request: AuthorizationRequest) -> CedarUidEntity:
    resource_type = request["resource_type"]
    resource = request["resource"]

    resource_id = None
    if isinstance(resource, dict):
        resource_id = resource.get("id")

    if resource_id is None:
        resource_id = "request"

    return CedarUidEntity(
        uid=CedarEntity(
            type=build_cedar_type_ref(schema_namespace, resource_type),
            id=_get_id(resource_id),
        ),
        attrs=_build_attributes(resource),
        parents=[],
    )


def build_action_ref(schema_namespace: str, action_name: str) -> str:
    return build_cedar_uid_ref(schema_namespace, "Action", action_name)


def build_correlation_id(batch_id: str, request: AuthorizationRequest) -> str:
    action_name = request["action_name"]
    return f"authz_req::{batch_id}-{action_name}"


class CedarJSONEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, set):
            return list(obj)
        return JSONEncoder.default(self, obj)
