"""Factory functions for creating broker clients.

Analogous to ``db_core.clients.factory`` — resolves connection parameters
from explicit arguments first, then falls back to environment variables.

Each broker has a ``create_<name>_client()`` function and a
``get_<name>_client()`` alias.
"""

from __future__ import annotations

import os
from typing import Any, Optional

from .kafka import KafkaClient, KafkaConfig

# ---------------------------------------------------------------------------
# Environment variable names
# ---------------------------------------------------------------------------

DEFAULT_KAFKA_PREFIX = "KAFKA"
KAFKA_BOOTSTRAP_KEYS = [
    "KAFKA_BOOTSTRAP_SERVERS",
    "KAFKA_URL",
]


def _resolve_kafka_bootstrap_servers(prefix: str = DEFAULT_KAFKA_PREFIX) -> Optional[str]:
    """Resolve bootstrap servers from environment variables.

    Checked in order:
    1. ``KAFKA_BOOTSTRAP_SERVERS``
    2. ``KAFKA_URL``
    3. ``KAFKA_HOST`` + ``KAFKA_PORT`` (default port 9092)
    """
    for key in KAFKA_BOOTSTRAP_KEYS:
        value = os.getenv(key)
        if value:
            return value

    host = os.getenv(f"{prefix}_HOST") or os.getenv("KAFKA_HOST")
    port = os.getenv(f"{prefix}_PORT") or os.getenv("KAFKA_PORT", "9092")
    if host:
        return f"{host}:{port}"

    return None


def _env(key: str, default: Any = None) -> Optional[str]:
    """Shorthand for ``os.getenv`` with a default."""
    return os.getenv(key, default)


# ---------------------------------------------------------------------------
# create_kafka_client
# ---------------------------------------------------------------------------


def create_kafka_client(
    bootstrap_servers: Optional[str] = None,
    client_id: Optional[str] = None,
    group_id: Optional[str] = None,
    config: Optional[KafkaConfig] = None,
    **kwargs: Any,
) -> KafkaClient:
    """Create a configured :class:`KafkaClient`.

    Resolution order (first wins):

    1. Explicit *config* (a full :class:`KafkaConfig` instance)
    2. Explicit keyword arguments (*bootstrap_servers*, *client_id*, …)
    3. Environment variables (``KAFKA_BOOTSTRAP_SERVERS``, ``KAFKA_CLIENT_ID``, …)
    4. :class:`KafkaConfig` defaults

    Args:
        bootstrap_servers: Comma-separated broker addresses
            (``"localhost:9092"`` or ``"kafka1:9092,kafka2:9092"``).
        client_id: Identifies this client instance to the broker.
        group_id: Default consumer group for subscriptions.
        config: A fully-built :class:`KafkaConfig`.  When provided, all
            other arguments are ignored.
        **kwargs: Additional overrides for any :class:`KafkaConfig` field.

    Returns:
        A :class:`KafkaClient` (not yet connected).

    Raises:
        ValueError: If no ``bootstrap_servers`` can be resolved.

    Example::

        # From env vars
        client = create_kafka_client()

        # Explicit
        client = create_kafka_client(
            bootstrap_servers="localhost:9092",
            client_id="my-svc",
            group_id="worker",
        )

        # Full config
        config = KafkaConfig(bootstrap_servers="kafka:9093", acks="all")
        client = create_kafka_client(config=config)
    """
    # 1. If a full config is passed, use it as base
    if config is not None:
        final_config = config
    else:
        # 2. Resolve bootstrap_servers
        resolved_bootstrap = bootstrap_servers or _resolve_kafka_bootstrap_servers()
        if not resolved_bootstrap:
            raise ValueError(
                "No Kafka bootstrap_servers configured. Provide "
                "`bootstrap_servers` or set KAFKA_BOOTSTRAP_SERVERS / "
                "KAFKA_URL / KAFKA_HOST + KAFKA_PORT environment variables."
            )

        # 3. Build config from args + env
        resolved_client_id = client_id or _env("KAFKA_CLIENT_ID")
        resolved_group_id = group_id or _env("KAFKA_GROUP_ID")
        resolved_auto_offset = _env("KAFKA_AUTO_OFFSET_RESET")
        resolved_security = _env("KAFKA_SECURITY_PROTOCOL")
        resolved_sasl_mechanism = _env("KAFKA_SASL_MECHANISM")
        resolved_sasl_username = _env("KAFKA_SASL_USERNAME")
        resolved_sasl_password = _env("KAFKA_SASL_PASSWORD")

        config_dict: dict = {
            "bootstrap_servers": resolved_bootstrap,
        }
        if resolved_client_id:
            config_dict["client_id"] = resolved_client_id
        if resolved_group_id:
            config_dict["group_id"] = resolved_group_id
        if resolved_auto_offset:
            config_dict["auto_offset_reset"] = resolved_auto_offset
        if resolved_security:
            config_dict["security_protocol"] = resolved_security
        if resolved_sasl_mechanism:
            config_dict["sasl_mechanism"] = resolved_sasl_mechanism
        if resolved_sasl_username:
            config_dict["sasl_username"] = resolved_sasl_username
        if resolved_sasl_password:
            config_dict["sasl_password"] = resolved_sasl_password

        # Apply **kwargs overrides
        config_dict.update(kwargs)

        final_config = KafkaConfig(**config_dict)

    return KafkaClient(final_config)


def get_kafka_client(
    bootstrap_servers: Optional[str] = None,
    client_id: Optional[str] = None,
    group_id: Optional[str] = None,
    config: Optional[KafkaConfig] = None,
    **kwargs: Any,
) -> KafkaClient:
    """Alias for :func:`create_kafka_client`."""
    return create_kafka_client(
        bootstrap_servers=bootstrap_servers,
        client_id=client_id,
        group_id=group_id,
        config=config,
        **kwargs,
    )


# ---------------------------------------------------------------------------
# Future backend stubs
# ---------------------------------------------------------------------------


def create_nats_client(**kwargs: Any) -> Any:
    """Create a NATS client (not yet implemented).

    Raises:
        NotImplementedError: Always — NATS support is planned but not yet available.
    """
    raise NotImplementedError(
        "NATS client is not yet implemented. "
        "Install the [nats] extra and check back in a future release: "
        "pip install broker_core[nats]"
    )


def get_nats_client(**kwargs: Any) -> Any:
    """Alias for :func:`create_nats_client`."""
    return create_nats_client(**kwargs)


def create_rabbitmq_client(**kwargs: Any) -> Any:
    """Create a RabbitMQ client (not yet implemented).

    Raises:
        NotImplementedError: Always — RabbitMQ support is planned but not yet available.
    """
    raise NotImplementedError(
        "RabbitMQ client is not yet implemented. "
        "Install the [rabbitmq] extra and check back in a future release: "
        "pip install broker_core[rabbitmq]"
    )


def get_rabbitmq_client(**kwargs: Any) -> Any:
    """Alias for :func:`create_rabbitmq_client`."""
    return create_rabbitmq_client(**kwargs)
