"""
Views for the enrollments app.
"""
from django.views.generic import ListView, DetailView, View, UpdateView
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render
from django.core.exceptions import PermissionDenied
from django.urls import reverse_lazy
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.http import HttpResponse, HttpResponseBadRequest
from django.db.models import Q

from apps.accounts.forms import EducationBackgroundForm
from apps.enrollments.models import Enrollment, PAYMENT_METHOD_MANUAL
from apps.enrollments.forms import PaymentProofForm
from apps.enrollments.services import (
    enroll_student, approve_enrollment, reject_enrollment,
    get_student_enrollments, activate_free_enrollment,
    create_stripe_checkout_session, finalize_stripe_enrollment,
)
from apps.courses.models import Course
from apps.dashboard.models import SiteSettings
from utils.permissions import (
    StudentRequiredMixin, AdminRequiredMixin, TeacherRequiredMixin,
    AdminOrAccountsStaffMixin,
)


class EnrollView(StudentRequiredMixin, View):
    """Confirm education background, then create enrollment."""
    template_name = 'enrollments/enroll_confirm.html'

    def _get_course(self, slug):
        return get_object_or_404(Course, slug=slug, status='published')

    def get(self, request, slug):
        course = self._get_course(slug)
        if not course.is_enrollment_open:
            messages.error(
                request,
                f"Enrollment for '{course.title}' is closed — the course has already started."
            )
            return redirect('courses:course_detail', slug=slug)
        form = EducationBackgroundForm(instance=request.user)
        return render(request, self.template_name, {'course': course, 'form': form})

    def post(self, request, slug):
        course = self._get_course(slug)
        if not course.is_enrollment_open:
            messages.error(
                request,
                f"Enrollment for '{course.title}' is closed — the course has already started."
            )
            return redirect('courses:course_detail', slug=slug)

        form = EducationBackgroundForm(request.POST, instance=request.user)
        if not form.is_valid():
            return render(request, self.template_name, {'course': course, 'form': form})
        form.save()

        try:
            enrollment = enroll_student(request.user, course)
            if course.price <= 0:
                activate_free_enrollment(enrollment)
                messages.success(
                    request,
                    f"You're enrolled in '{course.title}' — this course is free. Start learning!"
                )
                return redirect('enrollments:my_enrollments')

            messages.success(
                request,
                f"Enrollment started for '{course.title}'. Complete payment to activate access."
            )
            return redirect('enrollments:upload_payment', pk=enrollment.pk)
        except ValueError as e:
            messages.warning(request, str(e))
            return redirect('courses:course_detail', slug=slug)


class MyEnrollmentsView(StudentRequiredMixin, ListView):
    template_name = 'enrollments/my_enrollments.html'
    context_object_name = 'enrollments'

    def get_queryset(self):
        return get_student_enrollments(self.request.user)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        base_qs = Enrollment.objects.filter(student=self.request.user)
        context['total_count'] = base_qs.count()
        context['pending_count'] = base_qs.filter(status='pending').count()
        context['approved_count'] = base_qs.filter(status='approved').count()
        context['rejected_count'] = base_qs.filter(status='rejected').count()
        return context


class UploadPaymentView(StudentRequiredMixin, UpdateView):
    """Payment hub: Manual proof upload and/or Stripe Checkout."""
    model = Enrollment
    form_class = PaymentProofForm
    template_name = 'enrollments/upload_payment.html'
    success_url = reverse_lazy('enrollments:my_enrollments')

    def get_object(self):
        enrollment = get_object_or_404(Enrollment, pk=self.kwargs['pk'])
        if enrollment.student != self.request.user:
            raise PermissionDenied
        return enrollment

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        site = SiteSettings.load()
        from apps.promotions.services import enrollment_payable
        context['site'] = site
        context['manual_enabled'] = site.enable_manual_payment
        context['stripe_enabled'] = site.stripe_ready
        context['amount_due'] = enrollment_payable(self.object)
        context['list_price'] = self.object.course.price
        return context

    def dispatch(self, request, *args, **kwargs):
        self.object = self.get_object()
        if self.object.status == 'approved':
            messages.info(request, 'This enrollment is already active.')
            return redirect('enrollments:my_enrollments')
        return super().dispatch(request, *args, **kwargs)

    def form_valid(self, form):
        site = SiteSettings.load()
        if not site.enable_manual_payment:
            messages.error(self.request, 'Manual payment is currently disabled.')
            return redirect('enrollments:upload_payment', pk=self.object.pk)

        enrollment = form.save(commit=False)
        enrollment.payment_method = PAYMENT_METHOD_MANUAL
        enrollment.save()
        messages.success(
            self.request,
            "Payment proof uploaded successfully! Admin will review your enrollment."
        )
        return redirect(self.success_url)


