"""
Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com

Resolver functions for registration credentials, heartbeat settings, and endpoints.
"""

from __future__ import annotations

from typing import Any, Dict, Optional, Union
from urllib.parse import urlparse

from .models import ProxyCredentials, HeartbeatSettings
from .helpers import build_cert_tuple


def resolve_runtime_credentials(
    use_proxy_client: bool,
    proxy_client_config: Dict[str, Any],
    proxy_registration_config: Dict[str, Any],
) -> ProxyCredentials:
    """Return credentials for runtime interactions (heartbeat, unregister)."""
    if use_proxy_client:
        # Protocol MUST be explicitly specified (same as in prepare_registration_context)
        proxy_protocol = proxy_client_config.get("protocol")
        if not proxy_protocol:
            error_msg = (
                "proxy_client.protocol is required in configuration. "
                "It specifies the protocol used to connect to the proxy (http, https, or mtls)."
            )
            raise ValueError(error_msg)

        if proxy_protocol not in ("http", "https", "mtls"):
            error_msg = (
                f"Invalid proxy_client.protocol value: {proxy_protocol}. "
                "Must be one of: http, https, mtls"
            )
            raise ValueError(error_msg)

        # Build certificate tuple for client authentication (required for mTLS)
        # Use new ssl structure first, fallback to legacy fields
        ssl_config = proxy_client_config.get("ssl", {})
        cert_file = (
            ssl_config.get("cert")
            if ssl_config and isinstance(ssl_config, dict)
            else None
        )
        key_file = (
            ssl_config.get("key")
            if ssl_config and isinstance(ssl_config, dict)
            else None
        )
        ca_cert = (
            ssl_config.get("ca")
            if ssl_config and isinstance(ssl_config, dict)
            else None
        )

        # Fallback to legacy fields
        cert_file = cert_file or proxy_client_config.get("cert_file")
        key_file = key_file or proxy_client_config.get("key_file")
        ca_cert = ca_cert or proxy_client_config.get("ca_cert_file")

        cert_tuple = build_cert_tuple(cert_file, key_file)

        # For mTLS, certificates are required
        if proxy_protocol == "mtls":
            if not cert_tuple:
                error_msg = (
                    "proxy_client.ssl.cert and proxy_client.ssl.key are required "
                    "when proxy_client.protocol is mtls"
                )
                raise ValueError(error_msg)
            if not ca_cert:
                error_msg = (
                    "proxy_client.ssl.ca is required when proxy_client.protocol is mtls"
                )
                raise ValueError(error_msg)

        verify: Union[bool, str] = True
        if proxy_protocol == "http":
            verify = False
        elif proxy_protocol in ("https", "mtls"):
            if ca_cert:
                verify = ca_cert
            elif proxy_protocol == "mtls":
                # mTLS requires CA cert - should have been checked above
                verify = True  # Will fail if no CA, but that's expected
            else:
                # HTTPS can use system CA store
                verify = True

        return ProxyCredentials(cert=cert_tuple, verify=verify)

    return _resolve_registration_credentials(False, {}, proxy_registration_config)


def resolve_heartbeat_settings(
    use_proxy_client: bool,
    proxy_client_config: Dict[str, Any],
    proxy_registration_config: Dict[str, Any],
    proxy_url: str,
) -> HeartbeatSettings:
    """Compute heartbeat interval and full URL from configuration only.

    Args:
        use_proxy_client: Whether using proxy_client config format
        proxy_client_config: proxy_client configuration dict
        proxy_registration_config: proxy_registration configuration dict
        proxy_url: Base proxy URL (e.g., "http://localhost:3005") - not used, kept for compatibility

    Returns:
        HeartbeatSettings with interval and full URL from config
    """
    if use_proxy_client:
        heartbeat_config = proxy_client_config.get("heartbeat") or {}
        interval = int(heartbeat_config.get("interval", 30))
        # URL must be provided in config - no auto-generation
        heartbeat_url = heartbeat_config.get("url")
        if not heartbeat_url:
            raise ValueError(
                "heartbeat.url is required in proxy_client.heartbeat configuration"
            )
    else:
        heartbeat_config = proxy_registration_config.get("heartbeat") or {}
        interval = int(
            heartbeat_config.get(
                "interval",
                proxy_registration_config.get("heartbeat_interval", 30),
            )
        )
        # URL must be provided in config - no auto-generation
        heartbeat_url = heartbeat_config.get("url")
        if not heartbeat_url:
            # Debug: log what we have
            import logging

            logger = logging.getLogger(__name__)
            logger.error(
                f"❌ heartbeat.url is missing. proxy_registration_config keys: {list(proxy_registration_config.keys())}"
            )
            logger.error(f"❌ heartbeat_config: {heartbeat_config}")
            raise ValueError(
                "heartbeat.url is required in proxy_registration.heartbeat configuration"
            )

    return HeartbeatSettings(interval=interval, url=heartbeat_url)


