from django.contrib import messages
from django.contrib.auth import get_user_model, login, logout
from django.contrib.auth.decorators import login_required
from django.db.models import Q
from django.core.exceptions import ImproperlyConfigured
from django.conf import settings
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.http import url_has_allowed_host_and_scheme
from django.views.decorators.http import require_POST

from .forms import OTPForm, PhoneForm
from .enrollment import create_pending_order, issue_course_access
from .models import Category, Course, Enrollment, Order, Review, Video
from .otp import SESSION_KEY, issue_otp, verify_otp
from .payment import PaymentGatewayError, ZarinpalGateway

User = get_user_model()


def home(request):
    return render(request, 'index.html', {
        'featured_courses': Course.objects.filter(is_hidden=False, is_featured=True).select_related('instructor')[:6],
        'categories': Category.objects.all(),
        'reviews': Review.objects.select_related('course')[:6],
    })


def course_catalog(request):
    category_slug = request.GET.get('category', '')
    search_query = request.GET.get('q', '').strip()
    sort_by = request.GET.get('sort', 'featured')
    courses = Course.objects.filter(is_hidden=False).select_related('category', 'instructor')
    if category_slug:
        courses = courses.filter(category__slug=category_slug)
    if search_query:
        courses = courses.filter(Q(name__icontains=search_query) | Q(tagline__icontains=search_query) | Q(description__icontains=search_query))
    ordering = {'price_low': 'price', 'price_high': '-price', 'rating': '-rating'}.get(sort_by)
    courses = courses.order_by(ordering) if ordering else courses.order_by('-is_featured', '-created_at')
    return render(request, 'courses.html', {'courses': courses, 'categories': Category.objects.all(), 'current_category': category_slug, 'search_query': search_query, 'sort_by': sort_by})


def course_detail(request, course_id):
    course = get_object_or_404(Course, id=course_id, is_hidden=False)
    enrolled = request.user.is_authenticated and Enrollment.objects.filter(user=request.user, course=course).exists()
    return render(request, 'course_detail.html', {
        'course': course, 'headlines': course.headlines.prefetch_related('videos'), 'files': course.files.all(),
        'reviews': course.reviews.all(), 'related_courses': Course.objects.filter(is_hidden=False).exclude(id=course.id)[:3],
        'is_enrolled': enrolled,
    })


@login_required(login_url='courses:login')
def course_player(request, course_id):
    course = get_object_or_404(Course, id=course_id, is_hidden=False)
    completed_order = Order.objects.filter(user=request.user, course=course, status='Completed').first()
    has_access = course.is_free or completed_order is not None or Enrollment.objects.filter(user=request.user, course=course).exists()
    if not has_access:
        messages.error(request, 'برای مشاهده این دوره ابتدا ثبت‌نام کنید.')
        return redirect('courses:checkout', course_id=course.id)
    active_video = Video.objects.filter(course=course, is_active=True, id=request.GET.get('v')).first() if request.GET.get('v') else Video.objects.filter(course=course, is_active=True).first()
    enrollment = Enrollment.objects.filter(user=request.user, course=course).first()
    if not enrollment:
        if not completed_order and course.is_free:
            completed_order, _ = Order.objects.get_or_create(user=request.user, course=course, status='Completed', defaults={'amount': 0, 'transaction_id': f'FREE-{request.user.pk}-{course.pk}'})
        if completed_order:
            issue_course_access(completed_order)
    return render(request, 'player.html', {'course': course, 'headlines': course.headlines.prefetch_related('videos'), 'active_video': active_video, 'files': course.files.all()})


@login_required(login_url='courses:login')
def checkout(request, course_id):
    course = get_object_or_404(Course, id=course_id, is_hidden=False)
    if Enrollment.objects.filter(user=request.user, course=course).exists():
        return redirect('courses:player', course_id=course.id)
    if request.method == 'POST':
        if course.is_free or course.effective_price == 0:
            order, _ = Order.objects.get_or_create(user=request.user, course=course, status='Completed', defaults={'amount': 0, 'transaction_id': f'FREE-{request.user.pk}-{course.pk}'})
            issue_course_access(order)
            messages.success(request, 'دوره رایگان با موفقیت به حساب شما اضافه شد.')
            return redirect('courses:player', course_id=course.id)
        order = create_pending_order(request.user, course)
        callback_path = reverse('courses:zarinpal_callback', args=[order.id])
        callback_url = f'{settings.PAYMENT_CALLBACK_BASE_URL}{callback_path}' if settings.PAYMENT_CALLBACK_BASE_URL else request.build_absolute_uri(callback_path)
        try:
            result = ZarinpalGateway().request_payment(order=order, callback_url=callback_url)
        except (PaymentGatewayError, ImproperlyConfigured) as exc:
            order.status = 'Failed'
            order.payment_error = str(exc)
            order.gateway_response = getattr(exc, 'payload', {})
            order.save(update_fields=['status', 'payment_error', 'gateway_response'])
            messages.error(request, 'اتصال به درگاه پرداخت ناموفق بود. جزئیات خطا در سفارش ثبت شد.')
            return redirect('courses:order_status', order_id=order.id)
        order.gateway_authority = result.authority
        order.gateway_response = result.payload
        order.payment_error = ''
        order.save(update_fields=['gateway_authority', 'gateway_response', 'payment_error'])
        return redirect(result.payment_url)
    pending_order = Order.objects.filter(user=request.user, course=course, status='Pending').first()
    return render(request, 'checkout.html', {'course': course, 'pending_order': pending_order})


@login_required(login_url='courses:login')
def order_status(request, order_id):
    order = get_object_or_404(Order.objects.select_related('course'), id=order_id, user=request.user)
    if order.status == 'Completed':
        issue_course_access(order)
    return render(request, 'order_status.html', {'order': order})


