"""
Refactored Auth Operations with proper 3DS/3DS2 support
Implements SetupIntent-first architecture for secure registration flows
"""

import logging
from typing import Dict, Any
from fastapi import APIRouter, Depends, HTTPException, status, Request
from app.services.auth_service import auth_service, AuthService
from app.schemas.auth import UserRegistration, UserLogin, UserResponse
from app.models.users import User
from shared.exceptions.auth_exceptions import (
    UserCreationError,
    RegistrationError,
    PaymentOrchestrationError
)
from shared.auth.decorators import get_current_user_dep

# Configure logging
logger = logging.getLogger(__name__)

# Create router
router = APIRouter(prefix="/api/v1/auth", tags=["auth"])


async def get_auth_service() -> AuthService:
    """Dependency injection for auth service"""
    return auth_service


@router.post("/start-registration", 
            response_model=Dict[str, Any],
            summary="Start secure registration with payment verification")
async def start_registration(
    user_data: UserRegistration,
    request: Request,
    auth_service: AuthService = Depends(get_auth_service)
) -> Dict[str, Any]:
    """
    Start secure registration flow with SetupIntent-first approach
    
    This is the FIRST step in the atomic registration architecture:
    1. Creates Stripe customer via tenant service
    2. Creates SetupIntent with confirm=True
    3. Returns SetupIntent data to frontend
    
    IMPORTANT: NO subscription or user is created in this step!
    
    Two possible outcomes:
    - requires_action=True: 3DS required, frontend must confirm SetupIntent then call complete-registration
    - requires_action=False: No 3DS required, but frontend STILL must call complete-registration
    
    In BOTH cases, the frontend must call complete-registration to create the subscription and user.
    This ensures consistent flow and prevents duplicate subscriptions.
    
    Args:
        user_data: User registration data with payment info
        
    Returns:
        SetupIntent result with:
        - requires_action: True if 3DS required, False if not
        - setup_intent_id: SetupIntent ID for verification
        - client_secret: For 3DS authentication (when requires_action=True)
        - customer_id: Stripe customer ID
        - Other SetupIntent metadata
        
    Raises:
        HTTPException: 400 for validation errors, 500 for server errors
    """
    try:
        logger.info(f"Starting secure registration flow, email={user_data.email}, plan={user_data.subscription_plan}")
        
        # Validate required fields
        if not user_data.email or not user_data.email.strip():
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Email is required"
            )
        
        if not user_data.password or len(user_data.password) < 8:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Password must be at least 8 characters long"
            )
        
        if not user_data.full_name or not user_data.full_name.strip():
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Full name is required"
            )
        
        if user_data.subscription_plan and not user_data.payment_method_id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Payment method ID is required for subscription registration"
            )
        
        # Start secure registration flow
        result = await auth_service.start_secure_registration_flow(user_data)
        
        # Check if 3DS is required
        if result.get('requires_action', False):
            logger.info(f"Registration requires 3DS verification, email={user_data.email}, setup_intent_id={result.get('setup_intent_id')}")

            return {
                "requires_action": True,
                "action_type": "setup_intent_confirmation",
                "client_secret": result.get('client_secret'),
                "setup_intent_id": result.get('setup_intent_id'),
                "customer_id": result.get('customer_id'),
                "payment_customer_id": result.get('payment_customer_id'),
                "plan_id": result.get('plan_id'),
                "payment_method_id": result.get('payment_method_id'),
                "billing_cycle": result.get('billing_cycle'),
                "coupon_info": result.get('coupon_info'),
                "trial_info": result.get('trial_info'),
                "email": result.get('email'),
                "message": "Payment verification required. Frontend must confirm SetupIntent to handle 3DS."
            }
        else:
            user = result.get('user')
            user_id = user.id if user else None
            logger.info(f"Registration completed without 3DS, email={user_data.email}, user_id={user_id}, subscription_id={result.get('subscription_id')}")

            # Return complete registration result
            user_data_response = None
            if user:
                user_data_response = {
                    "id": str(user.id),
                    "email": user.email,
                    "full_name": user.full_name,
                    "is_active": user.is_active
                }

            return {
                "requires_action": False,
                "setup_intent_id": result.get('setup_intent_id'),
                "user": user_data_response,
                "subscription_id": result.get('subscription_id'),
                "payment_customer_id": result.get('payment_customer_id'),
                "status": result.get('status'),
                "message": "Registration completed successfully"
            }
            
    except RegistrationError as e:
        logger.error(f"Registration flow failed: {str(e)}, email: {user_data.email}",
                    exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Registration failed: {str(e)}"
        ) from e
    except PaymentOrchestrationError as e:
        logger.error(f"Payment orchestration failed: {str(e)}, email: {user_data.email}",
                    exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Payment setup failed: {str(e)}"
        ) from e
    except Exception as e:
        logger.error(f"Unexpected registration error: {str(e)}, email: {user_data.email}",
                    exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Registration error: {str(e)}"
        ) from e


