import logging
from dataclasses import dataclass
from enum import StrEnum

from opn_api.api.client import OPNAPIClient
from opn_api.client import OPNFirewallClient
from pyfsense_client.v1.client import PfSenseV1Client
from pyfsense_client.v2.client import PfSenseV2Client

from .config import ConfigLoader
from .exceptions import ConfigException

logger = logging.getLogger(__name__)


# Keep a cache of created client instances to avoid recreating clients / reloading configurations
client_cache = {}


class RouterType(StrEnum):
    PFSENSE_V1 = "pfsenseV1"
    PFSENSE_V2 = "pfsenseV2"
    OPNSENSE = "opnsense"


@dataclass
class Router:
    name: str
    type: RouterType

    @property
    def client(self):
        return self._get_client()

    def _get_client(self):
        # Check if the instance already exists in the cache
        if self.name in client_cache:
            return client_cache[self.name]

        # If not, create a new instance and store it in the cache
        client = ClientFactory(self).get_client()
        client_cache[self.name] = client
        return client


class ClientFactory:
    def __init__(self, router: Router):
        self.router = router
        self.loader = ConfigLoader(router.name)

    def _get_client_config(self):
        if self.router.name not in self.loader.list_clients():
            message = f"No Redis configuration found for router '{self.router.name}'."
            logger.error(message)
            raise ConfigException(message)

        config_router_type = self.loader.get_router_type()
        if config_router_type != self.router.type:
            message = (
                f"Configuration type mismatch for router '{self.router.name}': "
                f"expected '{self.router.type}', but got '{config_router_type}'. "
                "Please verify that the stored configuration matches the router type."
            )
            logger.error(message)
            raise ConfigException(message)
        return self.loader.get_client_config()

    def get_client(self):
        config = self._get_client_config()

        match self.router.type:
            case RouterType.PFSENSE_V1:
                return PfSenseV1Client(config=config)
            case RouterType.PFSENSE_V2:
                return PfSenseV2Client(config=config)
            case RouterType.OPNSENSE:
                api_client = OPNAPIClient(config=config)
                return OPNFirewallClient(client=api_client)
            case _:
                raise ValueError(f"Unknown router type: {self.router.type}")
