"""
Main Daisi client providing unified interface to all services.
"""

from typing import TYPE_CHECKING, Optional

from .clients.accounts import AccountClient
from .clients.auth import AuthClient
from .clients.commands import AppCommandsClient, HostCommandsClient
from .clients.dapps import DappClient
from .clients.hosts import HostClient
from .clients.inference import InferenceClient
from .clients.models import ModelsClient
from .clients.networks import NetworkClient
from .clients.orcs import OrcClient
from .clients.peers import PeerClient
from .clients.session import SessionClient
from .clients.settings import SettingsClient
from .config import DaisiConfig

if TYPE_CHECKING:
    from .providers import ClientKeyProvider


class DaisiClient:
    """Unified client for interacting with Daisi services.

    This client provides access to all Daisi services through a single interface.
    It manages the configuration and lifecycle of all underlying gRPC clients.

    Unlike .NET's approach using DI/IServiceCollection, Python's DaisiClient
    uses simple composition. For DI integration, you can register DaisiClient
    as a singleton in your DI container of choice.

    Attributes:
        config: Daisi configuration
        auth: Authentication client
        session: Session management client
        inference: Inference client
        models: Models client
        hosts: Host management client
        peers: Peer client
        settings: Settings client
        host_commands: Host commands client
        app_commands: App commands client
        accounts: Account management client
        networks: Network management client
        dapps: Dapp management client
        orcs: Orchestrator management client
    """

    def __init__(
        self,
        client_key: Optional[str] = None,
        orc_address: Optional[str] = None,
        orc_port: Optional[int] = None,
        use_ssl: bool = True,
        network_name: Optional[str] = None,
        secret_key: Optional[str] = None,
        client_key_provider: Optional["ClientKeyProvider"] = None,
        config: Optional[DaisiConfig] = None,
    ) -> None:
        """Initialize Daisi client.

        Args:
            client_key: Client authentication key. Falls back to DAISI_CLIENT_KEY env var.
            orc_address: Orchestrator address. Falls back to DAISI_ORC_ADDRESS env var.
            orc_port: Orchestrator port. Falls back to DAISI_ORC_PORT env var.
            use_ssl: Whether to use SSL/TLS for connections.
            network_name: Network name to use. Defaults to "devnet".
            secret_key: Secret key for auto-creating client keys.
            client_key_provider: Custom client key provider.
            config: Pre-configured DaisiConfig (overrides other params if provided).

        Raises:
            ValueError: If required configuration is missing.
        """
        if config is not None:
            self.config = config
        else:
            self.config = DaisiConfig(
                client_key=client_key,
                orc_address=orc_address,
                orc_port=orc_port,
                use_ssl=use_ssl,
                network_name=network_name,
                secret_key=secret_key,
                client_key_provider=client_key_provider,
            )

        # Core clients
        self.auth = AuthClient(self.config)
        self.session = SessionClient(self.config)
        self.inference = InferenceClient(self.config)
        self.models = ModelsClient(self.config)
        self.hosts = HostClient(self.config)
        self.peers = PeerClient(self.config)
        self.settings = SettingsClient(self.config)
        self.host_commands = HostCommandsClient(self.config)
        self.app_commands = AppCommandsClient(self.config)

        # New clients (matching .NET SDK)
        self.accounts = AccountClient(self.config)
        self.networks = NetworkClient(self.config)
        self.dapps = DappClient(self.config)
        self.orcs = OrcClient(self.config)

    def use_daisi(self) -> "DaisiClient":
        """Initialize SDK with secret key authentication.

        Similar to .NET's UseDaisi() extension method, this creates
        a client key from the secret key if configured.

        Returns:
            Self for method chaining.

        Raises:
            DaisiError: If client key creation fails.
        """
        if self.config.secret_key:
            client_key, _ = self.auth.create_client_key(self.config.secret_key)
            self.config.client_key = client_key
        return self

    def close(self) -> None:
        """Close all client connections."""
        self.auth.close()
        self.session.close()
        self.inference.close()
        self.models.close()
        self.hosts.close()
        self.peers.close()
        self.settings.close()
        self.host_commands.close()
        self.app_commands.close()
        self.accounts.close()
        self.networks.close()
        self.dapps.close()
        self.orcs.close()

    def __enter__(self) -> "DaisiClient":
        """Context manager entry."""
        return self

    def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
        """Context manager exit."""
        self.close()
