import functools
from typing import Callable, TypedDict

from edpauthorization.registry import (
    add_request_context,
    append_resource_attribute,
    set_resource_attribute,
)


class AuthorizationResource(TypedDict):
    field_name: str
    resource_type: str
    resource_field: str


class AuthorizationContext(TypedDict):
    field_name: str
    context_field: str


_DECORATOR_METHOD = "__new__"


def _common_decorator(handler: Callable) -> Callable:
    def check_method(request_cls):
        _original_method = getattr(request_cls, _DECORATOR_METHOD)
        _is_original_call = not hasattr(request_cls, "__original_callable__")

        if _is_original_call:
            setattr(request_cls, "__original_callable__", _original_method)

        @functools.wraps(_original_method)
        def new_method(self, *args, **kwargs):
            used_kwargs = kwargs or {}
            handler(**used_kwargs)
            if _is_original_call:
                return _original_method(self)
            else:
                return _original_method(self, *args, **kwargs)

        setattr(request_cls, _DECORATOR_METHOD, new_method)
        return request_cls

    return check_method


def authorization_contexts(contexts: list[AuthorizationContext]):
    def handler(**kwargs):
        for context in contexts:
            add_request_context(
                context_field=context["context_field"],
                context_value=kwargs.get(context["field_name"]),
            )

    return _common_decorator(handler)


def authorization_context_values(field_name: str, context_field: str):
    def handler(**kwargs):
        if kwargs.get(field_name) is not None:
            add_request_context(
                context_field=context_field,
                context_value=[kwargs.get(field_name)],
            )

    return _common_decorator(handler)


def authorization_context(field_name: str, context_field: str):
    return authorization_contexts(
        contexts=[
            {
                "field_name": field_name,
                "context_field": context_field,
            }
        ]
    )


def authorization_resources(resources: list[AuthorizationResource]):
    def handler(**kwargs):
        for resource in resources:
            set_resource_attribute(
                resource_type=resource["resource_type"],
                resource_field=resource["resource_field"],
                resource_value=kwargs.get(resource["field_name"]),
            )

    return _common_decorator(handler)


def authorization_resource(resource: AuthorizationResource):
    return authorization_resources([resource])


def authorization_resource_ids(field_name: str, resource_type: str):
    def handler(**kwargs):
        append_resource_attribute(
            resource_type=resource_type,
            resource_field="ids",
            resource_value=kwargs.get(field_name),
        )

    return _common_decorator(handler)


def authorization_resource_id(field_name: str, resource_type: str):
    return authorization_resource(
        {
            "field_name": field_name,
            "resource_type": resource_type,
            "resource_field": "id",
        }
    )
