from django.http import JsonResponse
from django.shortcuts import render, redirect
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.models import User
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.urls import reverse
from django.db.models import Sum
from django.db import transaction
from orders.models import Order, UserProfile
from store.models import Cart, CartItem
from .models import UserOTPProfile
from .otp_utils import send_otp, send_welcome_email, send_login_notification_email

import json
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from chize_stores.firebase import verify_firebase_token



from django.conf import settings
import requests
import urllib.parse

def google_auth_redirect(request):
    """Step 1 — Send user to Google."""
    params = {
        'client_id':     settings.GOOGLE_CLIENT_ID,
        'redirect_uri':  request.build_absolute_uri('/users/google-callback/'),
        'response_type': 'code',
        'scope':         'openid email profile',
        'access_type':   'offline',
        'state':         request.session.session_key or '',
    }
    url = 'https://accounts.google.com/o/oauth2/v2/auth?' + urllib.parse.urlencode(params)
    return redirect(url)


def google_auth_callback(request):
    """Step 2 — Google sends user back with a code, exchange it for user info."""
    code  = request.GET.get('code')
    error = request.GET.get('error')

    if error or not code:
        messages.error(request, 'Google sign-in was cancelled.')
        return redirect('users:login')

    # Exchange code for tokens
    token_response = requests.post('https://oauth2.googleapis.com/token', data={
        'code':          code,
        'client_id':     settings.GOOGLE_CLIENT_ID,
        'client_secret': settings.GOOGLE_CLIENT_SECRET,
        'redirect_uri':  request.build_absolute_uri('/users/google-callback/'),
        'grant_type':    'authorization_code',
    })
    token_data = token_response.json()

    if 'error' in token_data:
        messages.error(request, 'Google sign-in failed. Please try again.')
        return redirect('users:login')

    # Get user info from Google
    userinfo_response = requests.get('https://www.googleapis.com/oauth2/v3/userinfo', headers={
        'Authorization': f'Bearer {token_data["access_token"]}'
    })
    userinfo = userinfo_response.json()

    email      = userinfo.get('email', '').lower().strip()
    name       = userinfo.get('name', '')
    first_name = userinfo.get('given_name', name.split(' ')[0] if name else '')
    last_name  = userinfo.get('family_name', '')

    if not email:
        messages.error(request, 'No email returned from Google.')
        return redirect('users:login')

    session_key = request.session.session_key

    with transaction.atomic():
        user = User.objects.filter(email=email).first()

        if not user:
            base_username = email.split('@')[0]
            username      = base_username
            counter       = 1
            while User.objects.filter(username=username).exists():
                username = f'{base_username}{counter}'
                counter += 1

            user = User.objects.create_user(
                username=username,
                email=email,
                first_name=first_name,
                last_name=last_name,
                password=None,
            )
            UserOTPProfile.objects.get_or_create(
                user=user,
                defaults={'method': 'email'},
            )
            is_new = True
        else:
            is_new = False

    # ── 2FA check ─────────────────────────────────────────────────────────────
    otp_profile = getattr(user, 'otp_profile', None)
    if otp_profile and otp_profile.is_2fa_enabled:
        # Store everything we need for after the OTP step
        _send_login_otp(request, user)
        request.session['_2fa_next']        = 'store:home'
        request.session['_2fa_session_key'] = session_key
        request.session['_2fa_is_new_user'] = is_new
        return redirect('users:login_2fa')
    # ── /2FA check ────────────────────────────────────────────────────────────

    # No 2FA — log in directly
    login(request, user, backend='django.contrib.auth.backends.ModelBackend')
    merge_session_cart(session_key, user)

    try:
        if is_new:
            send_welcome_email(user)
        else:
            send_login_notification_email(user)
    except Exception:
        pass

    messages.success(request, f"Welcome {'to Chize Stores' if is_new else 'back'}, {user.first_name or user.username}!")
    return redirect('store:home')
    
    
# ── Firebase login ────────────────────────────────────────────────────────────