def zarinpal_callback(request, order_id):
    order = get_object_or_404(Order.objects.select_related('course', 'user'), id=order_id)
    authority = request.GET.get('Authority', '')
    status = request.GET.get('Status', '')
    if order.status == 'Completed' and authority == order.gateway_authority:
        login(request, order.user, backend='django.contrib.auth.backends.ModelBackend')
        return redirect('courses:player', course_id=order.course_id)
    if not authority or authority != order.gateway_authority:
        order.status = 'Failed'
        order.payment_error = 'شناسه بازگشتی درگاه با سفارش مطابقت ندارد.'
        order.save(update_fields=['status', 'payment_error'])
        return redirect('courses:order_status', order_id=order.id)
    if status != 'OK':
        order.status = 'Failed'
        order.payment_error = 'پرداخت توسط کاربر لغو شد یا در درگاه ناموفق بود.'
        order.save(update_fields=['status', 'payment_error'])
        return redirect('courses:order_status', order_id=order.id)
    try:
        data, raw_response = ZarinpalGateway().verify_payment(order=order, authority=authority)
    except (PaymentGatewayError, ImproperlyConfigured) as exc:
        order.status = 'Failed'
        order.payment_error = f'خطا در تأیید پرداخت: {exc}'
        order.gateway_response = getattr(exc, 'payload', {})
        order.save(update_fields=['status', 'payment_error', 'gateway_response'])
        return redirect('courses:order_status', order_id=order.id)
    with transaction.atomic():
        order.status = 'Completed'
        order.gateway_reference = str(data.get('ref_id') or '')
        order.gateway_response = raw_response
        order.payment_error = ''
        order.save(update_fields=['status', 'gateway_reference', 'gateway_response', 'payment_error'])
        issue_course_access(order)
    login(request, order.user, backend='django.contrib.auth.backends.ModelBackend')
    messages.success(request, 'پرداخت با موفقیت تأیید شد و دوره برای شما فعال شد.')
    return redirect('courses:player', course_id=order.course_id)


@login_required(login_url='courses:login')
def profile(request):
    if request.method == 'POST':
        request.user.name = request.POST.get('name', '').strip()[:100]
        request.user.email = request.POST.get('email', '').strip()
        request.user.save(update_fields=['name', 'email'])
        messages.success(request, 'پروفایل به‌روزرسانی شد.')
        return redirect('courses:profile')
    return render(request, 'profile.html', {'enrollments': request.user.enrollments.select_related('course'), 'orders': request.user.orders.select_related('course')})


def _begin_auth(request, purpose):
    if request.user.is_authenticated:
        return redirect('courses:home')
    form = PhoneForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        phone = form.cleaned_data['phone']
        if purpose == 'login' and not User.objects.filter(phone=phone, is_active=True).exists():
            form.add_error('phone', 'حسابی با این شماره وجود ندارد.')
        else:
            profile = {'name': form.cleaned_data.get('name', ''), 'email': form.cleaned_data.get('email', '')}
            try:
                sent = issue_otp(request, phone, purpose, profile)
            except (ImproperlyConfigured, OSError, RuntimeError):
                sent = None
                form.add_error(None, 'ارسال پیامک با مشکل روبه‌رو شد. لطفاً چند دقیقه دیگر تلاش کنید.')
            if sent:
                request.session['auth_next'] = request.GET.get('next', '')
                return redirect('courses:verify_otp')
            if sent is False:
                messages.warning(request, 'کد قبلی هنوز معتبر است؛ کمی بعد دوباره تلاش کنید.')
                return redirect('courses:verify_otp')
    return render(request, 'register.html' if purpose == 'register' else 'login.html', {'form': form})


def login_view(request):
    return _begin_auth(request, 'login')


def register_view(request):
    return _begin_auth(request, 'register')


def verify_otp_view(request):
    challenge = request.session.get(SESSION_KEY)
    if not challenge:
        messages.error(request, 'ابتدا شماره موبایل را وارد کنید.')
        return redirect('courses:login')
    form = OTPForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        verified, error = verify_otp(request, form.cleaned_data['code'])
        if error:
            form.add_error('code', error)
        else:
            user, created = User.objects.get_or_create(phone=verified['phone'])
            if verified['purpose'] == 'register' or created:
                user.name = verified['profile'].get('name', '') or user.name
                user.email = verified['profile'].get('email', '') or user.email
                user.save(update_fields=['name', 'email'])
            request.session.cycle_key()
            login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            target = request.session.pop('auth_next', '')
            if not url_has_allowed_host_and_scheme(target, allowed_hosts={request.get_host()}, require_https=request.is_secure()):
                target = reverse('courses:profile')
            messages.success(request, 'ورود با موفقیت انجام شد.')
            return redirect(target)
    return render(request, 'verify.html', {'form': form, 'phone': challenge['phone']})


@require_POST
def resend_otp_view(request):
    challenge = request.session.get(SESSION_KEY)
    if not challenge:
        messages.error(request, 'درخواست ورود منقضی شده است. شماره موبایل را دوباره وارد کنید.')
        return redirect('courses:login')
    try:
        sent = issue_otp(request, challenge['phone'], challenge['purpose'], challenge.get('profile'))
    except (ImproperlyConfigured, OSError, RuntimeError):
        sent = None
    if sent:
        messages.success(request, 'کد جدید ارسال شد.')
    elif sent is False:
        messages.warning(request, 'برای ارسال دوباره کد، کمی صبر کنید.')
    else:
        messages.error(request, 'ارسال پیامک ناموفق بود. لطفاً دوباره تلاش کنید.')
    return redirect('courses:verify_otp')


@require_POST
def logout_view(request):
    logout(request)
    request.session.flush()
    return redirect('courses:home')
