"""
Configuration and settings for Daisi SDK.
"""

import os
from typing import TYPE_CHECKING, Mapping, Optional, Union

if TYPE_CHECKING:
    from .providers import ClientKeyProvider


# Default orchestrator addresses
# Note: Currently only orc.daisinet.com exists. The separate dev/live
# endpoints documented in .NET SDK (orc-dev.daisinet.com, orc-live.daisinet.com)
# do not have DNS records.
ORC_DEV_ADDRESS = "orc.daisinet.com"
ORC_LIVE_ADDRESS = "orc.daisinet.com"


class DaisiConfig:
    """Configuration for Daisi SDK clients.

    This class manages SDK configuration including orchestrator connection,
    authentication, and network settings.

    Note: Unlike .NET's DaisiStaticSettings which uses static properties,
    Python uses instance-based configuration for better testability and
    to avoid global mutable state.
    """

    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,
    ) -> None:
        """Initialize Daisi configuration.

        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 creating client keys (optional).
            client_key_provider: Custom provider for client keys.
        """
        self._client_key = client_key or os.getenv("DAISI_CLIENT_KEY", "")
        self.orc_address = orc_address or os.getenv(
            "DAISI_ORC_ADDRESS", ORC_LIVE_ADDRESS
        )
        self.orc_port = orc_port or int(os.getenv("DAISI_ORC_PORT", "443"))
        self.use_ssl = use_ssl
        self.network_name = network_name or os.getenv("DAISI_NETWORK_NAME", "devnet")
        self.secret_key = secret_key or os.getenv("DAISI_SECRET_KEY", "")
        self._client_key_provider = client_key_provider

        self.client_key_header = "x-daisi-client-key"

    @property
    def client_key(self) -> str:
        """Get the client key.

        If a client_key_provider is set, uses it to get the key.
        Otherwise returns the stored client_key.
        """
        if self._client_key_provider is not None:
            return self._client_key_provider.get_client_key()
        return self._client_key

    @client_key.setter
    def client_key(self, value: str) -> None:
        """Set the client key directly."""
        self._client_key = value

    @property
    def client_key_provider(self) -> Optional["ClientKeyProvider"]:
        """Get the client key provider."""
        return self._client_key_provider

    @client_key_provider.setter
    def client_key_provider(self, provider: Optional["ClientKeyProvider"]) -> None:
        """Set a custom client key provider."""
        self._client_key_provider = provider

    @property
    def orc_url(self) -> str:
        """Get the full orchestrator URL."""
        protocol = "https" if self.use_ssl else "http"
        return f"{protocol}://{self.orc_address}:{self.orc_port}"

    def auto_swap_orc(self, environment: Optional[str] = None) -> None:
        """Automatically select orchestrator based on environment.

        Similar to .NET's AutoswapOrc(), this selects dev or live orc
        based on environment. However, instead of checking ASPNETCORE_ENVIRONMENT,
        this checks DAISI_ENVIRONMENT or accepts an explicit parameter.

        Args:
            environment: Environment name ("development" or "production").
                        Falls back to DAISI_ENVIRONMENT env var.
        """
        env = environment or os.getenv("DAISI_ENVIRONMENT", "development")
        env_lower = env.lower()

        if env_lower in ("development", "dev", "local"):
            self.orc_address = ORC_DEV_ADDRESS
        else:
            self.orc_address = ORC_LIVE_ADDRESS

        self.orc_port = 443
        self.use_ssl = True

    def load_from_dict(self, config: Mapping[str, str]) -> None:
        """Load configuration from a dictionary.

        Similar to .NET's LoadFromConfiguration, this loads settings
        from a dictionary (useful for loading from JSON/YAML config files).

        Args:
            config: Dictionary with configuration values. Supports keys:
                   - "orc_address" or "Daisi:OrcIpAddressOrDomain"
                   - "orc_port" or "Daisi:OrcPort"
                   - "use_ssl" or "Daisi:OrcUseSSL"
                   - "client_key" or "Daisi:ClientKey"
                   - "secret_key" or "Daisi:SecretKey"
                   - "network_name" or "Daisi:NetworkName"
        """
        # Support both Python-style and .NET-style keys
        key_mappings = {
            "orc_address": ["orc_address", "Daisi:OrcIpAddressOrDomain"],
            "orc_port": ["orc_port", "Daisi:OrcPort"],
            "use_ssl": ["use_ssl", "Daisi:OrcUseSSL"],
            "client_key": ["client_key", "Daisi:ClientKey"],
            "secret_key": ["secret_key", "Daisi:SecretKey"],
            "network_name": ["network_name", "Daisi:NetworkName"],
        }

        for attr, keys in key_mappings.items():
            for key in keys:
                if key in config:
                    value = config[key]
                    if attr == "orc_port":
                        setattr(self, attr, int(value))
                    elif attr == "use_ssl":
                        setattr(self, attr, value.lower() in ("true", "1", "yes"))
                    elif attr == "client_key":
                        self._client_key = value
                    else:
                        setattr(self, attr, value)
                    break

    def validate(self) -> None:
        """Validate configuration.

        Raises:
            ValueError: If required configuration is missing.
        """
        if not self.client_key:
            raise ValueError(
                "Client key not provided. Set DAISI_CLIENT_KEY environment variable "
                "or pass client_key parameter."
            )
        if not self.orc_address:
            raise ValueError("Orchestrator address not provided")