class StripeCheckoutView(StudentRequiredMixin, View):
    def post(self, request, pk):
        enrollment = get_object_or_404(Enrollment, pk=pk, student=request.user)
        if enrollment.status == 'approved':
            messages.info(request, 'This enrollment is already active.')
            return redirect('enrollments:my_enrollments')

        site = SiteSettings.load()
        if not site.stripe_ready:
            messages.error(request, 'Stripe payments are not available right now.')
            return redirect('enrollments:upload_payment', pk=pk)

        try:
            session = create_stripe_checkout_session(enrollment, request)
            return redirect(session.url, permanent=False)
        except Exception as exc:
            messages.error(request, f'Could not start Stripe checkout: {exc}')
            return redirect('enrollments:upload_payment', pk=pk)


class ApplyEnrollmentPromoView(StudentRequiredMixin, View):
    def post(self, request, pk):
        from apps.promotions.services import (
            PromoError, apply_promo_to_enrollment, enrollment_payable,
        )
        enrollment = get_object_or_404(Enrollment, pk=pk, student=request.user)
        if enrollment.status == 'approved':
            return redirect('enrollments:my_enrollments')
        try:
            apply_promo_to_enrollment(enrollment, request.POST.get('code', ''))
            enrollment.refresh_from_db()
            due = enrollment_payable(enrollment)
            if due <= 0:
                activate_free_enrollment(enrollment)
                messages.success(request, 'Promo applied — this enrollment is now free and activated!')
                return redirect('enrollments:my_enrollments')
            messages.success(
                request,
                f'Promo {enrollment.promo_code.code} applied for {enrollment.promo_code.institute_name}.',
            )
        except PromoError as exc:
            messages.error(request, str(exc))
        return redirect('enrollments:upload_payment', pk=pk)


class ClearEnrollmentPromoView(StudentRequiredMixin, View):
    def post(self, request, pk):
        from apps.promotions.services import clear_promo_from_enrollment
        enrollment = get_object_or_404(Enrollment, pk=pk, student=request.user)
        clear_promo_from_enrollment(enrollment)
        messages.info(request, 'Promo code removed.')
        return redirect('enrollments:upload_payment', pk=pk)


class StripeSuccessView(StudentRequiredMixin, View):
    def get(self, request, pk):
        enrollment = get_object_or_404(Enrollment, pk=pk, student=request.user)
        session_id = request.GET.get('session_id', '').strip()
        if not session_id:
            messages.error(request, 'Missing Stripe session. Please try again.')
            return redirect('enrollments:upload_payment', pk=pk)

        import stripe
        site = SiteSettings.load()
        stripe.api_key = site.stripe_secret_key

        try:
            session = stripe.checkout.Session.retrieve(session_id)
        except Exception as exc:
            messages.error(request, f'Could not verify payment: {exc}')
            return redirect('enrollments:upload_payment', pk=pk)

        if session.get('payment_status') == 'paid' or session.get('status') == 'complete':
            meta_id = str((session.get('metadata') or {}).get('enrollment_id', ''))
            if meta_id and meta_id != str(enrollment.pk):
                messages.error(request, 'Payment session does not match this enrollment.')
                return redirect('enrollments:upload_payment', pk=pk)
            finalize_stripe_enrollment(enrollment, session)
            messages.success(
                request,
                f"Payment successful! '{enrollment.course.title}' is now active."
            )
            return redirect('enrollments:my_enrollments')

        messages.warning(request, 'Payment is not complete yet. If you were charged, contact support.')
        return redirect('enrollments:upload_payment', pk=pk)


@method_decorator(csrf_exempt, name='dispatch')
class StripeWebhookView(View):
    def post(self, request):
        import stripe

        site = SiteSettings.load()
        payload = request.body
        sig_header = request.META.get('HTTP_STRIPE_SIGNATURE', '')

        if site.stripe_webhook_secret:
            try:
                event = stripe.Webhook.construct_event(
                    payload, sig_header, site.stripe_webhook_secret
                )
            except (ValueError, stripe.error.SignatureVerificationError):
                return HttpResponseBadRequest('Invalid payload or signature')
        else:
            import json
            try:
                event = json.loads(payload)
            except json.JSONDecodeError:
                return HttpResponseBadRequest('Invalid JSON')

        event_type = event['type'] if isinstance(event, dict) else event.type
        data_object = event['data']['object'] if isinstance(event, dict) else event.data.object

        if event_type == 'checkout.session.completed':
            enrollment_id = (data_object.get('metadata') or {}).get('enrollment_id')
            if enrollment_id:
                try:
                    enrollment = Enrollment.objects.get(pk=enrollment_id)
                    finalize_stripe_enrollment(enrollment, data_object)
                except Enrollment.DoesNotExist:
                    pass

        return HttpResponse(status=200)


