"""Token storage — keyring primary, file fallback, env var override."""

from __future__ import annotations

from dataclasses import dataclass, field
import json
import os
import stat
import time
from typing import Any

from turumba_cli.config import TURUMBA_DIR

CREDENTIALS_FILE = TURUMBA_DIR / "credentials"
KEYRING_SERVICE = "turumba"
KEYRING_USERNAME = "default"
ENV_TOKEN = "TURUMBA_ACCESS_TOKEN"
ENV_REFRESH_TOKEN = "TURUMBA_REFRESH_TOKEN"


@dataclass
class TokenSet:
    """Stored authentication tokens."""

    access_token: str
    id_token: str = ""
    refresh_token: str = ""
    expires_at: float = 0.0
    email: str = ""
    extra: dict[str, Any] = field(default_factory=dict)

    @property
    def is_expired(self) -> bool:
        """Check if token is expired or will expire within 5 minutes."""
        if self.expires_at == 0:
            return False
        return time.time() > (self.expires_at - 300)

    def to_dict(self) -> dict[str, Any]:
        return {
            "access_token": self.access_token,
            "id_token": self.id_token,
            "refresh_token": self.refresh_token,
            "expires_at": self.expires_at,
            "email": self.email,
            **self.extra,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> TokenSet:
        known_keys = {"access_token", "id_token", "refresh_token", "expires_at", "email"}
        extra = {k: v for k, v in data.items() if k not in known_keys}
        return cls(
            access_token=data.get("access_token", ""),
            id_token=data.get("id_token", ""),
            refresh_token=data.get("refresh_token", ""),
            expires_at=float(data.get("expires_at", 0)),
            email=data.get("email", ""),
            extra=extra,
        )

    @classmethod
    def from_login_response(cls, response: dict[str, Any], email: str) -> TokenSet:
        """Create from the /v1/auth/login API response."""
        expires_in = response.get("expires_in", 3600)
        return cls(
            access_token=response["access_token"],
            id_token=response.get("id_token", ""),
            refresh_token=response.get("refresh_token", ""),
            expires_at=time.time() + expires_in,
            email=email,
        )


def _try_keyring_save(tokens: TokenSet) -> bool:
    """Try to save tokens using keyring. Returns True on success."""
    try:
        import keyring  # noqa: PLC0415

        keyring.set_password(KEYRING_SERVICE, KEYRING_USERNAME, json.dumps(tokens.to_dict()))
    except Exception:
        return False
    else:
        return True


def _try_keyring_load() -> TokenSet | None:
    """Try to load tokens from keyring."""
    try:
        import keyring  # noqa: PLC0415

        data = keyring.get_password(KEYRING_SERVICE, KEYRING_USERNAME)
        if data:
            return TokenSet.from_dict(json.loads(data))
    except Exception:
        pass
    return None


def _try_keyring_delete() -> bool:
    """Try to delete tokens from keyring."""
    try:
        import keyring  # noqa: PLC0415

        keyring.delete_password(KEYRING_SERVICE, KEYRING_USERNAME)
    except Exception:
        return False
    else:
        return True


def _file_save(tokens: TokenSet) -> None:
    """Save tokens to credentials file with restricted permissions."""
    TURUMBA_DIR.mkdir(parents=True, exist_ok=True)
    CREDENTIALS_FILE.write_text(json.dumps(tokens.to_dict(), indent=2))
    CREDENTIALS_FILE.chmod(stat.S_IRUSR | stat.S_IWUSR)  # 0600


def _file_load() -> TokenSet | None:
    """Load tokens from credentials file."""
    if CREDENTIALS_FILE.exists():
        data = json.loads(CREDENTIALS_FILE.read_text())
        return TokenSet.from_dict(data)
    return None


def _file_delete() -> None:
    """Delete credentials file."""
    if CREDENTIALS_FILE.exists():
        CREDENTIALS_FILE.unlink()


def save_tokens(tokens: TokenSet) -> None:
    """Save tokens using keyring (primary) or file (fallback)."""
    if not _try_keyring_save(tokens):
        _file_save(tokens)


def load_tokens() -> TokenSet | None:
    """Load tokens: env var → keyring → file."""
    env_token = os.environ.get(ENV_TOKEN)
    if env_token:
        return TokenSet(
            access_token=env_token,
            refresh_token=os.environ.get(ENV_REFRESH_TOKEN, ""),
        )

    tokens = _try_keyring_load()
    if tokens:
        return tokens

    return _file_load()


def delete_tokens() -> None:
    """Remove stored tokens from all backends."""
    _try_keyring_delete()
    _file_delete()


def get_access_token() -> str | None:
    """Get the current access token, or None if not authenticated."""
    tokens = load_tokens()
    if tokens is None:
        return None
    return tokens.access_token
