"""MLDClient — the main entry point for the MLD platform API.

Usage:
    from mld_sdk.client import MLDClient

    with MLDClient("http://localhost:8001", username="admin", password="pass") as client:
        experiments = client.experiments.list(status="completed")
        data = client.experiments.get_data(42)
"""

from __future__ import annotations

import os
from functools import cached_property
from typing import Any

from mld_sdk.client._config import get_default_host, get_stored_token
from mld_sdk.client._exceptions import MLDAPIError
from mld_sdk.client._http import HTTPClient


class MLDClient:
    """Python client for the MLD platform REST API.

    Args:
        base_url: Platform URL (e.g. "http://localhost:8001").
            Falls back to MLD_URL env var, then ~/.config/mld/credentials.json.
        token: JWT auth token. Falls back to MLD_TOKEN env var, then stored credentials.
        username: Username for login. If both username and password are given,
            login is performed automatically during initialization.
        password: Password for login.
        timeout: Request timeout in seconds (default 30).
    """

    def __init__(
        self,
        base_url: str | None = None,
        token: str | None = None,
        username: str | None = None,
        password: str | None = None,
        timeout: float = 30.0,
    ) -> None:
        # Resolve base URL
        self._base_url = base_url or os.environ.get("MLD_URL") or get_default_host()
        if not self._base_url:
            raise MLDAPIError("No MLD host configured. Pass base_url, set MLD_URL, or run: mld auth login --url <URL>")

        # Resolve token
        self._initial_token = token or os.environ.get("MLD_TOKEN") or get_stored_token(self._base_url)

        self._http = HTTPClient(
            base_url=self._base_url,
            token=self._initial_token,
            timeout=timeout,
        )

        # Wire up token refresh
        self._http.set_refresh_callback(self._try_refresh)

        # Auto-login if credentials provided
        if username and password:
            self.login(username, password)

    def _try_refresh(self) -> str | None:
        """Attempt to refresh the current token. Returns new token or None."""
        try:
            data = self.auth.refresh()
            return data.get("access_token")
        except Exception:
            return None

    # -- Convenience auth methods --

    def login(self, username: str, password: str) -> dict[str, Any]:
        """Authenticate and store credentials."""
        return self.auth.login(username, password)

    def logout(self) -> None:
        """Clear stored credentials."""
        self.auth.logout()

    def whoami(self) -> dict[str, Any]:
        """Return current user info."""
        return self.auth.whoami()

    # -- Resource namespaces --

    @cached_property
    def auth(self) -> Any:
        from mld_sdk.client.resources.auth import AuthAPI

        return AuthAPI(self._http)

    @cached_property
    def experiments(self) -> Any:
        from mld_sdk.client.resources.experiments import ExperimentsAPI

        return ExperimentsAPI(self._http)

    @cached_property
    def projects(self) -> Any:
        from mld_sdk.client.resources.projects import ProjectsAPI

        return ProjectsAPI(self._http)

    @cached_property
    def plugins(self) -> Any:
        from mld_sdk.client.resources.plugins import PluginsAPI

        return PluginsAPI(self._http)

    # -- Context manager --

    def __enter__(self) -> MLDClient:
        return self

    def __exit__(self, *args: object) -> None:
        self.close()

    def close(self) -> None:
        """Close the underlying HTTP connection."""
        self._http.close()

    def __repr__(self) -> str:
        return f"MLDClient(base_url={self._base_url!r})"
