"""Configuration and credential management for Navari CLI."""

import json
import os
from pathlib import Path

import msal

from navari_cli.constants import DEFAULT_FORMAT


class NavariConfig:
    """Manage CLI configuration and credentials.

    Credentials are keyed by server URL (e.g. "https://navari.example.com").
    Each entry stores the JWT token, username, and cached auth config from the server.
    """

    CONFIG_DIR = Path.home() / ".navari"
    CREDENTIALS_FILE = CONFIG_DIR / "credentials.json"
    CONFIG_FILE = CONFIG_DIR / "config.json"
    MSAL_CACHE_FILE = CONFIG_DIR / "msal_cache.json"

    @classmethod
    def _ensure_config_dir(cls) -> None:
        """Create config directory if it doesn't exist."""
        cls.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
        if os.name != "nt":
            os.chmod(cls.CONFIG_DIR, 0o700)

    @classmethod
    def save_credentials(cls, server_url: str, token: str, user: str, auth_config: dict | None = None) -> None:
        """Store JWT token and auth config for a server.

        Args:
            server_url: Server base URL
            token: JWT access token
            user: User email/username
            auth_config: OAuth config from server (cached for future logins)
        """
        cls._ensure_config_dir()

        credentials = {}
        if cls.CREDENTIALS_FILE.exists():
            try:
                credentials = json.loads(cls.CREDENTIALS_FILE.read_text())
            except json.JSONDecodeError:
                pass

        # Preserve existing settings (like verify_ssl) when updating credentials
        entry = credentials.get(server_url, {})
        entry["token"] = token
        entry["user"] = user
        if auth_config:
            entry["auth_config"] = auth_config

        credentials[server_url] = entry
        cls.CREDENTIALS_FILE.write_text(json.dumps(credentials, indent=2))

        if os.name != "nt":
            os.chmod(cls.CREDENTIALS_FILE, 0o600)

    @classmethod
    def load_credentials(cls, server_url: str) -> tuple[str, str]:
        """Load token and user for a server.

        Args:
            server_url: Server base URL

        Returns:
            Tuple of (token, user)

        Raises:
            FileNotFoundError: If credentials file doesn't exist
            KeyError: If server not found in credentials
        """
        if not cls.CREDENTIALS_FILE.exists():
            raise FileNotFoundError("No credentials found. Run: navari login <server-url>")

        credentials = json.loads(cls.CREDENTIALS_FILE.read_text())

        if server_url not in credentials:
            raise KeyError(f"No credentials for '{server_url}'. Run: navari login {server_url}")

        creds = credentials[server_url]
        return creds["token"], creds["user"]

    @classmethod
    def load_auth_config(cls, server_url: str) -> dict | None:
        """Load cached auth config for a server.

        Args:
            server_url: Server base URL

        Returns:
            Auth config dict or None if not cached
        """
        if not cls.CREDENTIALS_FILE.exists():
            return None

        try:
            credentials = json.loads(cls.CREDENTIALS_FILE.read_text())
            return credentials.get(server_url, {}).get("auth_config")
        except json.JSONDecodeError:
            return None

    @classmethod
    def set_server_option(cls, server_url: str, key: str, value: object) -> None:
        """Save a per-server option (e.g. verify_ssl) alongside credentials."""
        cls._ensure_config_dir()
        credentials = {}
        if cls.CREDENTIALS_FILE.exists():
            try:
                credentials = json.loads(cls.CREDENTIALS_FILE.read_text())
            except json.JSONDecodeError:
                pass
        entry = credentials.get(server_url, {})
        entry[key] = value
        credentials[server_url] = entry
        cls.CREDENTIALS_FILE.write_text(json.dumps(credentials, indent=2))

    @classmethod
    def get_server_option(cls, server_url: str, key: str, default: object = None) -> object:
        """Load a per-server option."""
        if not cls.CREDENTIALS_FILE.exists():
            return default
        try:
            credentials = json.loads(cls.CREDENTIALS_FILE.read_text())
            return credentials.get(server_url, {}).get(key, default)
        except json.JSONDecodeError:
            return default

    @classmethod
    def delete_credentials(cls, server_url: str | None = None) -> None:
        """Delete credentials for a server (or all if server_url is None).

        Args:
            server_url: Server URL, or None to delete all credentials
        """
        if not cls.CREDENTIALS_FILE.exists():
            return

        if server_url is None:
            cls.CREDENTIALS_FILE.unlink()
            return

        credentials = json.loads(cls.CREDENTIALS_FILE.read_text())
        if server_url in credentials:
            del credentials[server_url]
            if credentials:
                cls.CREDENTIALS_FILE.write_text(json.dumps(credentials, indent=2))
            else:
                cls.CREDENTIALS_FILE.unlink()

    @classmethod
    def load_config(cls) -> dict:
        """Load user configuration."""
        if not cls.CONFIG_FILE.exists():
            return {
                "default_format": DEFAULT_FORMAT,
                "color_output": True,
            }

        try:
            config = json.loads(cls.CONFIG_FILE.read_text())
            config.setdefault("default_format", DEFAULT_FORMAT)
            config.setdefault("color_output", True)
            return config
        except json.JSONDecodeError:
            return {
                "default_format": DEFAULT_FORMAT,
                "color_output": True,
            }

    @classmethod
    def set_last_used_server(cls, server_url: str | None) -> None:
        """Save the last used server URL.

        Args:
            server_url: Server URL to remember, or None to clear
        """
        cls._ensure_config_dir()
        config = cls.load_config()
        if server_url is None:
            config.pop("last_used_server", None)
        else:
            config["last_used_server"] = server_url
        cls.CONFIG_FILE.write_text(json.dumps(config, indent=2))

    @classmethod
    def get_last_used_server(cls) -> str | None:
        """Get the last used server URL.

        Returns:
            Last used server URL, or None if never set
        """
        config = cls.load_config()
        return config.get("last_used_server")

    @classmethod
    def list_saved_servers(cls) -> list[str]:
        """List servers with saved credentials.

        Returns:
            List of server URLs
        """
        if not cls.CREDENTIALS_FILE.exists():
            return []

        try:
            credentials = json.loads(cls.CREDENTIALS_FILE.read_text())
            return list(credentials.keys())
        except json.JSONDecodeError:
            return []

    # MSAL token cache

    @classmethod
    def load_msal_cache(cls) -> msal.SerializableTokenCache:
        """Load MSAL token cache from disk.

        Returns a SerializableTokenCache, loading persisted data if available.
        The cache contains refresh tokens that enable silent token refresh.
        """
        cache = msal.SerializableTokenCache()
        if cls.MSAL_CACHE_FILE.exists():
            try:
                cache.deserialize(cls.MSAL_CACHE_FILE.read_text())
            except (json.JSONDecodeError, ValueError):
                pass  # Start with empty cache if file is corrupted
        return cache

    @classmethod
    def save_msal_cache(cls, cache: msal.SerializableTokenCache) -> None:
        """Persist MSAL token cache to disk."""
        if cache.has_state_changed:
            cls._ensure_config_dir()
            cls.MSAL_CACHE_FILE.write_text(cache.serialize())
            if os.name != "nt":
                os.chmod(cls.MSAL_CACHE_FILE, 0o600)
