from __future__ import annotations

import os
from collections.abc import Mapping
from dataclasses import dataclass

from wildedge import constants

TRUE_VALUES = {"1", "true", "yes", "on"}


@dataclass(frozen=True)
class ClientEnv:
    dsn: str | None
    debug: bool
    app_identity: str | None


@dataclass(frozen=True)
class RuntimeEnv:
    dsn: str | None
    app_version: str | None
    debug: bool
    print_startup_report: bool
    strict_integrations: bool
    flush_timeout: float
    integrations: list[str]
    hubs: list[str]
    propagate: bool
    sampling_interval_s: float | None


@dataclass(frozen=True)
class RunnerEnv:
    print_startup_report: bool
    propagate: bool


def parse_bool(value: str | None) -> bool:
    return (value or "").strip().lower() in TRUE_VALUES


def parse_integration_list(value: str | None, all_values: list[str]) -> list[str]:
    """Parse ``--integrations`` value; defaults to all when unset."""
    if not value or value == "all":
        return sorted(all_values)
    return [item.strip() for item in value.split(",") if item.strip()]


def parse_hub_list(value: str | None, all_values: list[str]) -> list[str]:
    """Parse ``--hubs`` value; defaults to empty when unset."""
    if not value or value == "none":
        return []
    if value == "all":
        return sorted(all_values)
    return [item.strip() for item in value.split(",") if item.strip()]


def resolve_app_identity(
    *,
    explicit: str | None,
    project_key: str,
    environ: Mapping[str, str] | None = None,
) -> str:
    env = environ if environ is not None else os.environ
    return explicit or env.get(constants.ENV_APP_IDENTITY) or project_key


def read_client_env(
    *,
    dsn: str | None = None,
    debug: bool | None = None,
    app_identity: str | None = None,
    environ: Mapping[str, str] | None = None,
) -> ClientEnv:
    env = environ if environ is not None else os.environ
    resolved_dsn = dsn or env.get(constants.ENV_DSN)
    resolved_debug = (
        debug if debug is not None else parse_bool(env.get(constants.ENV_DEBUG))
    )
    resolved_identity = app_identity or env.get(constants.ENV_APP_IDENTITY)
    return ClientEnv(
        dsn=resolved_dsn,
        debug=resolved_debug,
        app_identity=resolved_identity,
    )


def read_runtime_env(
    *,
    all_integrations: list[str],
    all_hubs: list[str],
    environ: Mapping[str, str] | None = None,
) -> RuntimeEnv:
    env = environ if environ is not None else os.environ
    flush_timeout = float(
        env.get(
            constants.ENV_FLUSH_TIMEOUT,
            str(constants.DEFAULT_SHUTDOWN_FLUSH_TIMEOUT_SEC),
        )
    )
    raw_sampling = env.get(constants.ENV_SAMPLING_INTERVAL)
    sampling_interval_s: float | None
    if raw_sampling is None:
        sampling_interval_s = constants.DEFAULT_SAMPLING_INTERVAL_S
    else:
        parsed_sampling = float(raw_sampling)
        sampling_interval_s = parsed_sampling if parsed_sampling > 0 else None
    return RuntimeEnv(
        dsn=env.get(constants.ENV_DSN),
        app_version=env.get(constants.ENV_APP_VERSION),
        debug=parse_bool(env.get(constants.ENV_DEBUG)),
        print_startup_report=parse_bool(env.get(constants.ENV_PRINT_STARTUP_REPORT)),
        strict_integrations=parse_bool(env.get(constants.ENV_STRICT_INTEGRATIONS)),
        flush_timeout=flush_timeout,
        integrations=parse_integration_list(
            env.get(constants.ENV_INTEGRATIONS), all_integrations
        ),
        hubs=parse_hub_list(env.get(constants.ENV_HUBS), all_hubs),
        propagate=parse_bool(env.get(constants.ENV_PROPAGATE, "1")),
        sampling_interval_s=sampling_interval_s,
    )


def read_runner_env(
    *,
    environ: Mapping[str, str] | None = None,
) -> RunnerEnv:
    env = environ if environ is not None else os.environ
    return RunnerEnv(
        print_startup_report=parse_bool(env.get(constants.ENV_PRINT_STARTUP_REPORT)),
        propagate=parse_bool(env.get(constants.ENV_PROPAGATE, "1")),
    )
