"""
MFA API Views

API endpoints for Two-Factor Authentication management:
- Setup/disable MFA
- Send/verify SMS codes
- Setup/verify TOTP (authenticator app)
- Generate/verify backup codes
- MFA challenge during login
"""

import logging
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response

from .models import MFAVerificationCode, TrustedDevice
from .mfa_services import mfa_manager

logger = logging.getLogger(__name__)


# ============================================================================
# MFA STATUS
# ============================================================================

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def mfa_status(request):
    """
    Get current MFA status for the authenticated user.

    Returns:
        - mfa_enabled: bool
        - mfa_method: str
        - methods: list of available methods
        - phone_last_4: str or null
        - backup_codes_count: int
        - backup_codes_generated_at: datetime or null
        - trusted_devices_count: int
    """
    user = request.user

    response_data = {
        'mfa_enabled': user.mfa_enabled,
        'mfa_method': user.mfa_method,
        'methods': mfa_manager.get_available_methods(user),
        'phone_last_4': user.phone[-4:] if user.phone and len(user.phone) >= 4 else None,
        'phone_verified': user.phone_verified,
        'totp_verified': user.totp_verified,
        'backup_codes_count': len(user.mfa_backup_codes) if user.mfa_backup_codes else 0,
        'backup_codes_generated_at': user.mfa_backup_codes_generated_at,
        'trusted_devices_count': TrustedDevice.objects.filter(user=user).count(),
    }

    return Response(response_data)


