import logging
from typing import Any

import httpx
from fastapi import APIRouter, HTTPException, Header
from pydantic import BaseModel

from app.config import get_settings
from app.utils.auth import create_token, verify_token
from jose import JWTError

logger = logging.getLogger(f"{__name__}.auth")

router = APIRouter()
settings = get_settings()


# ============================================================================
# Request/Response Models
# ============================================================================


class DiscordCallbackRequest(BaseModel):
    """Request model for Discord OAuth callback"""

    code: str
    state: str


class DiscordUser(BaseModel):
    """Discord user information"""

    id: str
    username: str
    discriminator: str
    avatar: str | None = None
    email: str | None = None


class AuthResponse(BaseModel):
    """Response model for successful authentication"""

    access_token: str
    refresh_token: str
    expires_in: int
    token_type: str = "bearer"
    user: DiscordUser


class RefreshRequest(BaseModel):
    """Request model for token refresh"""

    refresh_token: str


class RefreshResponse(BaseModel):
    """Response model for token refresh"""

    access_token: str
    expires_in: int
    token_type: str = "bearer"


class UserInfoResponse(BaseModel):
    """Response model for /me endpoint"""

    user: DiscordUser
    teams: list[dict[str, Any]] = []


# ============================================================================
# Discord OAuth Helpers
# ============================================================================


async def exchange_code_for_token(code: str) -> dict[str, Any]:
    """
    Exchange Discord OAuth code for access token

    Args:
        code: OAuth authorization code from Discord

    Returns:
        Discord OAuth token response

    Raises:
        HTTPException: If exchange fails
    """
    data = {
        "client_id": settings.discord_client_id,
        "client_secret": settings.discord_client_secret,
        "grant_type": "authorization_code",
        "code": code,
        "redirect_uri": settings.discord_redirect_uri,
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.post(
                "https://discord.com/api/oauth2/token",
                data=data,
                headers={"Content-Type": "application/x-www-form-urlencoded"},
            )
            response.raise_for_status()
            return response.json()
        except httpx.HTTPStatusError as e:
            logger.error(f"Discord token exchange failed: {e}")
            logger.error(f"Response: {e.response.text}")
            raise HTTPException(
                status_code=400, detail="Failed to exchange code for token"
            )


async def get_discord_user(access_token: str) -> DiscordUser:
    """
    Get Discord user information using access token

    Args:
        access_token: Discord OAuth access token

    Returns:
        Discord user information

    Raises:
        HTTPException: If request fails
    """
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(
                "https://discord.com/api/users/@me",
                headers={"Authorization": f"Bearer {access_token}"},
            )
            response.raise_for_status()
            user_data = response.json()
            return DiscordUser(**user_data)
        except httpx.HTTPStatusError as e:
            logger.error(f"Failed to get Discord user: {e}")
            raise HTTPException(status_code=400, detail="Failed to get user information")


# ============================================================================
# Auth Endpoints
# ============================================================================


@router.post("/discord/callback", response_model=AuthResponse)
async def discord_callback(request: DiscordCallbackRequest):
    """
    Handle Discord OAuth callback

    Exchange authorization code for Discord token, get user info,
    and create our JWT tokens.

    Args:
        request: OAuth callback data (code and state)

    Returns:
        JWT tokens and user information
    """
    try:
        # Exchange code for Discord access token
        logger.info("Exchanging Discord code for token")
        discord_token_data = await exchange_code_for_token(request.code)

        # Get Discord user information
        logger.info("Fetching Discord user information")
        discord_user = await get_discord_user(discord_token_data["access_token"])

        # Create JWT tokens for our application
        user_payload = {
            "user_id": discord_user.id,
            "username": discord_user.username,
            "discord_id": discord_user.id,
        }

        access_token = create_token(user_payload)

        # Create refresh token with longer expiration
        refresh_token_payload = {**user_payload, "type": "refresh"}
        refresh_token = create_token(refresh_token_payload)

        logger.info(f"User {discord_user.username} authenticated successfully")

        return AuthResponse(
            access_token=access_token,
            refresh_token=refresh_token,
            expires_in=604800,  # 7 days in seconds
            user=discord_user,
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Discord OAuth callback error: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail="Authentication failed")


@router.post("/refresh", response_model=RefreshResponse)
async def refresh_access_token(request: RefreshRequest):
    """
    Refresh JWT access token using refresh token

    Args:
        request: Refresh token

    Returns:
        New access token
    """
    try:
        # Verify refresh token
        payload = verify_token(request.refresh_token)

        # Check if it's a refresh token
        if payload.get("type") != "refresh":
            raise HTTPException(status_code=400, detail="Invalid refresh token")

        # Create new access token
        user_payload = {
            "user_id": payload["user_id"],
            "username": payload["username"],
            "discord_id": payload["discord_id"],
        }

        access_token = create_token(user_payload)

        logger.info(f"Token refreshed for user {payload['username']}")

        return RefreshResponse(
            access_token=access_token,
            expires_in=604800,  # 7 days in seconds
        )

    except JWTError:
        logger.warning("Invalid refresh token provided")
        raise HTTPException(status_code=401, detail="Invalid or expired refresh token")
    except Exception as e:
        logger.error(f"Token refresh error: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to refresh token")


@router.get("/me", response_model=UserInfoResponse)
async def get_current_user_info(authorization: str = Header(None)):
    """
    Get current authenticated user information

    Args:
        authorization: Bearer token in Authorization header

    Returns:
        User information and teams
    """
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(
            status_code=401, detail="Missing or invalid authorization header"
        )

    token = authorization.split(" ")[1]

    try:
        # Verify token
        payload = verify_token(token)

        # Create user info
        user = DiscordUser(
            id=payload["discord_id"],
            username=payload["username"],
            discriminator="0",  # Discord removed discriminators
        )

        # TODO: Load user's teams from database
        teams = []

        logger.info(f"User info retrieved for {user.username}")

        return UserInfoResponse(user=user, teams=teams)

    except JWTError:
        logger.warning("Invalid token in /me request")
        raise HTTPException(status_code=401, detail="Invalid or expired token")
    except Exception as e:
        logger.error(f"Get user info error: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to get user information")


@router.get("/verify")
async def verify_auth(authorization: str = Header(None)):
    """
    Verify authentication status

    Args:
        authorization: Bearer token in Authorization header

    Returns:
        Authentication status
    """
    if not authorization or not authorization.startswith("Bearer "):
        return {"authenticated": False}

    token = authorization.split(" ")[1]

    try:
        payload = verify_token(token)
        return {
            "authenticated": True,
            "user_id": payload["user_id"],
            "username": payload["username"],
        }
    except JWTError:
        return {"authenticated": False}
