"""Helpers for resolving named endpoints from component context."""

from __future__ import annotations

from collections.abc import Mapping
from pathlib import Path
from typing import Any

from cmvr_edge_ai.core.component import ComponentContext
from cmvr_edge_ai.transports.grpc import GrpcChannelSettings
from cmvr_edge_ai.transports.http import HttpClientSettings


class ConnectorConfigurationError(ValueError):
    pass


def resolve_endpoint(context: ComponentContext, endpoint_id: str) -> Any:
    endpoints = context.metadata.get("endpoints")
    if not isinstance(endpoints, Mapping):
        raise ConnectorConfigurationError(
            "component context does not contain a named endpoints mapping"
        )
    try:
        return endpoints[endpoint_id]
    except KeyError as exc:
        raise ConnectorConfigurationError(
            f"unknown endpoint referenced by connector: {endpoint_id}"
        ) from exc


def endpoint_value(endpoint: Any, name: str, default: Any = None) -> Any:
    if isinstance(endpoint, Mapping):
        return endpoint.get(name, default)
    return getattr(endpoint, name, default)


def grpc_channel_settings(endpoint: Any) -> GrpcChannelSettings:
    target = endpoint_value(endpoint, "target")
    if not target:
        raise ConnectorConfigurationError("gRPC endpoint requires target")
    options = endpoint_value(endpoint, "options", {}) or {}
    if not isinstance(options, Mapping):
        raise ConnectorConfigurationError("gRPC endpoint options must be a mapping")

    channel_options: list[tuple[str, int | str]] = []
    configured_options = options.get("channel_options", {})
    if isinstance(configured_options, Mapping):
        channel_options.extend(
            (str(key), value) for key, value in configured_options.items()
        )
    max_receive_mb = options.get("max_receive_mb")
    if max_receive_mb is not None:
        channel_options.append(
            (
                "grpc.max_receive_message_length",
                int(float(max_receive_mb) * 1024 * 1024),
            )
        )
    max_send_mb = options.get("max_send_mb")
    if max_send_mb is not None:
        channel_options.append(
            ("grpc.max_send_message_length", int(float(max_send_mb) * 1024 * 1024))
        )

    root_certificates = options.get("root_certificates")
    return GrpcChannelSettings(
        target=str(target),
        tls=bool(endpoint_value(endpoint, "tls", False)),
        root_certificates=(Path(root_certificates) if root_certificates else None),
        options=tuple(channel_options),
    )


def http_client_settings(endpoint: Any) -> HttpClientSettings:
    base_url = endpoint_value(endpoint, "base_url")
    if not base_url:
        raise ConnectorConfigurationError("HTTP endpoint requires base_url")
    metadata = endpoint_value(endpoint, "metadata", {}) or {}
    options = endpoint_value(endpoint, "options", {}) or {}
    if not isinstance(options, Mapping):
        raise ConnectorConfigurationError("HTTP endpoint options must be a mapping")
    verify_tls = options.get("verify_tls", True)
    if not isinstance(verify_tls, bool):
        raise ConnectorConfigurationError("HTTP options.verify_tls must be boolean")
    return HttpClientSettings(
        base_url=str(base_url),
        timeout_s=float(endpoint_value(endpoint, "timeout_s", 5.0)),
        headers=tuple((str(key), str(value)) for key, value in metadata.items()),
        # HTTPS verification is secure by default.  The generic ``tls`` flag is
        # used by gRPC channel construction and must never silently disable
        # certificate verification for an https:// URL.
        verify_tls=verify_tls,
    )