@router.post("/complete-registration", 
            response_model=Dict[str, Any],
            summary="Complete registration after SetupIntent verification")
async def complete_registration(
    verification_data: Dict[str, Any],
    request: Request,
    auth_service: AuthService = Depends(get_auth_service)
) -> Dict[str, Any]:
    """
    Complete registration after frontend confirms SetupIntent
    
    This is the SECOND step in the atomic registration architecture:
    1. Called after frontend confirms SetupIntent (with or without 3DS)
    2. Verifies SetupIntent status with Stripe
    3. Creates subscription with verified payment method (FIRST time subscription is created)
    4. Creates user record in auth database
    5. Saves onboarding progress
    6. Generates auth tokens for auto-login
    
    This endpoint is called in TWO scenarios:
    1. After user completes 3DS authentication (requires_action=True flow)
    2. Immediately after start-registration (requires_action=False flow)
    
    In BOTH cases, this is where the subscription and user are actually created.
    This ensures consistent flow and prevents duplicate subscriptions.
    
    Args:
        verification_data: Must contain:
            - setup_intent_id: Verified SetupIntent ID
            - user_data: Original user registration data
        
    Returns:
        Complete registration result with:
        - user: Created user data
        - subscription_id: Created subscription ID
        - payment_customer_id: Stripe customer ID
        - access_token: JWT access token
        - refresh_token: JWT refresh token
        
    Raises:
        HTTPException: 400 if setup_intent_id is missing, 500 for server errors
    """
    try:
        # Validate required fields
        if not verification_data.get('setup_intent_id'):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="SetupIntent ID is required"
            )
        
        if not verification_data.get('user_data'):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="User data is required"
            )
        
        # Extract user data
        user_data_dict = verification_data['user_data']
        user_data = UserRegistration(**user_data_dict)
        
        logger.info(f"Completing registration after SetupIntent verification, email={user_data.email}, setup_intent_id={verification_data['setup_intent_id']}")
        
        # Complete registration with verified payment
        result = await auth_service.complete_registration_with_verified_payment(
            verification_data['setup_intent_id'],
            user_data
        )
        
        logger.info(f"Registration completed successfully after 3DS, user_id={result['user'].id}, email={result['user'].email}, subscription_id={result.get('subscription_id')}")
        
        return {
            "success": True,
            "user": {
                "id": str(result['user'].id),
                "email": result['user'].email,
                "full_name": result['user'].full_name,
                "is_active": result['user'].is_active,
                "is_verified": result['user'].is_verified,
                "created_at": result['user'].created_at.isoformat() if result['user'].created_at else None,
                "role": result['user'].role
            },
            "subscription_id": result.get('subscription_id'),
            "payment_customer_id": result.get('payment_customer_id'),
            "status": result.get('status'),
            "access_token": result.get('access_token'),
            "refresh_token": result.get('refresh_token'),
            "message": "Registration completed successfully after 3DS verification"
        }

    except RegistrationError as e:
        logger.error(f"Registration completion after 3DS failed: {str(e)}, setup_intent_id: {verification_data.get('setup_intent_id')}, email: {user_data_dict.get('email') if user_data_dict else 'unknown'}",
                    exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Registration completion failed: {str(e)}"
        ) from e
    except Exception as e:
        logger.error(f"Unexpected registration completion error: {str(e)}, setup_intent_id: {verification_data.get('setup_intent_id')}",
                    exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Registration completion error: {str(e)}"
        ) from e


@router.post("/login", 
            response_model=Dict[str, Any],
            summary="User login with subscription validation")
