"""Configuration loading for the Convexity SDK.

Resolution order (highest priority first):
1. Explicit arguments passed to ``ConvexityClient(...)``
2. Environment variables (``CONVEXITY_API_KEY``, ``CONVEXITY_BASE_URL``)
3. Local config file (``.convexity/config.yaml`` found by walking up from CWD)
4. Home config file (``~/.convexity/config.yaml``)
5. Built-in defaults
"""

from __future__ import annotations

from dataclasses import dataclass, field
import logging
import os
from pathlib import Path

logger = logging.getLogger("convexity_sdk.config")

DEFAULT_BASE_URL = "https://api.convexity.dev"
DEFAULT_TIMEOUT = 30.0
DEFAULT_MAX_RETRIES = 3
DEFAULT_RETRY_BACKOFF = 1.0

HOME_CONFIG_DIR = Path.home() / ".convexity"
HOME_CONFIG_FILE = HOME_CONFIG_DIR / "config.yaml"

CONFIG_FILENAME = ".convexity/config.yaml"

ENV_API_KEY = "CONVEXITY_API_KEY"
ENV_BASE_URL = "CONVEXITY_BASE_URL"


@dataclass(frozen=True)
class SDKConfig:
    """Resolved SDK configuration."""

    api_key: str | None = None
    base_url: str = DEFAULT_BASE_URL
    timeout: float = DEFAULT_TIMEOUT
    max_retries: int = DEFAULT_MAX_RETRIES
    retry_backoff: float = DEFAULT_RETRY_BACKOFF
    extra_headers: dict[str, str] = field(default_factory=dict)


def _find_local_config() -> Path | None:
    """Walk up from CWD looking for ``.convexity/config.yaml``.

    Returns the first matching path, or ``None`` if not found before reaching
    the filesystem root.
    """
    current = Path.cwd().resolve()
    while True:
        candidate = current / CONFIG_FILENAME
        if candidate.is_file():
            return candidate
        parent = current.parent
        if parent == current:
            # Reached filesystem root
            return None
        current = parent


def _parse_config_file(path: Path) -> dict[str, str]:
    """Parse a single config YAML file and return a flat dict."""
    try:
        # Lazy import – yaml is not a hard dependency
        import yaml  # type: ignore[import-untyped]

        with path.open() as fh:
            data = yaml.safe_load(fh)

        if not isinstance(data, dict):
            return {}

        # Support a ``default`` profile (only one supported for now)
        profile = data.get("default", data)
        if not isinstance(profile, dict):
            return {}

        result: dict[str, str] = {}
        if "api_key" in profile:
            result["api_key"] = str(profile["api_key"])
        if "base_url" in profile:
            result["base_url"] = str(profile["base_url"])
        return result
    except Exception:
        logger.debug("Failed to read config file %s", path, exc_info=True)
        return {}


def _load_config_file() -> dict[str, str]:
    """Load configuration from the nearest ``.convexity/config.yaml``.

    Search order:
    1. Walk up from CWD looking for a local ``.convexity/config.yaml``
    2. Fall back to ``~/.convexity/config.yaml``

    Returns a flat dict with keys ``api_key`` and ``base_url`` (if present).
    """
    # 1. Local config (project-level)
    local_path = _find_local_config()
    if local_path is not None:
        logger.debug("Using local config file: %s", local_path)
        return _parse_config_file(local_path)

    # 2. Home directory config
    if HOME_CONFIG_FILE.exists():
        logger.debug("Using home config file: %s", HOME_CONFIG_FILE)
        return _parse_config_file(HOME_CONFIG_FILE)

    return {}


def resolve_config(
    *,
    api_key: str | None = None,
    base_url: str | None = None,
    timeout: float | None = None,
    max_retries: int | None = None,
    retry_backoff: float | None = None,
    extra_headers: dict[str, str] | None = None,
) -> SDKConfig:
    """Merge explicit args → env vars → config file → defaults."""

    file_cfg = _load_config_file()

    resolved_api_key = api_key or os.environ.get(ENV_API_KEY) or file_cfg.get("api_key")
    resolved_base_url = base_url or os.environ.get(ENV_BASE_URL) or file_cfg.get("base_url") or DEFAULT_BASE_URL
    # Strip trailing slash for consistency
    resolved_base_url = resolved_base_url.rstrip("/")

    return SDKConfig(
        api_key=resolved_api_key,
        base_url=resolved_base_url,
        timeout=timeout if timeout is not None else DEFAULT_TIMEOUT,
        max_retries=max_retries if max_retries is not None else DEFAULT_MAX_RETRIES,
        retry_backoff=retry_backoff if retry_backoff is not None else DEFAULT_RETRY_BACKOFF,
        extra_headers=extra_headers or {},
    )