def _reviewable_enrollments_qs():
    return Enrollment.objects.filter(
        Q(payment_proof__isnull=False) & ~Q(payment_proof='')
        | Q(payment_method='stripe')
        | Q(payment_method='manual')
    )


class EnrollmentListAdminView(AdminOrAccountsStaffMixin, ListView):
    template_name = 'enrollments/enrollment_review.html'
    context_object_name = 'enrollments'

    def get_queryset(self):
        queryset = (
            _reviewable_enrollments_qs()
            .select_related('student', 'course', 'approved_by')
            .order_by('-requested_at')
        )
        status_filter = self.request.GET.get('status', '')
        course_filter = self.request.GET.get('course', '')
        if status_filter:
            queryset = queryset.filter(status=status_filter)
        if course_filter:
            queryset = queryset.filter(course_id=course_filter)
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['status_filter'] = self.request.GET.get('status', '')
        context['selected_course'] = self.request.GET.get('course', '')
        base_qs = _reviewable_enrollments_qs()
        context['total_count'] = base_qs.count()
        context['pending_count'] = base_qs.filter(status='pending').count()
        context['approved_count'] = base_qs.filter(status='approved').count()
        context['rejected_count'] = base_qs.filter(status='rejected').count()
        context['courses'] = Course.objects.all().order_by('title')
        return context


class EnrollmentApproveView(AdminOrAccountsStaffMixin, View):
    def post(self, request, pk):
        enrollment = get_object_or_404(Enrollment, pk=pk)
        approve_enrollment(enrollment, request.user)
        messages.success(
            request,
            f"Enrollment for '{enrollment.student.username}' in '{enrollment.course.title}' approved."
        )
        return redirect('enrollments:enrollment_review')


class EnrollmentRejectView(AdminOrAccountsStaffMixin, View):
    def post(self, request, pk):
        enrollment = get_object_or_404(Enrollment, pk=pk)
        reason = request.POST.get('rejection_reason', '').strip()
        if not reason:
            messages.error(request, "Please provide a rejection reason.")
            return redirect('enrollments:enrollment_review')
        reject_enrollment(enrollment, request.user, reason=reason)
        messages.warning(
            request,
            f"Enrollment for '{enrollment.student.username}' in '{enrollment.course.title}' rejected."
        )
        return redirect('enrollments:enrollment_review')


class EnrollmentDetailView(AdminOrAccountsStaffMixin, DetailView):
    model = Enrollment
    template_name = 'enrollments/enrollment_detail.html'
    context_object_name = 'enrollment'


class TeacherEnrollmentListView(TeacherRequiredMixin, ListView):
    """Roster of students in the teacher's assigned courses (no fees/payments)."""
    template_name = 'enrollments/teacher_enrollment_review.html'
    context_object_name = 'enrollments'

    def get_queryset(self):
        queryset = (
            Enrollment.objects.filter(course__teachers=self.request.user)
            .select_related('student', 'course')
            .order_by('-requested_at')
        )
        status_filter = self.request.GET.get('status', '')
        if status_filter:
            queryset = queryset.filter(status=status_filter)
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['status_filter'] = self.request.GET.get('status', '')
        base_qs = Enrollment.objects.filter(course__teachers=self.request.user)
        context['total_count'] = base_qs.count()
        context['pending_count'] = base_qs.filter(status='pending').count()
        context['approved_count'] = base_qs.filter(status='approved').count()
        context['rejected_count'] = base_qs.filter(status='rejected').count()
        return context


class TeacherEnrollmentDetailView(TeacherRequiredMixin, DetailView):
    """Student enrollment detail for assigned courses — no payment/fee data."""
    model = Enrollment
    template_name = 'enrollments/teacher_enrollment_detail.html'
    context_object_name = 'enrollment'

    def get_object(self):
        enrollment = get_object_or_404(Enrollment, pk=self.kwargs['pk'])
        if not enrollment.course.teachers.filter(pk=self.request.user.pk).exists():
            raise PermissionDenied
        return enrollment