async def login(
    login_data: UserLogin,
    request: Request,
    auth_service: AuthService = Depends(get_auth_service)
) -> Dict[str, Any]:
    """
    User login endpoint with subscription validation
    
    This endpoint:
    1. Validates user credentials
    2. Checks if user has active subscription (if required)
    3. Returns authentication tokens
    4. Updates last login timestamp
    
    Args:
        login_data: User login credentials (email and password)
        
    Returns:
        Authentication tokens and user information
        
    Raises:
        HTTPException: 401 for invalid credentials, 403 for subscription issues
    """
    try:
        logger.info(f"Login attempt, email={login_data.email}")
        
        # Validate required fields
        if not login_data.email or not login_data.email.strip():
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Email is required"
            )
        
        if not login_data.password or len(login_data.password) < 8:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Password must be at least 8 characters long"
            )
        
        # Call auth service to perform login
        result = await auth_service.login_user(login_data)
        
        logger.info(f"Login successful, email={login_data.email}, user_id={result['user'].id}")
        
        # Extract tokens from result for top-level response
        tokens = result.get('tokens', {})
        
        return {
            "success": True,
            "access_token": tokens.get('access_token'),
            "refresh_token": tokens.get('refresh_token'),
            "token_type": tokens.get('token_type'),
            "expires_in": tokens.get('expires_in'),
            "user": {
                "id": str(result['user'].id),
                "email": result['user'].email,
                "full_name": result['user'].full_name,
                "is_active": result['user'].is_active,
                "last_login": result['user'].last_login.isoformat() if result['user'].last_login else None
            },
            "subscription": result.get('subscription', {}),
            "message": "Login successful"
        }
        
    except HTTPException:
        # Re-raise HTTP exceptions (like 401 for invalid credentials)
        raise
    except Exception as e:
        logger.error(f"Login failed: {str(e)}, email: {login_data.email}",
                    exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Login failed: {str(e)}"
        ) from e





# ============================================================================
# TOKEN MANAGEMENT ENDPOINTS - NEWLY ADDED
# ============================================================================

@router.post("/refresh",
            response_model=Dict[str, Any],
            summary="Refresh access token using refresh token")
async def refresh_token(
    request: Request,
    refresh_data: Dict[str, Any],
    auth_service: AuthService = Depends(get_auth_service)
) -> Dict[str, Any]:
    """
    Refresh access token using a valid refresh token
    
    This endpoint:
    1. Validates the refresh token
    2. Generates new access and refresh tokens
    3. Returns the new tokens
    
    Args:
        refresh_data: Dictionary containing refresh_token
        
    Returns:
        New authentication tokens
        
    Raises:
        HTTPException: 401 for invalid refresh tokens
    """
    try:
        logger.info("Token refresh request initiated")
        
        # Extract refresh token from request
        refresh_token = refresh_data.get("refresh_token")
        if not refresh_token:
            logger.warning("Refresh token missing from request")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Refresh token is required"
            )
        
        # Use service layer to refresh tokens
        tokens = await auth_service.refresh_auth_tokens(refresh_token)
        
        logger.info("Token refresh successful via service layer")
        
        return {
            "success": True,
            "access_token": tokens.get("access_token"),
            "refresh_token": tokens.get("refresh_token"),
            "token_type": "bearer",
            "expires_in": 1800,  # 30 minutes
            "message": "Token refresh successful"
        }
        
    except HTTPException:
        # Re-raise HTTP exceptions
        raise
    except Exception as e:
        logger.error(f"Token refresh failed: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Token refresh failed: {str(e)}"
        ) from e


@router.post("/verify",
            response_model=Dict[str, Any],
            summary="Verify token validity")
async def verify_token(
    request: Request,
    token_data: Dict[str, Any]
) -> Dict[str, Any]:
    """
    Verify the validity of an access token
    
    Args:
        token_data: Dictionary containing access_token
        
    Returns:
        Token validation result
    """
    try:
        logger.info("Token verification request initiated")
        
        # Extract access token from request
        access_token = token_data.get("access_token")
        if not access_token:
            logger.warning("Access token missing from verification request")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Access token is required"
            )
        
        # Use service layer to verify token
        result = await auth_service.verify_access_token(access_token)
        
        logger.info("Token verification successful via service layer")
        
        return {
            "success": True,
            "valid": result.get("valid"),
            "user_id": result.get("user_id"),
            "email": result.get("email"),
            "message": "Token is valid"
        }
        
    except HTTPException:
        # Re-raise HTTP exceptions
        raise
    except Exception as e:
        logger.error(f"Token verification failed: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Token verification failed: {str(e)}"
        ) from e


