"""
Tool_OCR - External Authentication Router (V2)
Handles authentication via external Microsoft Azure AD API
"""

from datetime import datetime, timedelta
import logging
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, status, Request
from sqlalchemy.orm import Session

from app.core.config import settings
from app.core.deps import get_db, get_current_user
from app.core.security import create_access_token
from app.models.user import User
from app.models.session import Session as UserSession
from app.schemas.auth import LoginRequest, Token, UserResponse
from app.services.external_auth_service import external_auth_service

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v2/auth", tags=["Authentication V2"])


def get_client_ip(request: Request) -> str:
    """Extract client IP address from request"""
    # Check X-Forwarded-For header (for proxies)
    forwarded = request.headers.get("X-Forwarded-For")
    if forwarded:
        return forwarded.split(",")[0].strip()
    # Check X-Real-IP header
    real_ip = request.headers.get("X-Real-IP")
    if real_ip:
        return real_ip
    # Fallback to direct client
    return request.client.host if request.client else "unknown"


def get_user_agent(request: Request) -> str:
    """Extract user agent from request"""
    return request.headers.get("User-Agent", "unknown")[:500]


@router.post("/login", response_model=Token, summary="External API login")
async def login(
    login_data: LoginRequest,
    request: Request,
    db: Session = Depends(get_db)
):
    """
    User login via external Microsoft Azure AD API

    Returns JWT access token and stores session information

    - **username**: User's email address
    - **password**: User's password
    """
    # Call external authentication API
    success, auth_response, error_msg = await external_auth_service.authenticate_user(
        username=login_data.username,
        password=login_data.password
    )

    if not success or not auth_response:
        logger.warning(
            f"External auth failed for user {login_data.username}: {error_msg}"
        )
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=error_msg or "Authentication failed",
            headers={"WWW-Authenticate": "Bearer"},
        )

    # Extract user info from external API response
    user_info = auth_response.user_info
    email = user_info.email
    display_name = user_info.name

    # Find or create user in database
    user = db.query(User).filter(User.email == email).first()

    if not user:
        # Create new user
        user = User(
            email=email,
            display_name=display_name,
            is_active=True,
            last_login=datetime.utcnow()
        )
        db.add(user)
        db.commit()
        db.refresh(user)
        logger.info(f"Created new user: {email} (ID: {user.id})")
    else:
        # Update existing user
        user.display_name = display_name
        user.last_login = datetime.utcnow()

        # Check if user is active
        if not user.is_active:
            logger.warning(f"Inactive user login attempt: {email}")
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="User account is inactive"
            )

        db.commit()
        db.refresh(user)
        logger.info(f"Updated existing user: {email} (ID: {user.id})")

    # Parse token expiration
    try:
        expires_at = datetime.fromisoformat(auth_response.expires_at.replace('Z', '+00:00'))
        issued_at = datetime.fromisoformat(auth_response.issued_at.replace('Z', '+00:00'))
    except Exception as e:
        logger.error(f"Failed to parse token timestamps: {e}")
        expires_at = datetime.utcnow() + timedelta(seconds=auth_response.expires_in)
        issued_at = datetime.utcnow()

    # Create session in database
    # TODO: Implement token encryption before storing
    session = UserSession(
        user_id=user.id,
        access_token=auth_response.access_token,  # Should be encrypted
        id_token=auth_response.id_token,  # Should be encrypted
        token_type=auth_response.token_type,
        expires_at=expires_at,
        issued_at=issued_at,
        ip_address=get_client_ip(request),
        user_agent=get_user_agent(request)
    )
    db.add(session)
    db.commit()
    db.refresh(session)

    logger.info(
        f"Created session {session.id} for user {user.email} "
        f"(expires: {expires_at})"
    )

    # Create internal JWT token for API access
    # This token contains user ID and session ID
    internal_token_expires = timedelta(minutes=settings.access_token_expire_minutes)
    internal_access_token = create_access_token(
        data={
            "sub": str(user.id),
            "email": user.email,
            "session_id": session.id
        },
        expires_delta=internal_token_expires
    )

    return {
        "access_token": internal_access_token,
        "token_type": "bearer",
        "expires_in": int(internal_token_expires.total_seconds()),
        "user": {
            "id": user.id,
            "email": user.email,
            "display_name": user.display_name
        }
    }


