from pydantic import ValidationError
from semantic_router import Route, RouteLayer
from semantic_router.encoders import OpenAIEncoder

from arcan.ai.router.routes import *


class RouteFactory:
    @staticmethod
    def create_route(name: str, utterances: List[str]) -> Route:
        """
        Factory method to create Route instances.
        """
        return Route(name=name, utterances=utterances)


class RouteManager:
    def __init__(self, encoder: OpenAIEncoder):
        """
        Manages routes and their responses.
        """
        self.encoder = encoder
        self.routes = []
        self.response_strategies = {}

    def add_route(
        self,
        name: str,
        utterances: List[str],
        response_strategy: Type[RouteResponseStrategy],
    ):
        """
        Adds a single route and its response strategy to the manager.
        """
        route = RouteFactory.create_route(name, utterances)
        self.routes.append(route)
        self.response_strategies[name] = response_strategy()

    def add_routes_from_config(self, config: List[RouteConfig]):
        """
        Adds multiple routes based on a list of RouteConfig objects.
        """
        for route_config in config:
            try:
                # validated_config = RouteConfig(**route_config)
                self.add_route(
                    route_config.name,
                    route_config.utterances,
                    route_config.strategy,
                )
            except ValidationError as e:
                print(f"Error validating route configuration: {e}")

    def get_response(self, query: str, user_id: str) -> str:
        """
        Processes the query through the RouteLayer, executing the strategy of the matched route.
        """
        rl = RouteLayer(encoder=self.encoder, routes=self.routes)
        route = rl(query)
        strategy = self.response_strategies.get(route.name)
        if strategy:
            print(
                f"Matched route: {route.name}, using strategy: {strategy.__class__.__name__}"
            )
            route_text = strategy.execute(query=query, user_id=user_id)
            route_template = f" (SYSTEM NOTE: {route_text})"
            query += route_template
            return route_text, query
        else:
            print(f"No route found for query: {query}")
        return "No Router Matched", query


# Initialize RouteManager with an encoder
encoder = OpenAIEncoder()
route_manager = RouteManager(encoder)

# Dynamically adding routes to the RouteManager using the configuration (from routes.py)
route_manager.add_routes_from_config(ROUTES_CONFIGURATIONS)


# Function to process queries using the semantic layer
def semantic_layer(query: str, user_id: str = None):
    """
    This function processes the given query and returns a response.

    Args:
        query (str): The query to be processed.

    Returns:
        str: The response generated by the route manager.
    """
    response, route_text = route_manager.get_response(query=query, user_id=user_id)
    return response, route_text
