"""Token validation and refresh utilities.

Ensures GitHub OAuth access tokens are valid before use, automatically
refreshing expired tokens via the stored refresh token.
"""

from __future__ import annotations

import logging
import os
from datetime import datetime, timedelta, timezone

import httpx

from peon_mcp.auth.crypto import decrypt_token, encrypt_token

logger = logging.getLogger(__name__)

# Refresh if token expires within this window
_REFRESH_BUFFER = timedelta(minutes=5)

GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token"


async def ensure_valid_token(db, session_token: str) -> str | None:
    """Return a valid encrypted access token for the session, refreshing if needed.

    Returns None if the session is invalid or the token cannot be refreshed.
    """
    row = await db.execute_fetchone(
        "SELECT s.id AS session_id, s.encrypted_access_token, "
        "s.encrypted_refresh_token, s.token_expires_at "
        "FROM user_sessions s "
        "WHERE s.session_token = ? AND s.expires_at > NOW()",
        (session_token,),
    )
    if not row or not row["encrypted_access_token"]:
        return None

    token_expires_at = row["token_expires_at"]

    # If no expiry tracked or token still has time, return as-is
    if token_expires_at is None:
        return row["encrypted_access_token"]

    if token_expires_at.tzinfo is None:
        token_expires_at = token_expires_at.replace(tzinfo=timezone.utc)

    now = datetime.now(timezone.utc)
    if token_expires_at > now + _REFRESH_BUFFER:
        return row["encrypted_access_token"]

    # Token is expired or about to expire — attempt refresh
    if not row["encrypted_refresh_token"]:
        logger.warning("Access token expired but no refresh token available")
        return row["encrypted_access_token"]  # Return stale; caller will get 403

    return await _refresh_token(
        db,
        session_id=row["session_id"],
        encrypted_refresh_token=row["encrypted_refresh_token"],
    )


async def _refresh_token(
    db,
    session_id: int,
    encrypted_refresh_token: str,
) -> str | None:
    """Refresh the GitHub access token and update the session."""
    client_id = os.environ.get("GITHUB_APP_CLIENT_ID", "").strip()
    client_secret = os.environ.get("GITHUB_APP_CLIENT_SECRET", "").strip()
    if not client_id or not client_secret:
        logger.error("Cannot refresh token: GitHub App credentials not configured")
        return None

    refresh_token = decrypt_token(encrypted_refresh_token)

    try:
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                GITHUB_TOKEN_URL,
                data={
                    "client_id": client_id,
                    "client_secret": client_secret,
                    "grant_type": "refresh_token",
                    "refresh_token": refresh_token,
                },
                headers={"Accept": "application/json"},
                timeout=10,
            )
            if resp.status_code != 200:
                logger.error("GitHub token refresh failed: HTTP %s", resp.status_code)
                return None
            data = resp.json()
    except Exception:
        logger.exception("Failed to refresh GitHub token")
        return None

    new_access = data.get("access_token")
    if not new_access:
        error = data.get("error_description", data.get("error", "unknown"))
        logger.error("GitHub token refresh error: %s", error)
        return None

    new_refresh = data.get("refresh_token")
    new_expires_in = data.get("expires_in")

    encrypted_access = encrypt_token(new_access)
    encrypted_refresh = encrypt_token(new_refresh) if new_refresh else encrypted_refresh_token

    new_token_expires_at = None
    if new_expires_in:
        new_token_expires_at = datetime.now(timezone.utc) + timedelta(seconds=int(new_expires_in))

    await db.execute(
        "UPDATE user_sessions SET encrypted_access_token = ?, "
        "encrypted_refresh_token = ?, token_expires_at = ? WHERE id = ?",
        (encrypted_access, encrypted_refresh, new_token_expires_at, session_id),
    )
    await db.commit()

    logger.info("Successfully refreshed GitHub access token for session %s", session_id)
    return encrypted_access