@router.post("/logout",
            response_model=Dict[str, Any],
            summary="Logout and revoke refresh token")
async def logout(
    request: Request,
    logout_data: Dict[str, Any],
    auth_service: AuthService = Depends(get_auth_service)
) -> Dict[str, Any]:
    """
    Logout user and revoke refresh token
    
    Args:
        logout_data: Dictionary containing refresh_token
        
    Returns:
        Logout confirmation
    """
    try:
        logger.info("Logout request initiated")
        
        # Extract refresh token from request
        refresh_token = logout_data.get("refresh_token")
        if not refresh_token:
            logger.warning("Refresh token missing from logout request")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Refresh token is required"
            )
        
        # Use service layer to revoke refresh token
        try:
            await auth_service.revoke_refresh_token(refresh_token)
            logger.info("Logout successful via service layer")
            
            return {
                "success": True,
                "message": "Logout successful"
            }
            
        except Exception as e:
            logger.error(f"Error during logout: {str(e)}")
            # Don't fail logout if revocation fails
            return {
                "success": True,
                "message": "Logout successful (token revocation failed but user logged out)"
            }
            
    except HTTPException:
        # Re-raise HTTP exceptions
        raise
    except Exception as e:
        logger.error(f"Logout failed: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Logout failed: {str(e)}"
        ) from e


@router.post("/change-password",
            response_model=Dict[str, Any],
            summary="Change user password")
async def change_password(
    request: Request,
    password_data: Dict[str, Any],
    auth_service: AuthService = Depends(get_auth_service)
) -> Dict[str, Any]:
    """
    Change user password
    
    Args:
        password_data: Dictionary containing current_password and new_password
        
    Returns:
        Password change confirmation
    """
    try:
        logger.info("Password change request initiated")
        
        # Extract user from request state
        if not hasattr(request.state, 'user') or not request.state.user:
            logger.warning("Unauthorized password change attempt - no user context")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Authentication required"
            )
        
        user_id = request.state.user.get("user_id")
        if not user_id:
            logger.warning("Unauthorized password change attempt - no user_id")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid user context"
            )
        
        # Extract password data
        current_password = password_data.get("current_password")
        new_password = password_data.get("new_password")
        
        if not current_password or not new_password:
            logger.warning("Password change missing required fields")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Current password and new password are required"
            )
        
        if len(new_password) < 8:
            logger.warning("New password too short")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="New password must be at least 8 characters long"
            )
        
        # Use service layer to change password
        await auth_service.change_user_password(user_id, current_password, new_password)
        
        logger.info(f"Password change successful via service layer, user_id={user_id}")
        
        return {
            "success": True,
            "message": "Password changed successfully"
        }
            
    except HTTPException:
        # Re-raise HTTP exceptions
        raise
    except Exception as e:
        logger.error(f"Password change failed: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Password change failed: {str(e)}"
        ) from e


@router.post("/verify-email",
            response_model=Dict[str, Any],
            summary="Verify user email")
async def verify_email(
    request: Request,
    email_data: Dict[str, Any],
    auth_service: AuthService = Depends(get_auth_service)
) -> Dict[str, Any]:
    """
    Verify user email (placeholder implementation)
    
    Args:
        email_data: Dictionary containing email and verification_token
        
    Returns:
        Email verification confirmation
    """
    try:
        logger.info("Email verification request initiated")
        
        # Extract email and token
        email = email_data.get("email")
        verification_token = email_data.get("verification_token")
        
        if not email or not verification_token:
            logger.warning("Email verification missing required fields")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Email and verification token are required"
            )
        
        # Use service layer to verify email
        await auth_service.verify_user_email(email, verification_token)
        
        logger.info("Email verification successful via service layer")
        
        return {
            "success": True,
            "message": "Email verified successfully"
        }
            
    except HTTPException:
        # Re-raise HTTP exceptions
        raise
    except Exception as e:
        logger.error(f"Email verification failed: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Email verification failed: {str(e)}"
        ) from e


