"""
NOMAZRA auth views — Pure Django JWT authentication.
No Google auth, no Firebase auth. Firebase is ONLY used for FCM notifications.
Includes: register, login, refresh, me, update_profile, change_password,
           update_fcm_token, delete_account, password_reset, password_reset_confirm.
"""
import re
import uuid
import secrets
import logging

from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.core.mail import send_mail
from django.conf import settings
from django.utils import timezone
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken

from .models import UserStats
from .email_templates import welcome_email, password_reset_email

logger = logging.getLogger(__name__)

User = get_user_model()

EMAIL_RE = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')

RESET_CODE_EXPIRY = 15 * 60  # 15 minutes in seconds


def _user_response(user, refresh=None):
    """Standard user response payload."""
    data = {
        'user': {
            'id': str(user.id),
            'email': user.email,
            'display_name': user.display_name or user.first_name or '',
            'plan': getattr(user, 'subscription_plan', 'free') or 'free',
        }
    }
    if refresh:
        data['access'] = str(refresh.access_token)
        data['refresh'] = str(refresh)
    return data


@api_view(['POST'])
@permission_classes([AllowAny])
def register(request):
    email = request.data.get('email', '').strip().lower()
    phone = request.data.get('phone', '').strip()
    password = request.data.get('password', '')
    display_name = request.data.get('display_name', '').strip()[:255]
    country = request.data.get('country', '').strip()[:5]

    if not email or not password:
        return Response({'error': 'Email and password required'}, status=status.HTTP_400_BAD_REQUEST)
    if not EMAIL_RE.match(email):
        return Response({'error': 'Invalid email format'}, status=status.HTTP_400_BAD_REQUEST)
    if len(password) < 8:
        return Response({'error': 'Password must be at least 8 characters'}, status=status.HTTP_400_BAD_REQUEST)
    if len(email) > 254:
        return Response({'error': 'Email too long'}, status=status.HTTP_400_BAD_REQUEST)
    if User.objects.filter(email=email).exists():
        return Response({'error': 'Email already registered'}, status=status.HTTP_409_CONFLICT)

    try:
        user = User.objects.create_user(
            username=email,
            email=email,
            password=password,
            firebase_uid=f'nomazra_{uuid.uuid4().hex}',
            signup_provider='nomazra',
            last_auth_provider='nomazra',
        )
        if display_name:
            user.display_name = display_name
            user.first_name = display_name
            user.save(update_fields=['display_name', 'first_name'])

    except Exception as e:
        logger.error("Registration failed: %s", e)
        return Response({'error': 'Registration failed'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    UserStats.objects.get_or_create(user=user)
    refresh = RefreshToken.for_user(user)

    # Send welcome email (non-blocking)
    try:
        send_mail(
            subject='Welcome to NOMAZRA — AI Legal Assistant',
            message=f'Welcome to NOMAZRA, {display_name or "there"}! Your account is ready.',
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[email],
            html_message=welcome_email(display_name or 'there'),
            fail_silently=True,
        )
    except Exception as e:
        logger.warning("Welcome email failed: %s", e)

    return Response(_user_response(user, refresh), status=status.HTTP_201_CREATED)


@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
    email = request.data.get('email', '').strip().lower()
    password = request.data.get('password', '')

    if not email or not password:
        return Response({'error': 'Email and password required'}, status=status.HTTP_400_BAD_REQUEST)

    try:
        user = User.objects.get(email=email, is_active=True, is_deleted=False)
    except User.DoesNotExist:
        return Response({'error': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)

    if not user.check_password(password):
        return Response({'error': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)

    refresh = RefreshToken.for_user(user)

    return Response(_user_response(user, refresh))


@api_view(['POST'])
@permission_classes([AllowAny])
def refresh_token(request):
    token = request.data.get('refresh')
    if not token:
        return Response({'error': 'Refresh token required'}, status=status.HTTP_400_BAD_REQUEST)
    try:
        refresh = RefreshToken(token)
        return Response({
            'access': str(refresh.access_token),
            'refresh': str(refresh),
        })
    except Exception:
        return Response({'error': 'Invalid refresh token'}, status=status.HTTP_401_UNAUTHORIZED)


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def me(request):
    return Response(_user_response(request.user)['user'])


@api_view(['PATCH'])
@permission_classes([IsAuthenticated])
def update_profile(request):
    user = request.user
    if 'display_name' in request.data:
        name = request.data['display_name'][:255]
        user.display_name = name
        user.first_name = name
    if 'country' in request.data:
        # Store preferred country if model supports it
        country = request.data['country'][:5]
        if hasattr(user, 'default_country'):
            user.default_country = country
    user.save()
    return Response(_user_response(user)['user'])


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def change_password(request):
    user = request.user
    current = request.data.get('current_password', '')
    new = request.data.get('new_password', '')

    if not user.check_password(current):
        return Response({'error': 'Current password incorrect'}, status=status.HTTP_400_BAD_REQUEST)
    if len(new) < 8:
        return Response({'error': 'Password must be at least 8 characters'}, status=status.HTTP_400_BAD_REQUEST)

    user.set_password(new)
    user.save()
    return Response({'message': 'Password changed'})


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def update_fcm_token(request):
    """Store the device FCM token for push notifications."""
    fcm_token = request.data.get('fcm_token', '').strip()
    if not fcm_token:
        return Response({'error': 'fcm_token required'}, status=status.HTTP_400_BAD_REQUEST)
    if len(fcm_token) > 500:
        return Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)

    request.user.fcm_token = fcm_token
    request.user.save(update_fields=['fcm_token'])
    return Response({'message': 'FCM token updated'})


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def delete_account(request):
    password = request.data.get('password', '')
    if not request.user.check_password(password):
        return Response({'error': 'Incorrect password'}, status=status.HTTP_400_BAD_REQUEST)

    request.user.soft_delete()
    return Response({'message': 'Account scheduled for deletion'}, status=status.HTTP_200_OK)


# ──────────────────── PASSWORD RESET ────────────────────

@api_view(['POST'])
@permission_classes([AllowAny])
def password_reset_request(request):
    """
    Request password reset. Sends a 6-digit code via email.
    Always returns success (even for unknown emails) to prevent user enumeration.
    """
    email = request.data.get('email', '').strip().lower()

    if not email:
        return Response({'error': 'Email required'}, status=status.HTTP_400_BAD_REQUEST)
    if not EMAIL_RE.match(email):
        return Response({'error': 'Invalid email format'}, status=status.HTTP_400_BAD_REQUEST)

    # Rate limit: 3 reset requests per email per hour
    rate_key = f'nomazra:reset_rate:{email}'
    rate_count = cache.get(rate_key, 0)
    if rate_count >= 3:
        return Response({
            'message': 'If an account with that email exists, a reset code has been sent.',
        })

    try:
        user = User.objects.get(email=email, is_active=True, is_deleted=False)
    except User.DoesNotExist:
        # Don't reveal whether the email exists
        return Response({
            'message': 'If an account with that email exists, a reset code has been sent.',
        })

    # Generate 6-digit code
    code = ''.join([str(secrets.randbelow(10)) for _ in range(6)])

    # Store in cache with expiry
    cache_key = f'nomazra:reset:{email}'
    cache.set(cache_key, {
        'code': code,
        'user_id': str(user.id),
        'attempts': 0,
    }, timeout=RESET_CODE_EXPIRY)

    # Update rate limit
    cache.set(rate_key, rate_count + 1, timeout=3600)

    # Send email
    display_name = user.display_name or user.first_name or 'there'
    try:
        send_mail(
            subject='NOMAZRA — Password Reset Code',
            message=f'Your NOMAZRA password reset code is: {code}. It expires in 15 minutes.',
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[email],
            html_message=password_reset_email(display_name, code, expiry_minutes=15),
            fail_silently=False,
        )
        logger.info("Password reset code sent to %s", email)
    except Exception as e:
        logger.error("Password reset email failed for %s: %s", email, e)
        return Response(
            {'error': 'Failed to send reset email. Please try again later.'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )

    return Response({
        'message': 'If an account with that email exists, a reset code has been sent.',
    })


@api_view(['POST'])
@permission_classes([AllowAny])
def password_reset_confirm(request):
    """
    Confirm password reset with email, code, and new password.
    """
    email = request.data.get('email', '').strip().lower()
    code = request.data.get('code', '').strip()
    new_password = request.data.get('new_password', '')

    if not email or not code or not new_password:
        return Response(
            {'error': 'email, code, and new_password required'},
            status=status.HTTP_400_BAD_REQUEST,
        )
    if len(new_password) < 8:
        return Response(
            {'error': 'Password must be at least 8 characters'},
            status=status.HTTP_400_BAD_REQUEST,
        )

    cache_key = f'nomazra:reset:{email}'
    reset_data = cache.get(cache_key)

    if not reset_data:
        return Response(
            {'error': 'Reset code expired or not found. Request a new one.'},
            status=status.HTTP_400_BAD_REQUEST,
        )

    # Check attempts (max 5)
    if reset_data.get('attempts', 0) >= 5:
        cache.delete(cache_key)
        return Response(
            {'error': 'Too many failed attempts. Request a new code.'},
            status=status.HTTP_429_TOO_MANY_REQUESTS,
        )

    # Verify code using constant-time comparison
    if not secrets.compare_digest(code, reset_data['code']):
        reset_data['attempts'] = reset_data.get('attempts', 0) + 1
        cache.set(cache_key, reset_data, timeout=RESET_CODE_EXPIRY)
        remaining = 5 - reset_data['attempts']
        return Response(
            {'error': f'Invalid code. {remaining} attempts remaining.'},
            status=status.HTTP_400_BAD_REQUEST,
        )

    # Code is valid — reset password
    try:
        user = User.objects.get(pk=reset_data['user_id'], is_active=True)
        user.set_password(new_password)
        user.save()
    except User.DoesNotExist:
        return Response({'error': 'Account not found'}, status=status.HTTP_404_NOT_FOUND)

    # Invalidate the code
    cache.delete(cache_key)

    # Return new tokens so user is logged in
    refresh = RefreshToken.for_user(user)

    return Response({
        'message': 'Password reset successful',
        **_user_response(user, refresh),
    })