@csrf_exempt
@require_POST
def firebase_login(request):
    try:
        data     = json.loads(request.body)
        id_token = data.get('idToken')

        if not id_token:
            return JsonResponse({'error': 'No token provided'}, status=400)

        decoded = verify_firebase_token(id_token)

        if not decoded:
            return JsonResponse(
                {'error': 'Token verification failed — check firebase-credentials.json'},
                status=401,
            )

        email      = decoded.get('email', '').lower().strip()
        name       = decoded.get('name', '')
        first_name = name.split(' ')[0] if name else ''
        last_name  = ' '.join(name.split(' ')[1:]) if len(name.split(' ')) > 1 else ''

        if not email:
            return JsonResponse({'error': 'No email returned from Google.'}, status=400)

        # ── Save the anonymous session key BEFORE login() rotates it ──────────
        session_key = request.session.session_key

        # ── Get-or-create user safely (handles username collisions) ───────────
        with transaction.atomic():
            user = User.objects.filter(email=email).first()

            if not user:
                base_username = email.split('@')[0]
                username      = base_username
                counter       = 1
                while User.objects.filter(username=username).exists():
                    username = f'{base_username}{counter}'
                    counter += 1

                user = User.objects.create_user(
                    username=username,
                    email=email,
                    first_name=first_name,
                    last_name=last_name,
                    password=None,          # Firebase users have no Django password
                )

                # Create OTP profile for new users
                UserOTPProfile.objects.get_or_create(
                    user=user,
                    defaults={'method': 'email'},
                )

                is_new_user = True
            else:
                is_new_user = False

        # ── Log the user into the Django session ──────────────────────────────
        login(request, user, backend='django.contrib.auth.backends.ModelBackend')

        # ── Merge any anonymous cart items into the user's cart ───────────────
        merge_session_cart(session_key, user)

        # ── Send the appropriate email notification ────────────────────────────
        try:
            if is_new_user:
                send_welcome_email(user)
            else:
                send_login_notification_email(user)
        except Exception:
            pass  # Never let email failures break the login flow

        return JsonResponse({
            'success':  True,
            'redirect': '/',
        })

    except json.JSONDecodeError:
        return JsonResponse({'error': 'Invalid JSON body'}, status=400)
    except Exception as e:
        print(f'Firebase login error: {e}')
        return JsonResponse({'error': str(e)}, status=500)


# ── helpers ───────────────────────────────────────────────────────────────────

def merge_session_cart(session_key, user):
    if not session_key:
        return
    try:
        session_cart = Cart.objects.get(session_key=session_key, user__isnull=True)
    except Cart.DoesNotExist:
        return

    user_cart, _ = Cart.objects.get_or_create(user=user)

    for session_item in session_cart.items.all():
        existing = user_cart.items.filter(
            product=session_item.product,
            size=session_item.size,
            color=session_item.color,
        ).first()
        if existing:
            existing.quantity += session_item.quantity
            existing.save()
        else:
            session_item.cart = user_cart
            session_item.save()

    if session_cart.coupon and not user_cart.coupon:
        user_cart.coupon = session_cart.coupon
        user_cart.save()

    session_cart.delete()


def _send_login_otp(request, user):
    """Generate and dispatch an OTP for the login 2FA step."""
    profile = user.otp_profile
    code    = profile.generate_otp()
    send_otp(user, profile, code)
    # Store the pre-login session key so we can merge the anonymous cart after
    # the OTP step completes (login() will rotate the key, so we capture it now)
    request.session['_2fa_user_id']     = user.pk
    request.session['_2fa_session_key'] = request.session.session_key


# ── account ───────────────────────────────────────────────────────────────────

@login_required
def account_view(request):
    otp_profile = UserOTPProfile.objects.filter(user=request.user).first()
    return render(request, 'users/account.html', {
        'otp_profile':       otp_profile,
        'open_security_tab': request.GET.get('tab') == 'security',
    })


# ── login ─────────────────────────────────────────────────────────────────────

