import logging

from opn_api.client import OPNFirewallClient
from opn_api.models.firewall_alias import (
    AliasType,
    FirewallAliasCreate,
    FirewallAliasResponse,
    FirewallAliasUpdate,
)

from ..exceptions import AdapterException
from ..type_conversions import (
    alias_to_opnsense_firewall_alias,
    opnsense_firewall_alias_to_alias,
)
from ..types import Alias
from .base import ClientAliasAdapter

logger = logging.getLogger(__name__)


class OPNsenseAliasAdapter(ClientAliasAdapter):
    """
    Concrete alias adapter for OPNsense. This class knows
    how to talk to an OPNsense firewall.
    """

    def __init__(self, client: OPNFirewallClient):
        super().__init__(client)

    def create_alias(self, alias: Alias, apply: bool = True):
        firewall_alias = alias_to_opnsense_firewall_alias(alias)
        new_alias = FirewallAliasCreate(**firewall_alias.model_dump())
        response = self.client.alias.add(new_alias)
        logger.debug(f"OPNsense create_alias response: {response}")

        if apply:
            self.client.alias.apply_changes()
        return response

    def update_alias(self, alias: Alias, apply: bool = True):
        # Check that the alias exists first
        try:
            uuid = self.client.alias.get_uuid(alias.name)
        except ValueError:
            raise AdapterException(f"Alias {alias.name} not found, cannot update.")

        firewall_alias = alias_to_opnsense_firewall_alias(alias)
        updated_alias = FirewallAliasUpdate(uuid=uuid, **firewall_alias.model_dump())
        response = self.client.alias.set(updated_alias)
        logger.debug(f"OPNsense update_alias response: {response}")

        if apply:
            self.client.alias.apply_changes()
        return response

    def is_alias_in_use(self, alias_name: str) -> bool:
        rules = self.client.filter.list_rules()
        for rule in rules:
            if alias_name == rule.source_net or alias_name == rule.destination_net:
                return True
        return False

    def delete_alias(self, alias_name: str, apply: bool = True) -> dict:
        # Skip if alias is in use
        if self.is_alias_in_use(alias_name):
            response = {"msg": f"Alias {alias_name} is in use. Skipping deletion."}
            logger.debug(response)
            return response

        # Find the alias's UUID before deleting
        try:
            uuid = self.client.alias.get_uuid(alias_name)
        except ValueError:
            raise AdapterException(f"Alias {alias_name} not found, cannot delete.")

        response = self.client.alias.delete(uuid)
        logger.debug(f"OPNsense delete_alias response: {response}")

        if apply:
            self.client.alias.apply_changes()
        return response

    def rename_alias(self, alias: Alias, new_name: str, apply: bool = True):
        # Check that the alias exists first
        try:
            uuid = self.client.alias.get_uuid(alias.name)
        except ValueError:
            raise AdapterException(f"Alias {alias.name} not found, cannot rename.")

        firewall_alias = alias_to_opnsense_firewall_alias(alias)
        firewall_alias.name = new_name  # update the name
        updated_alias = FirewallAliasUpdate(uuid=uuid, **firewall_alias.model_dump())
        response = self.client.alias.set(updated_alias)
        logger.debug(f"OPNsense rename_alias response: {response}")

        if apply:
            self.client.alias.apply_changes()
        return response

    def get_all_aliases(self) -> list[Alias]:
        # Exclude internal and external system aliases
        user_aliases = []
        all_aliases: list[FirewallAliasResponse] = self.client.alias.list()
        for alias in all_aliases:
            if alias.uuid.startswith("_"):
                continue
            if alias.type == AliasType.EXTERNAL:
                continue
            user_aliases.append(alias)

        # Convert to your local "Alias" objects
        return [opnsense_firewall_alias_to_alias(user_alias) for user_alias in user_aliases]

    def apply(self):
        """Apply pending firewall changes on OPNsense."""
        return self.client.alias.apply_changes()