def resolve_unregister_endpoint(
    use_proxy_client: bool,
    proxy_client_config: Dict[str, Any],
) -> str:
    """Get unregister endpoint path."""
    if use_proxy_client:
        registration_config = proxy_client_config.get("registration") or {}
        endpoint = registration_config.get("unregister_endpoint")
        if endpoint:
            return str(endpoint)
        return "/unregister"
    return "/unregister"


def _resolve_registration_credentials(
    use_proxy_client: bool,
    proxy_client_config: Dict[str, Any],
    proxy_registration_config: Dict[str, Any],
) -> ProxyCredentials:
    """
    Resolve registration credentials from configuration.

    Args:
        use_proxy_client: Whether to use proxy client configuration
        proxy_client_config: Proxy client configuration dictionary
        proxy_registration_config: Proxy registration configuration dictionary

    Returns:
        ProxyCredentials instance with resolved certificate and verification settings

    Raises:
        ValueError: If required configuration is missing
    """
    if use_proxy_client:
        # Protocol MUST be explicitly specified in config (same as prepare_registration_context)
        proxy_protocol = proxy_client_config.get("protocol")
        if not proxy_protocol:
            error_msg = (
                "proxy_client.protocol is required in configuration. "
                "It specifies the protocol used to connect to the proxy (http, https, or mtls)."
            )
            raise ValueError(error_msg)

        if proxy_protocol not in ("http", "https", "mtls"):
            error_msg = (
                f"Invalid proxy_client.protocol value: {proxy_protocol}. "
                "Must be one of: http, https, mtls"
            )
            raise ValueError(error_msg)

        # Build certificate tuple for client authentication (required for mTLS)
        # Use new ssl structure first, fallback to legacy fields
        ssl_config = proxy_client_config.get("ssl", {})
        cert_file = (
            ssl_config.get("cert")
            if ssl_config and isinstance(ssl_config, dict)
            else None
        )
        key_file = (
            ssl_config.get("key")
            if ssl_config and isinstance(ssl_config, dict)
            else None
        )
        ca_cert = (
            ssl_config.get("ca")
            if ssl_config and isinstance(ssl_config, dict)
            else None
        )

        # Fallback to legacy fields
        cert_file = cert_file or proxy_client_config.get("cert_file")
        key_file = key_file or proxy_client_config.get("key_file")
        ca_cert = ca_cert or proxy_client_config.get("ca_cert_file")

        cert_tuple = build_cert_tuple(cert_file, key_file)

        # For mTLS, certificates are required
        if proxy_protocol == "mtls":
            if not cert_tuple:
                error_msg = (
                    "proxy_client.ssl.cert and proxy_client.ssl.key are required "
                    "when proxy_client.protocol is mtls"
                )
                raise ValueError(error_msg)
            if not ca_cert:
                error_msg = (
                    "proxy_client.ssl.ca is required when proxy_client.protocol is mtls"
                )
                raise ValueError(error_msg)

        verify: Union[bool, str] = True
        if proxy_protocol == "http":
            # HTTP proxy doesn't need certificates
            verify = False
        elif proxy_protocol in ("https", "mtls"):
            # HTTPS/mTLS proxy requires certificates
            if ca_cert:
                verify = ca_cert
            elif proxy_protocol == "mtls":
                # mTLS requires CA cert - should have been checked above
                verify = True  # Will fail if no CA, but that's expected
            else:
                # HTTPS can use system CA store
                verify = True

        return ProxyCredentials(cert=cert_tuple, verify=verify)

    # For proxy_registration format, determine protocol same way as in prepare_registration_context
    # New SimpleConfig format stores URLs in register_url/unregister_url; fall back to older keys
    proxy_url_candidate: Optional[str] = (
        proxy_registration_config.get("register_url")
        or proxy_registration_config.get("proxy_url")
        or proxy_registration_config.get("server_url")
    )

    register_url = proxy_registration_config.get("register_url")
    if register_url:
        parsed_register = urlparse(str(register_url))
        proxy_url_candidate = f"{parsed_register.scheme}://{parsed_register.netloc}"

    if proxy_url_candidate:
        parsed = urlparse(str(proxy_url_candidate))
        url_scheme = parsed.scheme or "http"
    else:
        url_scheme = "http"

    # New SimpleConfig format: certificates are in ssl sub-section
    ssl_config = proxy_registration_config.get("ssl")
    if ssl_config is None:
        ssl_config = {}
    elif not isinstance(ssl_config, dict):
        # ssl_config might be SSLConfig object - convert to dict
        ssl_config = {
            "cert": getattr(ssl_config, "cert", None),
            "key": getattr(ssl_config, "key", None),
            "ca": getattr(ssl_config, "ca", None),
            "crl": getattr(ssl_config, "crl", None),
            "dnscheck": getattr(ssl_config, "dnscheck", False),
        }
    cert_file = ssl_config.get("cert") if isinstance(ssl_config, dict) else None
    key_file = ssl_config.get("key") if isinstance(ssl_config, dict) else None
    ca_cert = ssl_config.get("ca") if isinstance(ssl_config, dict) else None
    crl_file = ssl_config.get("crl") if isinstance(ssl_config, dict) else None

    # Fallback to legacy nested structures if top-level values are not provided
    if not cert_file or not key_file:
        cert_file = cert_file or proxy_registration_config.get("cert_file")
        key_file = key_file or proxy_registration_config.get("key_file")
        cert_config = proxy_registration_config.get("certificate") or {}
        if isinstance(cert_config, dict):
            cert_file = cert_file or cert_config.get("cert_file")
            key_file = key_file or cert_config.get("key_file")

    if not ca_cert:
        ca_cert = proxy_registration_config.get("ca_cert_file")
        if isinstance(ssl_config, dict):
            ca_cert = ca_cert or ssl_config.get("ca_cert") or ssl_config.get("ca")
        crl_file = crl_file or proxy_registration_config.get("crl_file")
        if isinstance(ssl_config, dict):
            crl_file = crl_file or ssl_config.get("crl_file")

    # Determine verify_mode (legacy ssl.verify_mode or new field)
    verify_mode = "CERT_REQUIRED"
    if isinstance(ssl_config, dict):
        verify_mode = ssl_config.get("verify_mode", verify_mode)
    verify_mode = proxy_registration_config.get("verify_mode", verify_mode)

    cert_tuple = build_cert_tuple(cert_file, key_file)

    # Determine protocol: distinguish between HTTPS and mTLS (same logic as prepare_registration_context)
    if url_scheme == "https" and cert_tuple and ca_cert:
        # All conditions for mTLS met: https:// + client certs + CA cert
        proxy_protocol = "mtls"
    elif url_scheme == "https":
        # HTTPS: https:// scheme, but not all mTLS requirements met
        proxy_protocol = "https"
    else:
        # HTTP
        proxy_protocol = "http"

    # For mTLS, certificates are required
    if proxy_protocol == "mtls":
        if not cert_tuple:
            error_msg = (
                "proxy_registration.certificate.cert_file and proxy_registration.certificate.key_file "
                "are required when using mTLS (https:// scheme with CA cert)"
            )
            raise ValueError(error_msg)
        if not ca_cert:
            error_msg = (
                "proxy_registration.ssl.ca_cert is required when using mTLS "
                "(https:// scheme with client certs)"
            )
            raise ValueError(error_msg)

    verify: Union[bool, str] = True
    if verify_mode == "CERT_NONE":
        verify = False
    elif ca_cert:
        verify = ca_cert
    elif proxy_protocol == "mtls":
        # mTLS requires CA cert - should have been checked above
        verify = True  # Will fail if no CA, but that's expected
    elif proxy_protocol == "https":
        # HTTPS can use system CA store
        verify = True
    else:
        # HTTP
        verify = False

    return ProxyCredentials(cert=cert_tuple, verify=verify)

