"""ServiceManager base class."""

from typing import Dict, Optional

from loguru import logger

from iwa.core.chain import ChainInterfaces
from iwa.core.contracts.cache import ContractCache
from iwa.core.models import Config
from iwa.core.wallet import Wallet
from iwa.plugins.olas.constants import OLAS_CONTRACTS
from iwa.plugins.olas.contracts.service import ServiceManagerContract, ServiceRegistryContract
from iwa.plugins.olas.models import OlasConfig
from iwa.web.cache import CacheTTL, response_cache


class ServiceManagerBase:
    """Base class for ServiceManager."""

    def __init__(self, wallet: Wallet, service_key: Optional[str] = None):
        """Initialize ServiceManager.

        Args:
            wallet: The wallet instance for signing transactions.
            service_key: Optional key (chain_name:service_id) to select a specific service.
                        If not provided, service operations require explicit service selection.

        """
        self.wallet = wallet
        self.global_config = Config()

        self.olas_config = self.global_config.plugins.get("olas")
        if isinstance(self.olas_config, dict):
            self.olas_config = OlasConfig(**self.olas_config)
            self.global_config.plugins["olas"] = self.olas_config
        elif self.olas_config is None:
            self.olas_config = OlasConfig()
            self.global_config.plugins["olas"] = self.olas_config

        # Get service by key if provided
        self.service = None
        if service_key and ":" in service_key:
            chain_name, service_id = service_key.split(":", 1)
            self.service = self.olas_config.get_service(chain_name, int(service_id))

        # Initialize contracts (default to gnosis)
        service_chain = getattr(self.service, "chain_name", "gnosis")
        chain_name = service_chain if isinstance(service_chain, str) else "gnosis"
        self._init_contracts(chain_name)

        # Initialize TransferService from wallet
        self.transfer_service = self.wallet.transfer_service

    def _init_contracts(self, chain_name: str) -> None:
        """Initialize contracts for the given chain."""
        # OPTIMIZATION: Skip if already initialized for this chain
        if getattr(self, "chain_name", None) == chain_name.lower() and hasattr(self, "registry"):
            return

        chain_interface = ChainInterfaces().get(chain_name)

        # Get protocol contracts from plugin-local constants
        protocol_contracts = OLAS_CONTRACTS.get(chain_name.lower(), {})
        registry_address = protocol_contracts.get("OLAS_SERVICE_REGISTRY")
        manager_address = protocol_contracts.get("OLAS_SERVICE_MANAGER")

        if not registry_address or not manager_address:
            raise ValueError(f"OLAS contracts not found for chain: {chain_name}")

        self.registry = ContractCache().get_contract(
            ServiceRegistryContract, registry_address, chain_name=chain_name
        )
        self.manager = ContractCache().get_contract(
            ServiceManagerContract, manager_address, chain_name=chain_name
        )
        logger.debug(f"[SM-INIT] ServiceManager initialized. Chain: {chain_name}")
        logger.debug(f"[SM-INIT] Registry Address: {self.registry.address}")
        logger.debug(f"[SM-INIT] Manager Address: {self.manager.address}")
        self.chain_interface = chain_interface
        self.chain_name = chain_name.lower()

    def _save_config(self) -> None:
        """Persist configuration to config.yaml."""
        self.global_config.save_config()

    def _update_and_save_service_state(self) -> None:
        """Update the service object in olas_config and persist to config.yaml."""
        if self.service:
            # Update the service object in the configuration dictionary
            # This ensures that changes to self.service (which comes from the Router)
            # are reflected in the ServiceManager's internal configuration state
            # before saving to disk.
            self.olas_config.add_service(self.service)
        self._save_config()

    def get(self) -> Optional[Dict]:
        """Get service details by ID."""
        if not self.service:
            logger.error("No active service")
            return None
        return self.registry.get_service(self.service.service_id)

    def get_service_state(
        self, service_id: Optional[int] = None, force_refresh: bool = False
    ) -> str:
        """Get the state of a service as a string.

        Args:
            service_id: Optional service ID. If not provided, uses the active service.
            force_refresh: If True, bypass cache and fetch fresh data.

        Returns:
            The state name (e.g., 'DEPLOYED') or 'UNKNOWN' if not found.

        """
        if service_id is None:
            if not self.service:
                return "UNKNOWN"
            service_id = self.service.service_id

        # Build cache key using service_key if available, else chain:id
        if self.service and self.service.key:
            cache_key = f"service_state:{self.service.key}"
        else:
            cache_key = f"service_state:{self.chain_name}:{service_id}"

        # Invalidate cache if force refresh requested
        if force_refresh:
            response_cache.invalidate(cache_key)

        def fetch_state():
            try:
                info = self.registry.get_service(service_id)
                return info["state"].name
            except Exception as e:
                logger.debug(f"Failed to get service state for {service_id}: {e}")
                return "UNKNOWN"

        return response_cache.get_or_compute(
            cache_key, fetch_state, CacheTTL.SERVICE_STATE
        )
