"""
This module provides the CleverBRAG AMQP Adapter for handling API requests via AMQP.
The code IS MEANT to be part of CleverBRAG codebase, and has direct dependencies on the CleverBRAG codebase.
It is NOT MEANT to be used as a standalone module. It WILL NOT run outside the CleverBRAG context.
"""

from threading import Thread
from typing import Any, List
from uuid import UUID

# CleverBrag specific imports
import core
from fastapi.routing import APIRoute
from fastapi.security.http import HTTPAuthorizationCredentials
from pydantic import BaseModel
from shared.abstractions import R2RSerializable, User

# AMQP specific imports
from amqp.adapter.cleverthis_service_adapter import CleverThisServiceAdapter
from amqp.config.amq_configuration import AMQConfiguration
from amqp.service.amq_service import AMQService


class BRAGArgument:
    """
    This class is used to represent a BRAG API input argument. It is uses FastAPI annotation to define the arguments
    (the argument type) that are passed as input arguments to the BRAG endpoint.
    """

    def __init__(self, arg: Any):
        self.arg = arg
        self.module = arg.__module__
        self.cls = arg.__name__ if hasattr(arg, "__name__") else str(arg)
        self.is_r2r_serializable = issubclass(arg, R2RSerializable)
        self.is_builtin = self.module == "builtins"
        self.is_uuid = self.cls == "UUID"
        self.is_optional = self.cls == "Optional"
        self.is_list = self.cls == "list"
        self.is_dict = self.cls == "dict"
        self.is_base_model = issubclass(arg, BaseModel)
        self.is_nested = self.is_list or self.is_optional or self.is_dict
        if self.is_nested and hasattr(arg, "__args__"):
            self.nested = BRAGArgument(getattr(arg, "__args__")[0])
            if self.is_dict:
                self.nested = (self.nested, BRAGArgument(getattr(arg, "__args__")[1]))
        else:
            self.nested = None
            self.is_nested = False

    def __repr__(self):
        return f"{self.module}.{self.cls}" + (f"[{self.nested}]" if self.is_nested else "")


def convert_to_argument(arg: BRAGArgument, value: str) -> Any:
    """
    Converts the argument to a string representation.
    """
    try:
        if arg.cls == "str":
            return value
        elif arg.cls == "bool":
            return str(value).lower() == "true"
        elif arg.cls == "float":
            return float(value)
        elif arg.cls == "int":
            return int(value)
        elif arg.is_uuid:
            return UUID(value)
        elif arg.is_optional:
            return convert_to_argument(arg.nested, value) if arg.nested else None
        elif arg.is_list:
            return (
                [convert_to_argument(arg.nested, v.strip()) for v in value.split(",")]
                if arg.nested
                else value.split()
            )
        elif arg.is_dict:
            return (
                {
                    convert_to_argument(arg.nested[0], k.strip()): convert_to_argument(
                        arg.nested[1], v.strip()
                    )
                    for k, v in (item.split(":") for item in value.split(","))
                }
                if arg.nested
                else dict(item.split(":") for item in value.split(","))
            )
        elif arg.is_base_model:
            return arg.arg.parse_raw(value)
        else:
            print(f"Unsupported argument type: {type(arg)}")
    except Exception as e:
        print(f"Error converting argument {arg}: {e}")
    return None


class CleverBragAmqpAdapter(CleverThisServiceAdapter):
    """
    CleverBRAG AMQP Adapter for CleverBRAG API. Implements the interface to CleverBRAG API.
    """

    def __init__(
        self,
        amq_configuration: AMQConfiguration,
        routes: List[APIRoute],
        auth_provider: core.base.providers.auth.AuthProvider,
        extra_init_data: dict,
    ):
        super().__init__(
            amq_configuration, routes, None, "auth_user", extra_init_data=extra_init_data
        )
        self.auth_provider = auth_provider
        _amq_service: AMQService = AMQService(amq_configuration, self)
        self.thread = Thread(target=_amq_service.run)
        self.thread.start()

    async def get_current_user(self, token: str) -> User:
        """
        Overrides the default get_current_user method to use the token from the AMQP message.
        :param token: JWT token from the AMQP message
        :return: User object
        """
        return await self.auth_provider.auth_wrapper(
            HTTPAuthorizationCredentials(scheme="Bearer", credentials=token)
        )