@router.post("/logout", summary="User logout")
async def logout(
    session_id: Optional[int] = None,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    User logout - invalidates session

    - **session_id**: Session ID to logout (optional, logs out all if not provided)
    """
    # TODO: Implement proper current_user dependency from JWT token
    # For now, this is a placeholder

    if session_id:
        # Logout specific session
        session = db.query(UserSession).filter(
            UserSession.id == session_id,
            UserSession.user_id == current_user.id
        ).first()

        if session:
            db.delete(session)
            db.commit()
            logger.info(f"Logged out session {session_id} for user {current_user.email}")
            return {"message": "Logged out successfully"}
        else:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Session not found"
            )
    else:
        # Logout all sessions
        sessions = db.query(UserSession).filter(
            UserSession.user_id == current_user.id
        ).all()

        count = len(sessions)
        for session in sessions:
            db.delete(session)

        db.commit()
        logger.info(f"Logged out all {count} sessions for user {current_user.email}")
        return {"message": f"Logged out {count} sessions"}


@router.get("/me", response_model=UserResponse, summary="Get current user")
async def get_me(
    current_user: User = Depends(get_current_user)
):
    """
    Get current authenticated user information
    """
    # TODO: Implement proper current_user dependency from JWT token
    return {
        "id": current_user.id,
        "email": current_user.email,
        "display_name": current_user.display_name,
        "created_at": current_user.created_at,
        "last_login": current_user.last_login,
        "is_active": current_user.is_active
    }


@router.get("/sessions", summary="List user sessions")
async def list_sessions(
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    List all active sessions for current user
    """
    sessions = db.query(UserSession).filter(
        UserSession.user_id == current_user.id
    ).order_by(UserSession.created_at.desc()).all()

    return {
        "sessions": [
            {
                "id": s.id,
                "token_type": s.token_type,
                "expires_at": s.expires_at,
                "issued_at": s.issued_at,
                "ip_address": s.ip_address,
                "user_agent": s.user_agent,
                "created_at": s.created_at,
                "last_accessed_at": s.last_accessed_at,
                "is_expired": s.is_expired,
                "time_until_expiry": s.time_until_expiry
            }
            for s in sessions
        ]
    }


@router.post("/refresh", response_model=Token, summary="Refresh access token")
async def refresh_token(
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    Refresh access token before expiration

    Re-authenticates with external API using stored session.
    Note: Since external API doesn't provide refresh tokens,
    we re-issue internal JWT tokens with extended expiry.
    """
    try:
        # Find user's most recent session
        session = db.query(UserSession).filter(
            UserSession.user_id == current_user.id
        ).order_by(UserSession.created_at.desc()).first()

        if not session:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="No active session found"
            )

        # Check if token is expiring soon (within TOKEN_REFRESH_BUFFER)
        if not external_auth_service.is_token_expiring_soon(session.expires_at):
            # Token still valid for a while, just issue new internal JWT
            internal_token_expires = timedelta(minutes=settings.access_token_expire_minutes)
            internal_access_token = create_access_token(
                data={
                    "sub": str(current_user.id),
                    "email": current_user.email,
                    "session_id": session.id
                },
                expires_delta=internal_token_expires
            )

            logger.info(f"Refreshed internal token for user {current_user.email}")

            return {
                "access_token": internal_access_token,
                "token_type": "bearer",
                "expires_in": int(internal_token_expires.total_seconds()),
                "user": {
                    "id": current_user.id,
                    "email": current_user.email,
                    "display_name": current_user.display_name
                }
            }

        # External token expiring soon - would need re-authentication
        # For now, we extend internal token and log a warning
        logger.warning(
            f"External token expiring soon for user {current_user.email}. "
            "User should re-authenticate."
        )

        internal_token_expires = timedelta(minutes=settings.access_token_expire_minutes)
        internal_access_token = create_access_token(
            data={
                "sub": str(current_user.id),
                "email": current_user.email,
                "session_id": session.id
            },
            expires_delta=internal_token_expires
        )

        return {
            "access_token": internal_access_token,
            "token_type": "bearer",
            "expires_in": int(internal_token_expires.total_seconds()),
            "user": {
                "id": current_user.id,
                "email": current_user.email,
                "display_name": current_user.display_name
            }
        }

    except HTTPException:
        raise
    except Exception as e:
        logger.exception(f"Token refresh failed for user {current_user.id}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Token refresh failed: {str(e)}"
        )