def login_view(request):
    if request.user.is_authenticated:
        return redirect('store:home')

    errors    = {}
    form_data = {}
    request.session['_session_key_before_login'] = request.session.session_key

    if request.method == 'POST':
        username = request.POST.get('username', '').strip()
        password = request.POST.get('password', '')
        next_url = request.POST.get('next', '')
        form_data = {'username': username}

        if not username:
            errors['username'] = 'Please enter your username or email.'
        if not password:
            errors['password'] = 'Please enter your password.'

        if not errors:
            user = authenticate(request, username=username, password=password)
            if not user:
                try:
                    u = User.objects.get(email=username)
                    user = authenticate(request, username=u.username, password=password)
                except User.DoesNotExist:
                    pass

            if user:
                otp_profile = getattr(user, 'otp_profile', None)
                if otp_profile and otp_profile.is_2fa_enabled:
                    _send_login_otp(request, user)
                    request.session['_2fa_next'] = next_url
                    return redirect('users:login_2fa')
                else:
                    session_key = request.session.session_key
                    login(request, user, backend='django.contrib.auth.backends.ModelBackend')
                    merge_session_cart(session_key, user)
                    try:
                        send_login_notification_email(user)
                    except Exception:
                        pass
                    messages.success(request, f"Welcome back, {user.first_name or user.username}!")
                    return redirect(next_url or 'store:home')
            else:
                errors['password'] = 'Invalid username/email or password.'

    return render(request, 'users/login.html', {
        'errors':    errors,
        'form_data': form_data,
        'next':      request.GET.get('next', ''),
    })


def login_2fa_view(request):
    user_id = request.session.get('_2fa_user_id')
    if not user_id:
        return redirect('users:login')

    try:
        user = User.objects.get(pk=user_id)
    except User.DoesNotExist:
        return redirect('users:login')

    profile = user.otp_profile
    errors  = {}
    resent  = False

    if request.method == 'POST':
        if 'resend' in request.POST:
            code = profile.generate_otp()
            send_otp(user, profile, code)
            resent = True
        else:
            token = request.POST.get('token', '').strip()
            if not token:
                errors['token'] = 'Please enter the 6-digit code.'
            elif profile.verify_otp(token):
                session_key = request.session.pop('_2fa_session_key', None)
                next_url    = request.session.pop('_2fa_next', '')
                is_new      = request.session.pop('_2fa_is_new_user', False)
                request.session.pop('_2fa_user_id', None)

                login(request, user, backend='django.contrib.auth.backends.ModelBackend')
                merge_session_cart(session_key, user)

                try:
                    if is_new:
                        send_welcome_email(user)
                    else:
                        send_login_notification_email(user)
                except Exception:
                    pass

                messages.success(request, f"Welcome {'to Chize Stores' if is_new else 'back'}, {user.first_name or user.username}!")
                return redirect(next_url or 'store:home')
            else:
                errors['token'] = 'Invalid or expired code. Please try again.'

    method_label = 'WhatsApp' if profile.method == 'whatsapp' else user.email
    return render(request, 'users/login_2fa.html', {
        'errors':       errors,
        'method_label': method_label,
        'resent':       resent,
    })
# ── registration ──────────────────────────────────────────────────────────────

def register_view(request):
    if request.user.is_authenticated:
        return redirect('store:home')

    errors    = {}
    form_data = {}

    if request.method == 'POST':
        first_name = request.POST.get('first_name', '').strip()
        last_name  = request.POST.get('last_name',  '').strip()
        email      = request.POST.get('email',      '').strip()
        username   = request.POST.get('username',   '').strip()
        password1  = request.POST.get('password1',  '')
        password2  = request.POST.get('password2',  '')

        form_data = {
            'first_name': first_name,
            'last_name':  last_name,
            'email':      email,
            'username':   username,
        }

        if not first_name:
            errors['first_name'] = 'First name is required.'
        if not last_name:
            errors['last_name']  = 'Last name is required.'
        if not username:
            errors['username'] = 'Username is required.'
        elif User.objects.filter(username=username).exists():
            errors['username'] = 'Username already taken.'
        if not email:
            errors['email'] = 'Email address is required.'
        elif User.objects.filter(email=email).exists():
            errors['email'] = 'Email already registered.'
        if not password1:
            errors['password1'] = 'Password is required.'
        elif len(password1) < 8:
            errors['password1'] = 'Password must be at least 8 characters.'
        if not password2:
            errors['password2'] = 'Please confirm your password.'
        elif password1 and password1 != password2:
            errors['password2'] = 'Passwords do not match.'

        if not errors:
            session_key = request.session.session_key
            user = User.objects.create_user(
                username=username,
                email=email,
                password=password1,
                first_name=first_name,
                last_name=last_name,
            )
            UserOTPProfile.objects.create(user=user, method='email')
            login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            merge_session_cart(session_key, user)
            try:
                send_welcome_email(user)
            except Exception:
                pass
            messages.success(request, f"Welcome to Chiez Stores, {first_name or username}!")
            return redirect('users:setup_2fa')

    return render(request, 'users/register.html', {
        'errors':    errors,
        'form_data': form_data,
    })