# ============================================================================
# SMS SETUP
# ============================================================================

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def send_phone_verification(request):
    """
    Send a verification code to verify phone number for SMS MFA.

    Request body:
        - phone: str (phone number to verify)

    Returns:
        - success: bool
        - message: str
    """
    user = request.user
    phone = request.data.get('phone')

    if not phone:
        return Response(
            {'error': 'Phone number is required'},
            status=status.HTTP_400_BAD_REQUEST
        )

    # Update phone number (not verified yet)
    user.phone = phone
    user.phone_verified = False
    user.save(update_fields=['phone', 'phone_verified'])

    # Send verification code
    success, message = mfa_manager.send_sms_code(user, purpose='PHONE_VERIFY')

    if success:
        return Response({
            'success': True,
            'message': 'Verification code sent'
        })
    else:
        return Response(
            {'error': message},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def verify_phone(request):
    """
    Verify phone number with the code sent via SMS.

    Request body:
        - code: str (6-digit code)

    Returns:
        - success: bool
        - message: str
    """
    user = request.user
    code = request.data.get('code', '').strip()

    if not code or len(code) != 6:
        return Response(
            {'error': 'Invalid code format'},
            status=status.HTTP_400_BAD_REQUEST
        )

    # Find the verification code
    verification = MFAVerificationCode.objects.filter(
        user=user,
        purpose=MFAVerificationCode.Purpose.PHONE_VERIFY,
        used=False
    ).order_by('-created_at').first()

    if not verification:
        return Response(
            {'error': 'No pending verification. Request a new code.'},
            status=status.HTTP_400_BAD_REQUEST
        )

    if verification.verify(code):
        # Mark phone as verified
        user.phone_verified = True
        user.save(update_fields=['phone_verified'])

        return Response({
            'success': True,
            'message': 'Phone number verified'
        })
    else:
        remaining = 5 - verification.attempts
        return Response(
            {'error': f'Invalid code. {remaining} attempts remaining.'},
            status=status.HTTP_400_BAD_REQUEST
        )


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def enable_sms_mfa(request):
    """
    Enable SMS as MFA method.
    Requires phone to be verified first.

    Returns:
        - success: bool
        - backup_codes: list (if first time enabling MFA)
    """
    user = request.user

    if not user.phone_verified:
        return Response(
            {'error': 'Phone number must be verified first'},
            status=status.HTTP_400_BAD_REQUEST
        )

    # Update MFA settings
    is_first_mfa = not user.mfa_enabled
    user.mfa_enabled = True

    if user.mfa_method == 'NONE':
        user.mfa_method = 'SMS'
    elif user.mfa_method == 'TOTP':
        user.mfa_method = 'BOTH'

    user.save(update_fields=['mfa_enabled', 'mfa_method'])

    response_data = {
        'success': True,
        'message': 'SMS MFA enabled',
        'mfa_method': user.mfa_method,
    }

    # Generate backup codes if first time enabling MFA
    if is_first_mfa:
        backup_codes = mfa_manager.generate_backup_codes(user)
        response_data['backup_codes'] = backup_codes
        response_data['backup_codes_message'] = 'Save these backup codes in a safe place. They can be used if you lose access to your phone.'

    return Response(response_data)


# ============================================================================
# TOTP SETUP (Authenticator App)
# ============================================================================

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def setup_totp(request):
    """
    Initialize TOTP setup for authenticator app.

    Returns:
        - secret: str (for manual entry)
        - qr_code: str (data URL for QR code)
        - provisioning_uri: str (otpauth:// URI)
    """
    user = request.user

    setup_data = mfa_manager.setup_totp(user)

    return Response({
        'success': True,
        'secret': setup_data['secret'],
        'qr_code': setup_data['qr_code'],
        'provisioning_uri': setup_data['provisioning_uri'],
        'message': 'Scan this QR code with your authenticator app, then enter the code to verify.'
    })


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def verify_totp_setup(request):
    """
    Verify TOTP code to complete authenticator app setup.

    Request body:
        - code: str (6-digit TOTP code)

    Returns:
        - success: bool
        - backup_codes: list (if first time enabling MFA)
    """
    user = request.user
    code = request.data.get('code', '').strip()

    if not code or len(code) != 6:
        return Response(
            {'error': 'Invalid code format. Enter the 6-digit code from your authenticator app.'},
            status=status.HTTP_400_BAD_REQUEST
        )

    is_first_mfa = not user.mfa_enabled

    if mfa_manager.verify_totp_setup(user, code):
        response_data = {
            'success': True,
            'message': 'Authenticator app configured successfully',
            'mfa_method': user.mfa_method,
        }

        # Generate backup codes if first time enabling MFA
        if is_first_mfa:
            backup_codes = mfa_manager.generate_backup_codes(user)
            response_data['backup_codes'] = backup_codes
            response_data['backup_codes_message'] = 'Save these backup codes in a safe place. They can be used if you lose access to your authenticator app.'

        return Response(response_data)
    else:
        return Response(
            {'error': 'Invalid code. Make sure your device time is correct and try again.'},
            status=status.HTTP_400_BAD_REQUEST
        )


# ============================================================================
# BACKUP CODES
# ============================================================================

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def generate_backup_codes(request):
    """
    Generate new backup codes (invalidates old ones).

    Returns:
        - backup_codes: list
    """
    user = request.user

    if not user.mfa_enabled:
        return Response(
            {'error': 'MFA must be enabled to generate backup codes'},
            status=status.HTTP_400_BAD_REQUEST
        )

    backup_codes = mfa_manager.generate_backup_codes(user)

    return Response({
        'success': True,
        'backup_codes': backup_codes,
        'message': 'New backup codes generated. Previous codes are now invalid.',
        'warning': 'These codes will only be shown once. Save them in a safe place!'
    })


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def backup_codes_status(request):
    """
    Get backup codes status (count, when generated).

    Returns:
        - count: int
        - generated_at: datetime or null
    """
    user = request.user

    return Response({
        'count': len(user.mfa_backup_codes) if user.mfa_backup_codes else 0,
        'generated_at': user.mfa_backup_codes_generated_at,
    })


# ============================================================================
# DISABLE MFA
# ============================================================================

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def disable_mfa(request):
    """
    Disable MFA for the user.
    Requires current password or valid MFA code for security.

    Request body:
        - password: str (current password)
        OR
        - mfa_code: str (valid MFA code)

    Returns:
        - success: bool
    """
    user = request.user
    password = request.data.get('password')
    mfa_code = request.data.get('mfa_code')

    # Verify identity
    verified = False

    if password:
        if user.check_password(password):
            verified = True
    elif mfa_code:
        # Try TOTP first, then backup code
        if mfa_manager.verify_totp(user, mfa_code):
            verified = True
        elif mfa_manager.verify_backup_code(user, mfa_code):
            verified = True

    if not verified:
        return Response(
            {'error': 'Invalid password or MFA code'},
            status=status.HTTP_400_BAD_REQUEST
        )

    # Disable MFA
    mfa_manager.disable_mfa(user)

    return Response({
        'success': True,
        'message': 'Two-factor authentication has been disabled'
    })


# ============================================================================
# MFA LOGIN CHALLENGE
# ============================================================================

@api_view(['POST'])
@permission_classes([AllowAny])
def mfa_login_send_code(request):
    """
    Send MFA code for login challenge.
    Called after initial login when MFA is required.

    Request body:
        - user_id: int
        - method: str ('SMS' or 'TOTP')

    Returns:
        - success: bool
        - message: str
    """
    from .models import User

    user_id = request.data.get('user_id')
    method = request.data.get('method', 'SMS')

    if not user_id:
        return Response(
            {'error': 'User ID is required'},
            status=status.HTTP_400_BAD_REQUEST
        )

    try:
        user = User.objects.get(id=user_id)
    except User.DoesNotExist:
        return Response(
            {'error': 'Invalid user'},
            status=status.HTTP_400_BAD_REQUEST
        )

    if method == 'SMS':
        if not user.phone_verified:
            return Response(
                {'error': 'SMS not available for this user'},
                status=status.HTTP_400_BAD_REQUEST
            )

        success, message = mfa_manager.send_sms_code(user, purpose='LOGIN')

        if success:
            return Response({
                'success': True,
                'message': f'Code sent to ***-***-{user.phone[-4:]}',
                'method': 'SMS'
            })
        else:
            return Response(
                {'error': 'Failed to send SMS. Please try another method.'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    elif method == 'TOTP':
        return Response({
            'success': True,
            'message': 'Enter the code from your authenticator app',
            'method': 'TOTP'
        })
    else:
        return Response(
            {'error': 'Invalid method'},
            status=status.HTTP_400_BAD_REQUEST
        )


@api_view(['POST'])
@permission_classes([AllowAny])
def mfa_login_verify(request):
    """
    Verify MFA code to complete login.

    Request body:
        - user_id: int
        - code: str (6-digit code or backup code)
        - method: str ('SMS', 'TOTP', or 'BACKUP')
        - trust_device: bool (optional)

    Returns:
        - success: bool
        - access: str (JWT access token)
        - refresh: str (JWT refresh token)
        - user: dict
    """
    from .models import User
    from rest_framework_simplejwt.tokens import RefreshToken

    user_id = request.data.get('user_id')
    code = request.data.get('code', '').strip()
    method = request.data.get('method', 'TOTP')
    trust_device = request.data.get('trust_device', False)

    if not user_id or not code:
        return Response(
            {'error': 'User ID and code are required'},
            status=status.HTTP_400_BAD_REQUEST
        )

    try:
        user = User.objects.get(id=user_id)
    except User.DoesNotExist:
        return Response(
            {'error': 'Invalid user'},
            status=status.HTTP_400_BAD_REQUEST
        )

    # Verify the code based on method
    verified = False

    if method == 'SMS':
        verification = MFAVerificationCode.objects.filter(
            user=user,
            purpose=MFAVerificationCode.Purpose.LOGIN,
            method='SMS',
            used=False
        ).order_by('-created_at').first()

        if verification and verification.verify(code):
            verified = True

    elif method == 'TOTP':
        if mfa_manager.verify_totp(user, code):
            verified = True

    elif method == 'BACKUP':
        if mfa_manager.verify_backup_code(user, code):
            verified = True

    if not verified:
        return Response(
            {'error': 'Invalid verification code'},
            status=status.HTTP_400_BAD_REQUEST
        )

    # MFA verified - complete login
    # Trust device if requested
    if trust_device:
        mfa_manager.trust_device(user, request)

    # Generate JWT tokens
    refresh = RefreshToken.for_user(user)

    # Get subdomain for frontend routing
    subdomain = None
    if user.tenant:
        primary_domain = user.tenant.domains.filter(is_primary=True, is_custom_domain=False).first()
        if primary_domain:
            subdomain = primary_domain.domain.split('.')[0]
        else:
            subdomain = user.tenant.schema_name

    return Response({
        'success': True,
        'access': str(refresh.access_token),
        'refresh': str(refresh),
        'user': {
            'id': user.id,
            'email': user.email,
            'username': user.username,
            'first_name': user.first_name,
            'last_name': user.last_name,
            'full_name': user.full_name,
            'role': user.role.lower(),
            'business_subdomain': subdomain,
            'mfa_enabled': user.mfa_enabled,
        }
    })


# ============================================================================
# TRUSTED DEVICES
# ============================================================================

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def list_trusted_devices(request):
    """
    List all trusted devices for the user.

    Returns:
        - devices: list
    """
    user = request.user
    devices = TrustedDevice.objects.filter(user=user)

    return Response({
        'devices': [
            {
                'id': device.id,
                'name': device.name,
                'ip_address': device.ip_address,
                'created_at': device.created_at,
                'last_used_at': device.last_used_at,
                'expires_at': device.expires_at,
                'is_current': mfa_manager.device_service.generate_device_hash(
                    mfa_manager._get_client_ip(request),
                    request.META.get('HTTP_USER_AGENT', ''),
                    user.id
                ) == device.device_hash
            }
            for device in devices
        ]
    })


@api_view(['DELETE'])
@permission_classes([IsAuthenticated])
def revoke_trusted_device(request, device_id):
    """
    Revoke trust for a specific device.

    Args:
        device_id: int

    Returns:
        - success: bool
    """
    user = request.user

    try:
        device = TrustedDevice.objects.get(id=device_id, user=user)
        device.delete()
        return Response({'success': True, 'message': 'Device trust revoked'})
    except TrustedDevice.DoesNotExist:
        return Response(
            {'error': 'Device not found'},
            status=status.HTTP_404_NOT_FOUND
        )


@api_view(['DELETE'])
@permission_classes([IsAuthenticated])
def revoke_all_trusted_devices(request):
    """
    Revoke trust for all devices.

    Returns:
        - success: bool
        - count: int
    """
    user = request.user
    count = TrustedDevice.objects.filter(user=user).delete()[0]

    return Response({
        'success': True,
        'message': f'{count} device(s) revoked',
        'count': count
    })
