import dataclasses
from typing import Optional, Callable, List, Any, Union, Mapping, Sequence, Literal, Type

import strawberry
from strawberry import relay
from strawberry.annotation import StrawberryAnnotation
from strawberry.extensions import FieldExtension

from lys.core.consts.ai import ToolRiskLevel
from lys.core.consts.errors import NOT_FOUND_ERROR
from lys.core.contexts import Info
from lys.core.entities import Entity
from lys.core.errors import LysError
from lys.core.graphql.fields import create_strawberry_field_config, _apply_webservice_config
from lys.core.graphql.nodes import EntityNode, SuccessNode
from lys.core.utils.access import get_db_object_and_check_access
from lys.core.utils.webservice import WebserviceIsPublicType


def _delete_resolver_generator(resolver: Callable, ensure_type: Type[EntityNode]):
    async def inner_resolver(self, id: relay.GlobalID, info: Info) -> SuccessNode:
        info.context.app_manager = ensure_type.app_manager

        # Use session from context (set by DatabaseSessionExtension)
        # The session is kept open by the extension for the entire GraphQL operation
        session = info.context.session

        entity_obj: Optional[Entity] = await get_db_object_and_check_access(
            id.node_id,
            ensure_type.service_class,
            info.context,
            session=session
        )

        if not entity_obj:
            raise LysError(
                NOT_FOUND_ERROR,
                "_delete_resolver_generator: Unknown entity with type '%s' and id '%s'" % (
                    ensure_type.service_class.entity_class,
                    id.node_id
                )
            )

        # update the object with the resolver
        await resolver(self, obj=entity_obj, info=info)
        await session.delete(entity_obj)

        return SuccessNode(
            succeed=True
        )

    inner_resolver.__name__ = resolver.__name__
    inner_resolver.__qualname__ = resolver.__qualname__
    inner_resolver.__module__ = resolver.__module__

    return inner_resolver


def lys_delete(
        ensure_type: Type[EntityNode],
        is_public: WebserviceIsPublicType = False,
        enabled: bool = True,
        access_levels: List[str] = None,
        is_licenced: bool = True,
        allow_override: bool = False,
        name: Optional[str] = None,
        is_subscription: bool = False,
        description: Optional[str] = None,
        deprecation_reason: Optional[str] = None,
        default: Any = dataclasses.MISSING,
        default_factory: Union[Callable[..., object], object] = dataclasses.MISSING,
        metadata: Optional[Mapping[Any, Any]] = None,
        directives: Optional[Sequence[object]] = (),
        extensions: Optional[List[FieldExtension]] = None,
        graphql_type: Optional[Any] = None,
        init: Literal[True, False, None] = None,
        options: dict = None,
) -> Any:
    """
    Field used to delete a specified database object
    :param ensure_type:
    :param is_public:
    :param enabled:
    :param access_levels:
    :param is_licenced:
    :param name:
    :param is_subscription:
    :param description:
    :param deprecation_reason:
    :param default:
    :param default_factory:
    :param metadata:
    :param directives:
    :param extensions:
    :param graphql_type:
    :param init:
    :return:
    """

    description = (description if description is not None else "") + "\n" + (
        "PUBLIC"
        if is_public
        else (
            f"ACCESS LEVELS: {", ".join([access_level for access_level in access_levels])}"
            if (access_levels is not None and len(access_levels) > 0)
            else "ONLY FOR SUPER USER"
        )
    ) + "\n" + ("UNDER LICENCE" if is_licenced else "LICENCE FREE")

    # Set default risk_level for delete operations
    effective_options = options.copy() if options else {}
    if "risk_level" not in effective_options:
        effective_options["risk_level"] = ToolRiskLevel.DELETE

    effective_ensure_type = ensure_type.get_effective_node()

    def wrapper(resolver: Callable):
        wrapped_resolver = _delete_resolver_generator(resolver, effective_ensure_type)

        field_config = create_strawberry_field_config(
            resolver=wrapped_resolver,
            name=name,
            is_subscription=is_subscription,
            description=description,
            is_public=is_public,
            access_levels=access_levels,
            is_licenced=is_licenced,
            deprecation_reason=deprecation_reason,
            default=default,
            default_factory=default_factory,
            metadata=metadata,
            directives=directives,
            extensions=extensions,
            graphql_type=graphql_type,
            init=init,
        )

        field = strawberry.field(**field_config)
        field.base_resolver.type_annotation = StrawberryAnnotation(SuccessNode)

        return _apply_webservice_config(
            field, is_public, enabled, access_levels, is_licenced,
            allow_override, description, None, effective_options,
        )

    return wrapper