# ── 2FA setup ─────────────────────────────────────────────────────────────────

@login_required
def setup_2fa_view(request):
    profile, _ = UserOTPProfile.objects.get_or_create(
        user=request.user,
        defaults={'method': 'email'},
    )

    if request.method == 'GET' and request.GET.get('disable') == '1':
        profile.is_2fa_enabled = False
        profile.save()
        messages.success(request, 'Two-factor authentication has been disabled.')
        return redirect(reverse('orders:profile') + '?tab=security#security')

    errors = {}

    if request.method == 'POST':
        if 'skip' in request.POST:
            return redirect('store:home')

        method       = request.POST.get('method', 'email')
        phone_number = request.POST.get('phone_number', '').strip()

        if method == 'whatsapp':
            if not phone_number:
                errors['phone_number'] = 'Please enter your WhatsApp phone number.'
            elif not phone_number.startswith('+'):
                errors['phone_number'] = 'Include your country code, e.g. +2348012345678'

        if not errors:
            profile.method       = method
            profile.phone_number = phone_number
            profile.save()
            code = profile.generate_otp()
            send_otp(request.user, profile, code)
            return redirect('users:verify_2fa_setup')

    return render(request, 'users/setup_2fa.html', {
        'profile': profile,
        'errors':  errors,
    })


@login_required
def verify_2fa_setup_view(request):
    """Confirm the OTP received during setup to activate 2FA."""
    profile = getattr(request.user, 'otp_profile', None)
    if not profile:
        return redirect('users:setup_2fa')

    errors = {}
    resent = False

    if request.method == 'POST':
        if 'resend' in request.POST:
            code = profile.generate_otp()
            send_otp(request.user, profile, code)
            resent = True
        else:
            token = request.POST.get('token', '').strip()
            if not token:
                errors['token'] = 'Please enter the 6-digit code.'
            elif profile.verify_otp(token):
                profile.is_2fa_enabled = True
                profile.save()
                messages.success(request, '2FA has been enabled on your account!')
                return redirect(reverse('orders:profile') + '?tab=security#security')
            else:
                errors['token'] = 'Invalid or expired code. Please try again.'

    method_label = 'WhatsApp' if profile.method == 'whatsapp' else request.user.email
    return render(request, 'users/verify_2fa_setup.html', {
        'errors':       errors,
        'method_label': method_label,
        'resent':       resent,
    })


# ── logout ────────────────────────────────────────────────────────────────────

def logout_view(request):
    logout(request)
    messages.success(request, "You have been logged out.")
    return redirect("store:home")


# ── forgot password ───────────────────────────────────────────────────────────

def forgot_password(request):
    if request.method == 'POST':
        email = request.POST.get('email', '').strip()
        # Always show the same message to prevent user enumeration
        messages.success(
            request,
            'If an account with that email exists, reset instructions have been sent.',
        )
        user = User.objects.filter(email=email).first()
        if user:
            # TODO: generate a password reset token and email it
            # Example using Django's built-in:
            #   from django.contrib.auth.forms import PasswordResetForm
            #   form = PasswordResetForm({'email': email})
            #   if form.is_valid():
            #       form.save(request=request, use_https=request.is_secure())
            pass
        return redirect('users:forgot_password')

    return render(request, 'users/forgot_password.html')