"""
Module: auth
Description: Authentication client and token storage

Implements:
    - docs/cli/reference.md#huitzo-login
    - docs/guides/developer-environment.md#authentication

See Also:
    - docs/reference/secrets.md (for secure storage patterns)
"""

from __future__ import annotations

import os
from pathlib import Path
from typing import Any

import httpx

from huitzo_cli.config import ConfigManager
from huitzo_cli.http_utils import parse_error


class TokenStore:
    """Manages authentication tokens securely."""

    def __init__(self) -> None:
        """Initialize token store."""
        self.token_path = self._get_token_path()

    @staticmethod
    def _get_token_path() -> Path:
        """Get platform-specific token file path."""
        if os.name == "nt":  # Windows
            base = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
            return base / "huitzo" / "token"
        else:  # Unix-like
            return Path.home() / ".huitzo" / "token"

    def save(self, token: str) -> None:
        """Save token to secure storage.

        Args:
            token: Authentication token to save
        """
        self.token_path.parent.mkdir(parents=True, exist_ok=True)

        # Create file with restrictive permissions atomically (no TOCTOU window)
        if os.name != "nt":
            fd = os.open(str(self.token_path), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
            with os.fdopen(fd, "w") as f:
                f.write(token)
            self.token_path.parent.chmod(0o700)
        else:
            with open(self.token_path, "w") as f:
                f.write(token)

    def load(self) -> str | None:
        """Load token from secure storage.

        Returns:
            Token if it exists, None otherwise
        """
        if not self.token_path.exists():
            return None

        try:
            with open(self.token_path) as f:
                return f.read().strip()
        except OSError:
            return None

    def delete(self) -> None:
        """Delete stored token."""
        if self.token_path.exists():
            try:
                self.token_path.unlink()
            except OSError:
                pass


class AuthClient:
    """Client for handling authentication with Huitzo backend."""

    def __init__(self, config_manager: ConfigManager) -> None:
        """Initialize auth client.

        Args:
            config_manager: Configuration manager instance
        """
        self.config = config_manager
        self.token_store = TokenStore()

    def login(
        self,
        email: str,
        password: str | None = None,
        url: str | None = None,
        token: str | None = None,
    ) -> dict[str, Any]:
        """Login to Huitzo backend.

        Args:
            email: User email address
            password: User password (required for credential-based login)
            url: Backend API URL (default from config)
            token: Optional pre-existing token (skips credential exchange)

        Returns:
            User info dict from the login response

        Raises:
            RuntimeError: If login fails
        """
        if token:
            # Direct token — no credential exchange needed
            self.token_store.save(token)
            self.config.set("auth.token", token, scope="user")
            return {"email": email}

        if not password:
            raise RuntimeError("Password is required for credential-based login")

        api_url = url or self.config.get("api_url")

        # Store the API URL for future requests
        self.config.set("api_url", api_url, scope="user")

        try:
            response = httpx.post(
                f"{api_url}/api/v1/auth/login",
                json={"email": email, "password": password},
                timeout=30,
            )
        except httpx.HTTPError as e:
            raise RuntimeError(f"Could not connect to backend at {api_url}: {e}")

        if response.status_code != 200:
            raise RuntimeError(parse_error(response))

        resp_body: dict[str, Any] = response.json()
        data: dict[str, Any] = resp_body.get("data", {})
        # Backend returns tokens nested: {"tokens": {"accessToken": "..."}}
        access_token = data.get("tokens", {}).get("accessToken")

        if not access_token:
            raise RuntimeError("No access token in response")

        self.token_store.save(access_token)
        self.config.set("auth.token", access_token, scope="user")

        return data

    def logout(self) -> None:
        """Logout and remove stored token."""
        self.token_store.delete()
        self.config.set("auth.token", None, scope="user")

    def get_token(self) -> str | None:
        """Get current authentication token.

        Returns:
            Token if authenticated, None otherwise
        """
        # Try config first
        config_token = self.config.get("auth.token")
        if config_token is not None and isinstance(config_token, str):
            return str(config_token)

        # Try token file
        token = self.token_store.load()
        if token:
            return token

        return None

    def get_api_url(self) -> str:
        """Get the configured API URL.

        Returns:
            API URL string
        """
        return str(self.config.get("api_url"))

    def is_authenticated(self) -> bool:
        """Check if user is authenticated.

        Returns:
            True if authenticated, False otherwise
        """
        return self.get_token() is not None

    def get_current_user(self) -> dict[str, Any] | None:
        """Get current authenticated user info from the backend.

        Returns:
            User info dict if authenticated, None otherwise
        """
        token = self.get_token()
        if not token:
            return None

        api_url = self.get_api_url()
        try:
            response = httpx.get(
                f"{api_url}/api/v1/auth/me",
                headers={"Authorization": f"Bearer {token}"},
                timeout=10,
            )
            if response.status_code == 200:
                body: dict[str, Any] = response.json()
                result: dict[str, Any] | None = body.get("data")
                return result
        except httpx.HTTPError:
            # Network/HTTP error: treat as unauthenticated
            pass

        return None
