from __future__ import annotations

import os
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, Optional

if sys.version_info >= (3, 11):
    import tomllib
else:
    try:
        import tomli as tomllib  # type: ignore[import-not-found,no-redef]
    except ImportError:
        tomllib = None  # type: ignore[assignment]


@dataclass(frozen=True)
class VeritellConfig:
    base_url: str
    api_key: Optional[str]
    env: str
    timeout_seconds: float = 30.0
    user_agent: str = "veritell-langchain"
    fail_open: bool = False


def _normalize_env(env: str) -> str:
    e = (env or "").strip().lower()
    if e in {"prod", "production"}:
        return "production"
    if e in {"staging", "stage", "stg"}:
        return "staging"
    return "production"


def _default_base_url_for(env: str) -> str:
    return "https://api.dev.veritell.ai" if env == "staging" else "https://api.veritell.ai"


def _default_config_path() -> Optional[Path]:
    """Return an optional default config path.

    Important: we do NOT implicitly load ./veritell.toml by default.
    That behavior makes unit tests and library consumers surprising.

    If a caller wants a config file, they should pass `config_path=` or
    set VERITELL_CONFIG_PATH.
    """

    p = os.getenv("VERITELL_CONFIG_PATH")
    if p:
        return Path(p)
    return None


def _load_toml(path: Path) -> Dict[str, Any]:
    if tomllib is None:
        return {}
    with open(path, "rb") as f:
        return tomllib.load(f)


def _get(d: Dict[str, Any], dotted: str) -> Any:
    cur: Any = d
    for part in dotted.split("."):
        if not isinstance(cur, dict):
            return None
        cur = cur.get(part)
        if cur is None:
            return None
    return cur


def _resolve_env_from_sources(explicit_env: Optional[str], cfg_file: Dict[str, Any]) -> str:
    if explicit_env:
        return _normalize_env(explicit_env)

    env_from_envvar = os.getenv("VERITELL_ENV")
    if env_from_envvar:
        return _normalize_env(env_from_envvar)

    file_env = _get(cfg_file, "veritell.env")
    if isinstance(file_env, str) and file_env.strip():
        return _normalize_env(file_env)

    return "production"


def _resolve_api_key_from_env(resolved_env: str) -> Optional[str]:
    if resolved_env == "staging":
        return os.getenv("VERITELL_API_KEY_STAGING") or os.getenv("VERITELL_API_KEY")

    return (
        os.getenv("VERITELL_API_KEY_PRODUCTION")
        or os.getenv("VERITELL_API_KEY_PROD")
        or os.getenv("VERITELL_API_KEY")
    )


def resolve_config(
    *,
    config_path: Optional[str] = None,
    base_url: Optional[str] = None,
    api_key: Optional[str] = None,
    env: Optional[str] = None,
    timeout_seconds: Optional[float] = None,
    fail_open: Optional[bool] = None,
    user_agent: Optional[str] = None,
) -> VeritellConfig:
    resolved_path = Path(config_path) if config_path else _default_config_path()
    cfg_file: Dict[str, Any] = {}
    if resolved_path and resolved_path.exists():
        cfg_file = _load_toml(resolved_path)

    resolved_env = _resolve_env_from_sources(env, cfg_file)

    file_timeout = _get(cfg_file, "veritell.timeout_seconds")
    resolved_timeout = (
        float(timeout_seconds)
        if timeout_seconds is not None
        else float(file_timeout or 30)
    )

    file_fail_open = _get(cfg_file, "veritell.fail_open")
    resolved_fail_open = bool(fail_open) if fail_open is not None else bool(file_fail_open or False)

    file_user_agent = _get(cfg_file, "veritell.user_agent")
    resolved_user_agent = str(user_agent or file_user_agent or "veritell-langchain")

    env_base_url = _get(cfg_file, f"veritell.environments.{resolved_env}.base_url")
    env_api_key = _get(cfg_file, f"veritell.environments.{resolved_env}.api_key")

    resolved_base_url = (
        base_url
        or (env_base_url if isinstance(env_base_url, str) else None)
        or _default_base_url_for(resolved_env)
    ).rstrip("/")

    # Only consider environment variables if the caller did NOT provide a config file.
    # This keeps config_path-driven resolution deterministic for tests and consumers.
    env_api_key_value = None
    if not config_path:
        env_api_key_value = _resolve_api_key_from_env(resolved_env)

    resolved_api_key = (
        api_key
        if api_key is not None
        else (env_api_key if isinstance(env_api_key, str) else None)
        if (env_api_key if isinstance(env_api_key, str) else None) is not None
        else env_api_key_value
        if env_api_key_value is not None
        else None
    )

    return VeritellConfig(
        base_url=resolved_base_url,
        api_key=resolved_api_key,
        env=resolved_env,
        timeout_seconds=resolved_timeout,
        user_agent=resolved_user_agent,
        fail_open=resolved_fail_open,
    )


def get_api_key() -> Optional[str]:
    """Back-compat convenience helper (used by package tests).

    Priority:
    - VERITELL_API_KEY
    - VERITELL_KEY
    """

    return os.getenv("VERITELL_API_KEY") or os.getenv("VERITELL_KEY")


def get_api_base_url() -> str:
    """Back-compat convenience helper (used by package tests)."""

    return (os.getenv("VERITELL_API_BASE_URL") or "https://api.veritell.ai/v1").rstrip("/")


def get_timeout() -> int:
    """Back-compat convenience helper (used by package tests)."""

    raw = os.getenv("VERITELL_TIMEOUT")
    try:
        v = int(raw) if raw is not None else 30
        return v if v > 0 else 30
    except (TypeError, ValueError):
        return 30
