# finance/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin
from core.permissions import BursarRequiredMixin, ManagementRequiredMixin, can
from django.contrib import messages
from django.urls import reverse_lazy
from django.db.models import Q, Sum, Count
from django.http import JsonResponse, HttpResponse
from django.utils import timezone
from datetime import datetime, timedelta, date as date_type
import csv

from .models import (
    FeeStructure, SchoolItemPrice, StudentEnrollmentFee,
    Invoice, InvoiceItem, Payment,
    PaymentReminder, Discount, StudentDiscount,
    ExpenseCategory, Expense, FinancialReport
)
from students.models import Student
from academics.models import SchoolClass
from core.models import CustomUser


# ============================================================
# FEE STRUCTURE VIEWS
# ============================================================

class FeeStructureListView(LoginRequiredMixin, ListView):
    """List all fee structures grouped by section."""
    model = FeeStructure
    template_name = 'finance/fee_structure.html'
    context_object_name = 'fees'
    paginate_by = 20

    def get_queryset(self):
        qs = FeeStructure.objects.all()

        year = self.request.GET.get('academic_year')
        if year:
            qs = qs.filter(academic_year=year)

        section = self.request.GET.get('section')
        if section:
            qs = qs.filter(section=section)

        active = self.request.GET.get('active')
        if active == 'active':
            qs = qs.filter(is_active=True)
        elif active == 'inactive':
            qs = qs.filter(is_active=False)

        return qs.order_by('-academic_year', 'section', 'class_group')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['section_choices'] = FeeStructure._meta.get_field('section').choices
        context['academic_years'] = (
            FeeStructure.objects.values_list('academic_year', flat=True).distinct()
        )
        return context


class FeeStructureDetailView(LoginRequiredMixin, DetailView):
    """View a single fee structure row."""
    model = FeeStructure
    template_name = 'finance/fee_detail.html'
    context_object_name = 'fee'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['applicable_classes'] = self.object.specific_classes.all()
        return context


class FeeStructureCreateView(BursarRequiredMixin, CreateView):
    """Create a new fee structure row."""
    model = FeeStructure
    fields = [
        'section', 'class_group', 'academic_year',
        'registration_new', 'registration_old',
        'total_fees', 'installment_1', 'installment_2',
        'specific_classes', 'is_active', 'description',
    ]
    template_name = 'finance/fee_form.html'
    success_url = reverse_lazy('finance:fee_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Add New Fee Structure'
        return context

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        messages.success(self.request, 'Fee structure created successfully!')
        return super().form_valid(form)


class FeeStructureUpdateView(BursarRequiredMixin, UpdateView):
    """Edit an existing fee structure."""
    model = FeeStructure
    fields = [
        'section', 'class_group', 'academic_year',
        'registration_new', 'registration_old',
        'total_fees', 'installment_1', 'installment_2',
        'specific_classes', 'is_active', 'description',
    ]
    template_name = 'finance/fee_form.html'
    success_url = reverse_lazy('finance:fee_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Edit {self.object.name}'
        return context

    def form_valid(self, form):
        messages.success(self.request, 'Fee structure updated successfully!')
        return super().form_valid(form)


class FeeStructureDeleteView(BursarRequiredMixin, DeleteView):
    """Delete a fee structure."""
    model = FeeStructure
    success_url = reverse_lazy('finance:fee_list')
    template_name = 'finance/fee_confirm_delete.html'

    def delete(self, request, *args, **kwargs):
        messages.success(request, 'Fee structure deleted.')
        return super().delete(request, *args, **kwargs)


# ============================================================
# SCHOOL ITEM PRICE VIEWS  (uniforms, sportswear, etc.)
# ============================================================

class SchoolItemPriceListView(BursarRequiredMixin, ListView):
    """List all school item prices."""
    model = SchoolItemPrice
    template_name = 'finance/school_item_list.html'
    context_object_name = 'items'

    def get_queryset(self):
        return SchoolItemPrice.objects.all().order_by('item_type', 'name')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['item_type_choices'] = SchoolItemPrice.ITEM_TYPE_CHOICES
        return context


class SchoolItemPriceCreateView(BursarRequiredMixin, CreateView):
    """Add a new school item price."""
    model = SchoolItemPrice
    fields = ['item_type', 'name', 'price', 'academic_year', 'is_active', 'description']
    template_name = 'finance/school_item_form.html'
    success_url = reverse_lazy('finance:school_item_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Add School Item Price'
        return context

    def form_valid(self, form):
        messages.success(self.request, 'Item price saved.')
        return super().form_valid(form)


class SchoolItemPriceUpdateView(BursarRequiredMixin, UpdateView):
    """Edit a school item price."""
    model = SchoolItemPrice
    fields = ['item_type', 'name', 'price', 'academic_year', 'is_active', 'description']
    template_name = 'finance/school_item_form.html'
    success_url = reverse_lazy('finance:school_item_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Edit {self.object.name}'
        return context

    def form_valid(self, form):
        messages.success(self.request, 'Item price updated.')
        return super().form_valid(form)


# ============================================================
# STUDENT ENROLLMENT FEE VIEWS
# ============================================================

class StudentEnrollmentFeeListView(BursarRequiredMixin, ListView):
    """List all student enrollment fee records."""
    model = StudentEnrollmentFee
    template_name = 'finance/enrollment_fee_list.html'
    context_object_name = 'enrollment_fees'
    paginate_by = 30

    def get_queryset(self):
        qs = StudentEnrollmentFee.objects.select_related('student', 'fee_structure')

        year = self.request.GET.get('academic_year')
        if year:
            qs = qs.filter(academic_year=year)

        search = self.request.GET.get('q')
        if search:
            qs = qs.filter(
                Q(student__first_name__icontains=search) |
                Q(student__last_name__icontains=search) |
                Q(student__student_id__icontains=search)
            )
        return qs.order_by('student__last_name', 'student__first_name')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['academic_years'] = (
            StudentEnrollmentFee.objects.values_list('academic_year', flat=True).distinct()
        )
        return context


class StudentEnrollmentFeeCreateView(BursarRequiredMixin, CreateView):
    """Create a student enrollment fee record."""
    model = StudentEnrollmentFee
    fields = [
        'student', 'fee_structure', 'academic_year',
        'is_new_pupil', 'installment_1_due', 'installment_2_due', 'notes',
    ]
    template_name = 'finance/enrollment_fee_form.html'
    success_url = reverse_lazy('finance:enrollment_fee_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Enroll Student — Assign Fee Structure'
        context['fee_structures'] = FeeStructure.objects.filter(is_active=True)
        return context

    def form_valid(self, form):
        messages.success(self.request, 'Enrollment fee record created.')
        return super().form_valid(form)


# ============================================================
# INVOICE VIEWS
# ============================================================

class InvoiceListView(LoginRequiredMixin, ListView):
    """List all invoices with search and filtering."""
    model = Invoice
    template_name = 'finance/invoices.html'
    context_object_name = 'invoices'
    paginate_by = 20

    def get_queryset(self):
        qs = Invoice.objects.all().select_related('student', 'created_by')

        search = self.request.GET.get('q')
        if search:
            qs = qs.filter(
                Q(invoice_number__icontains=search) |
                Q(student__first_name__icontains=search) |
                Q(student__last_name__icontains=search)
            )

        status = self.request.GET.get('status')
        if status:
            qs = qs.filter(status=status)

        year = self.request.GET.get('academic_year')
        if year:
            qs = qs.filter(academic_year=year)

        # Filter by invoice_type (replaces old 'term' filter)
        invoice_type = self.request.GET.get('invoice_type')
        if invoice_type:
            qs = qs.filter(invoice_type=invoice_type)

        return qs.order_by('-issue_date')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['total_invoices'] = Invoice.objects.count()
        context['total_amount']   = Invoice.objects.aggregate(Sum('total_amount'))['total_amount__sum'] or 0
        context['total_paid']     = Invoice.objects.aggregate(Sum('amount_paid'))['amount_paid__sum'] or 0
        context['total_balance']  = context['total_amount'] - context['total_paid']
        context['status_choices']       = Invoice.STATUS_CHOICES
        context['invoice_type_choices'] = Invoice.INVOICE_TYPE_CHOICES
        context['academic_years'] = Invoice.objects.values_list('academic_year', flat=True).distinct()
        context['today'] = timezone.now().date()
        return context


class InvoiceDetailView(LoginRequiredMixin, DetailView):
    """View a single invoice."""
    model = Invoice
    template_name = 'finance/invoice_detail.html'
    context_object_name = 'invoice'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['items']    = self.object.items.all()
        context['payments'] = self.object.payments.all().order_by('-payment_date')
        context['reminders'] = self.object.reminders.all().order_by('-sent_date')
        return context


class InvoiceCreateView(BursarRequiredMixin, CreateView):
    """Create a new invoice."""
    model = Invoice
    fields = [
        'student', 'enrollment_fee', 'academic_year', 'invoice_type',
        'due_date', 'total_amount', 'notes',
    ]
    template_name = 'finance/invoice_form.html'
    success_url = reverse_lazy('finance:invoice_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Create New Invoice'
        context['students'] = Student.objects.filter(status='active')
        return context

    def form_valid(self, form):
        form.instance.balance    = form.instance.total_amount
        form.instance.created_by = self.request.user
        form.instance.status     = 'draft'
        messages.success(self.request, 'Invoice created successfully!')
        return super().form_valid(form)


class InvoiceUpdateView(BursarRequiredMixin, UpdateView):
    """Edit an existing invoice."""
    model = Invoice
    fields = ['due_date', 'notes', 'status']
    template_name = 'finance/invoice_form.html'
    success_url = reverse_lazy('finance:invoice_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Edit Invoice {self.object.invoice_number}'
        return context

    def form_valid(self, form):
        messages.success(self.request, 'Invoice updated successfully!')
        return super().form_valid(form)


class InvoiceDeleteView(BursarRequiredMixin, DeleteView):
    """Delete an invoice."""
    model = Invoice
    success_url = reverse_lazy('finance:invoice_list')
    template_name = 'finance/invoice_confirm_delete.html'

    def delete(self, request, *args, **kwargs):
        messages.success(request, 'Invoice deleted.')
        return super().delete(request, *args, **kwargs)


def mark_invoice_as_sent(request, pk):
    invoice = get_object_or_404(Invoice, pk=pk)
    invoice.status = 'sent'
    invoice.save()
    messages.success(request, f'Invoice {invoice.invoice_number} marked as sent.')
    return redirect('finance:invoice_detail', pk=pk)


def send_payment_reminder(request, pk):
    invoice = get_object_or_404(Invoice, pk=pk)
    if request.method == 'POST':
        reminder_type = request.POST.get('reminder_type')
        sent_to       = request.POST.get('sent_to')
        PaymentReminder.objects.create(
            invoice=invoice,
            reminder_type=reminder_type,
            sent_to=sent_to,
            sent_by=request.user,
        )
        invoice.last_reminder_sent = timezone.now().date()
        invoice.save()
        messages.success(request, f'Reminder sent to {sent_to}.')
    return redirect('finance:invoice_detail', pk=pk)

# ... [Fee Structure, School Item, and Enrollment views remain unchanged] ...

# ============================================================
# PAYMENT VIEWS
# ============================================================

class PaymentListView(LoginRequiredMixin, ListView):
    """List all payments."""
    model = Payment
    template_name = 'finance/payments.html'
    context_object_name = 'payments'
    paginate_by = 20

    def get_queryset(self):
        qs = Payment.objects.all().select_related('invoice', 'student', 'received_by')
        from_date = self.request.GET.get('from_date')
        to_date   = self.request.GET.get('to_date')
        if from_date:
            qs = qs.filter(payment_date__gte=from_date)
        if to_date:
            qs = qs.filter(payment_date__lte=to_date)

        method = self.request.GET.get('method')
        if method:
            qs = qs.filter(payment_method=method)

        status = self.request.GET.get('status')
        if status:
            qs = qs.filter(status=status)

        return qs.order_by('-payment_date')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['total_payments']  = Payment.objects.filter(status='completed').aggregate(Sum('amount'))['amount__sum'] or 0
        context['payment_count']   = Payment.objects.filter(status='completed').count()
        context['payment_methods'] = Payment.PAYMENT_METHOD_CHOICES
        context['status_choices']  = Payment.PAYMENT_STATUS_CHOICES
        return context

class PaymentDetailView(LoginRequiredMixin, DetailView):
    model = Payment
    template_name = 'finance/payment_detail.html'
    context_object_name = 'payment'

class PaymentCreateView(BursarRequiredMixin, CreateView):
    """Record a new payment with robust pre-population logic."""
    model = Payment
    fields = [
        'invoice', 'amount', 'payment_method', 'payment_date',
        'mobile_money_provider', 'mobile_money_number',
        'transaction_id', 'bank_name', 'cheque_number',
        'notes', 'status',
    ]
    template_name = 'finance/payment_form.html'
    success_url = reverse_lazy('finance:payment_list')

    def get_initial(self):
        """
        Handles pre-population by checking multiple potential URL parameters.
        Supports both ?invoice= and ?invoice_id=
        """
        initial = super().get_initial()
        # Check both common parameter names to be safe
        invoice_id = self.request.GET.get('invoice') or self.request.GET.get('invoice_id')
        
        if invoice_id:
            try:
                invoice = get_object_or_404(Invoice, pk=invoice_id)
                initial['invoice'] = invoice.pk
                # Automatically set amount to the current remaining balance
                initial['amount'] = invoice.balance
            except (ValueError, TypeError):
                pass
        return initial

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Record New Payment'
        invoice_id = self.request.GET.get('invoice') or self.request.GET.get('invoice_id')
        if invoice_id:
            try:
                context['selected_invoice'] = Invoice.objects.get(pk=invoice_id)
            except (Invoice.DoesNotExist, ValueError):
                pass
        return context

    def form_valid(self, form):
        # Check for duplicate payment (same invoice + amount within 5 minutes)
        from datetime import timedelta
        invoice = form.instance.invoice
        amount = form.cleaned_data['amount']
        payment_date = form.cleaned_data['payment_date']
   
        five_min_ago = timezone.now() - timedelta(minutes=5)
        duplicate = Payment.objects.filter(
            invoice=invoice,
            amount=amount,
            payment_date=payment_date,
            created_at__gte=five_min_ago
        ).exists()
   
        if duplicate:
            messages.warning(self.request, 'A payment with these exact details was already recorded in   the last 5 minutes.')
            return redirect('finance:payment_list')
   
        # Normal processing
        form.instance.student = form.instance.invoice.student
        form.instance.received_by = self.request.user
        form.instance.payment_number = f"PAY-{timezone.now().year}-{Payment.objects.count() + 1:04d}"
   
        messages.success(self.request, 'Payment recorded successfully!')
  return super().form_valid(form)

class PaymentUpdateView(BursarRequiredMixin, UpdateView):
    model = Payment
    fields = ['status', 'notes', 'receipt_issued', 'receipt_number']
    template_name = 'finance/payment_form.html'
    success_url = reverse_lazy('finance:payment_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Update Payment {self.object.payment_number}'
        return context

    def form_valid(self, form):
        messages.success(self.request, 'Payment updated!')
        return super().form_valid(form)


def generate_receipt(request, pk):
    """Generate and display a printable receipt for a payment."""
    payment = get_object_or_404(Payment, pk=pk)

    if not payment.receipt_issued:
        payment.receipt_issued      = True
        payment.receipt_issued_date = timezone.now().date()
        if not payment.receipt_number:
            payment.receipt_number = f"RCT-{payment.payment_number}"
        payment.save()

    items = payment.invoice.items.all() if hasattr(payment.invoice, 'items') else []

    context = {
        'payment': payment,
        'items':   items,
        'amount_in_words': _amount_in_words(payment.amount),
    }
    return render(request, 'finance/receipt.html', context)


# ============================================================
# EXPENSE VIEWS
# ============================================================

class ExpenseListView(LoginRequiredMixin, ListView):
    model = Expense
    template_name = 'finance/expense_list.html'
    context_object_name = 'expenses'
    paginate_by = 20

    def get_queryset(self):
        qs = Expense.objects.all().select_related('category', 'approved_by', 'created_by')

        from_date = self.request.GET.get('from_date')
        to_date   = self.request.GET.get('to_date')
        if from_date:
            qs = qs.filter(expense_date__gte=from_date)
        if to_date:
            qs = qs.filter(expense_date__lte=to_date)

        category = self.request.GET.get('category')
        if category:
            qs = qs.filter(category_id=category)

        return qs.order_by('-expense_date')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['total_expenses'] = Expense.objects.aggregate(Sum('amount'))['amount__sum'] or 0
        context['categories']     = ExpenseCategory.objects.all()
        return context


class ExpenseCreateView(BursarRequiredMixin, CreateView):
    model = Expense
    fields = ['category', 'description', 'amount', 'expense_date', 'payment_method', 'vendor', 'notes', 'receipt_file']
    template_name = 'finance/expense_form.html'
    success_url = reverse_lazy('finance:expense_list')

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        messages.success(self.request, 'Expense recorded successfully!')
        return super().form_valid(form)


class ExpenseUpdateView(BursarRequiredMixin, UpdateView):
    model = Expense
    fields = ['description', 'amount', 'payment_method', 'vendor', 'notes']
    template_name = 'finance/expense_form.html'
    success_url = reverse_lazy('finance:expense_list')

    def form_valid(self, form):
        messages.success(self.request, 'Expense updated!')
        return super().form_valid(form)


def approve_expense(request, pk):
    expense = get_object_or_404(Expense, pk=pk)
    expense.approved_by = request.user
    expense.save()
    messages.success(request, 'Expense approved.')
    return redirect('finance:expense_list')


# ============================================================
# EXPENSE CATEGORY VIEWS (unchanged — already correct)
# ============================================================

class ExpenseCategoryListView(BursarRequiredMixin, TemplateView):
    template_name = 'finance/expense_categories.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['categories'] = ExpenseCategory.objects.all().order_by('name')
        return context

    def post(self, request, *args, **kwargs):
        action = request.POST.get('action')

        if action == 'seed':
            defaults = [
                ('Salaries & Wages',        'Salaires et traitements',    'Staff salaries, bonuses and allowances'),
                ('Teaching Materials',       'Matériel pédagogique',       'Textbooks, exercise books, chalk, markers'),
                ('Stationery & Supplies',    'Papeterie et fournitures',   'Office and administrative supplies'),
                ('Utilities',               'Services publics',            'Electricity, water, internet, telephone'),
                ('Maintenance & Repairs',   'Entretien et réparations',   'Building, equipment and vehicle repairs'),
                ('Transport',               'Transport',                   'School bus fuel, maintenance and driver costs'),
                ('Canteen & Catering',      'Cantine et restauration',    'Food supplies and canteen operations'),
                ('Uniforms & Sportswear',   'Uniformes et tenues',        'School uniform procurement'),
                ('Exams & Certificates',    'Examens et diplômes',        'Exam fees, printing and certification costs'),
                ('Events & Activities',     'Événements et activités',    'School celebrations, sports day, excursions'),
                ('IT & Technology',         'Informatique et technologie', 'Computers, printers, software'),
                ('Cleaning & Sanitation',   'Nettoyage et hygiène',       'Cleaning supplies and sanitation'),
                ('Security',                'Sécurité',                   'Security staff and equipment'),
                ('Bank & Admin Charges',    'Frais bancaires et admin',   'Bank fees, government levies, admin costs'),
                ('Miscellaneous',           'Divers',                     'Other expenses not categorised above'),
            ]
            created = 0
            for name, name_fr, desc in defaults:
                _, was_created = ExpenseCategory.objects.get_or_create(
                    name=name,
                    defaults={'name_fr': name_fr, 'description': desc, 'is_active': True}
                )
                if was_created:
                    created += 1
            if created:
                messages.success(request, f'✓ {created} default categor{"ies" if created != 1 else "y"} added.')
            else:
                messages.info(request, 'All default categories already exist.')

        elif action == 'add':
            name        = request.POST.get('name', '').strip()
            name_fr     = request.POST.get('name_fr', '').strip()
            description = request.POST.get('description', '').strip()
            if not name:
                messages.error(request, 'Category name is required.')
            elif ExpenseCategory.objects.filter(name__iexact=name).exists():
                messages.error(request, f'A category named "{name}" already exists.')
            else:
                ExpenseCategory.objects.create(name=name, name_fr=name_fr, description=description)
                messages.success(request, f'Category "{name}" added.')

        elif action == 'delete':
            pk  = request.POST.get('pk')
            cat = get_object_or_404(ExpenseCategory, pk=pk)
            if cat.expenses.exists():
                messages.error(request, f'Cannot delete "{cat.name}" — it has existing expenses.')
            else:
                cat.delete()
                messages.success(request, 'Category deleted.')

        elif action == 'toggle':
            pk  = request.POST.get('pk')
            cat = get_object_or_404(ExpenseCategory, pk=pk)
            cat.is_active = not cat.is_active
            cat.save()
            messages.success(request, f'"{cat.name}" {"activated" if cat.is_active else "deactivated"}.')

        return redirect('finance:expense_categories')


# ============================================================
# DISCOUNT VIEWS
# ============================================================

class DiscountListView(LoginRequiredMixin, ListView):
    model = Discount
    template_name = 'finance/discount_list.html'
    context_object_name = 'discounts'


class DiscountCreateView(BursarRequiredMixin, CreateView):
    model = Discount
    fields = ['name', 'name_fr', 'discount_type', 'value', 'applies_to', 'specific_fees', 'description', 'is_active']
    template_name = 'finance/discount_form.html'
    success_url = reverse_lazy('finance:discount_list')


class StudentDiscountCreateView(BursarRequiredMixin, CreateView):
    model = StudentDiscount
    fields = ['student', 'discount', 'academic_year', 'reason']
    template_name = 'finance/student_discount_form.html'
    success_url = reverse_lazy('finance:student_discount_list')

    def form_valid(self, form):
        form.instance.approved_by = self.request.user
        messages.success(self.request, 'Discount applied to student.')
        return super().form_valid(form)


# ============================================================
# BURSAR DASHBOARD
# ============================================================

class BursarDashboardView(LoginRequiredMixin, TemplateView):
    template_name = 'finance/bursar_dashboard.html'

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return redirect('login')
        if hasattr(request.user, 'staff_profile'):
            allowed = ['bursar', 'proprietor', 'admin', 'head_master', 'head_mistress']
            if request.user.staff_profile.role in allowed:
                return super().dispatch(request, *args, **kwargs)
        messages.error(request, "You don't have permission to access the bursar dashboard.")
        return redirect('dashboard')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        today     = timezone.now().date()
        first_day = today.replace(day=1)

        context['total_revenue'] = Payment.objects.filter(
            status='completed'
        ).aggregate(Sum('amount'))['amount__sum'] or 0

        context['monthly_revenue'] = Payment.objects.filter(
            status='completed', payment_date__gte=first_day
        ).aggregate(Sum('amount'))['amount__sum'] or 0

        context['pending_invoices'] = Invoice.objects.filter(status__in=['sent', 'partial']).count()
        context['overdue_invoices'] = Invoice.objects.filter(status='overdue').count()

        context['recent_payments'] = Payment.objects.filter(
            status='completed'
        ).select_related('student').order_by('-payment_date')[:10]

        context['upcoming_due'] = Invoice.objects.filter(
            status__in=['sent', 'partial'],
            due_date__gte=today,
            due_date__lte=today + timedelta(days=7),
        ).select_related('student')[:10]

        context['monthly_expenses'] = Expense.objects.filter(
            expense_date__gte=first_day
        ).aggregate(Sum('amount'))['amount__sum'] or 0

        context['net_income'] = context['monthly_revenue'] - context['monthly_expenses']

        # Chart: last 6 months
        labels, data = [], []
        for i in range(5, -1, -1):
            month = today - timedelta(days=30 * i)
            rev = Payment.objects.filter(
                status='completed',
                payment_date__year=month.year,
                payment_date__month=month.month,
            ).aggregate(Sum('amount'))['amount__sum'] or 0
            labels.append(month.strftime('%b %Y'))
            data.append(float(rev))

        context['chart_labels'] = labels
        context['chart_data']   = data
        return context


# ============================================================
# FINANCIAL REPORTS
# ============================================================

class FinancialReportsView(LoginRequiredMixin, TemplateView):
    template_name = 'finance/reports.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['total_revenue']  = Payment.objects.filter(status='completed').aggregate(Sum('amount'))['amount__sum'] or 0
        context['total_expenses'] = Expense.objects.aggregate(Sum('amount'))['amount__sum'] or 0
        context['outstanding']    = Invoice.objects.filter(
            status__in=['sent', 'partial', 'overdue']
        ).aggregate(Sum('balance'))['balance__sum'] or 0

        payment_methods = {}
        for code, label in Payment.PAYMENT_METHOD_CHOICES:
            total = Payment.objects.filter(
                status='completed', payment_method=code
            ).aggregate(Sum('amount'))['amount__sum'] or 0
            if total > 0:
                payment_methods[label] = float(total)
        context['payment_methods'] = payment_methods
        context['recent_reports']  = FinancialReport.objects.all().order_by('-generated_at')[:5]
        return context


def generate_financial_report(request):
    if request.method == 'POST':
        report_type  = request.POST.get('report_type')
        start_date   = request.POST.get('start_date')
        end_date     = request.POST.get('end_date')

        total_revenue  = Payment.objects.filter(
            status='completed', payment_date__gte=start_date, payment_date__lte=end_date
        ).aggregate(Sum('amount'))['amount__sum'] or 0

        total_expenses = Expense.objects.filter(
            expense_date__gte=start_date, expense_date__lte=end_date
        ).aggregate(Sum('amount'))['amount__sum'] or 0

        report = FinancialReport.objects.create(
            title=f"{report_type.title()} Report {start_date} to {end_date}",
            report_type=report_type,
            start_date=start_date,
            end_date=end_date,
            total_revenue=total_revenue,
            total_expenses=total_expenses,
            net_profit=total_revenue - total_expenses,
            generated_by=request.user,
        )
        messages.success(request, f'Report generated: {report.title}')
    return redirect('finance:financial_reports')


# ============================================================
# EXPORT FUNCTIONS
# ============================================================

def export_invoices_csv(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="invoices_export.csv"'
    writer = csv.writer(response)
    writer.writerow([
        'Invoice #', 'Student', 'Academic Year', 'Type',
        'Issue Date', 'Due Date', 'Total', 'Paid', 'Balance', 'Status',
    ])
    for inv in Invoice.objects.select_related('student').all():
        writer.writerow([
            inv.invoice_number,
            str(inv.student),
            inv.academic_year,
            inv.get_invoice_type_display(),   # was get_term_display()
            inv.issue_date,
            inv.due_date,
            inv.total_amount,
            inv.amount_paid,
            inv.balance,
            inv.get_status_display(),
        ])
    return response


def export_payments_csv(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="payments_export.csv"'
    writer = csv.writer(response)
    writer.writerow(['Payment #', 'Invoice #', 'Student', 'Amount', 'Method', 'Date', 'Status'])
    for p in Payment.objects.select_related('invoice', 'student').all():
        writer.writerow([
            p.payment_number,
            p.invoice.invoice_number,
            str(p.student),
            p.amount,
            p.get_payment_method_display(),
            p.payment_date,
            p.get_status_display(),
        ])
    return response


# ============================================================
# API ENDPOINT
# ============================================================

def get_finance_stats(request):
    stats = {
        'total_revenue':    float(Payment.objects.filter(status='completed').aggregate(Sum('amount'))['amount__sum'] or 0),
        'pending_invoices': Invoice.objects.filter(status='sent').count(),
        'overdue_invoices': Invoice.objects.filter(status='overdue').count(),
        'paid_invoices':    Invoice.objects.filter(status='paid').count(),
        'total_expenses':   float(Expense.objects.aggregate(Sum('amount'))['amount__sum'] or 0),
        'by_payment_method': list(
            Payment.objects.values('payment_method')
            .annotate(total=Sum('amount'))
            .filter(status='completed')
        ),
    }
    return JsonResponse(stats)


# ============================================================
# RECEIPT — helper + view
# ============================================================

def _amount_in_words(amount):
    ones = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven',
            'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen',
            'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']
    tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty',
            'sixty', 'seventy', 'eighty', 'ninety']

    def _below_1000(n):
        if n == 0:
            return ''
        elif n < 20:
            return ones[n]
        elif n < 100:
            return tens[n // 10] + (' ' + ones[n % 10] if n % 10 else '')
        else:
            return ones[n // 100] + ' hundred' + (' ' + _below_1000(n % 100) if n % 100 else '')

    n = int(amount)
    if n == 0:
        return 'zero francs'
    parts = []
    if n >= 1_000_000:
        parts.append(_below_1000(n // 1_000_000) + ' million')
        n %= 1_000_000
    if n >= 1_000:
        parts.append(_below_1000(n // 1_000) + ' thousand')
        n %= 1_000
    if n > 0:
        parts.append(_below_1000(n))
    return ' '.join(parts).strip().capitalize() + ' francs CFA'


# ============================================================
# BULK INVOICE GENERATION
# ============================================================

# Map class levels to fee tier class_group keys in the new model
NURSERY_LEVELS  = {'pre_nursery', 'nursery1', 'nursery2', 'moyenne_section', 'grande_section'}
PRIMARY_LEVELS  = {'sil', 'cp', 'ce1', 'ce2', 'cm1', 'cm2', 'class1', 'class2', 'class3', 'class4', 'class5'}
CLASS6_LEVEL    = 'class6'

# Map each class level to its fee class_group key in the new FeeStructure model
LEVEL_TO_CLASS_GROUP = {
    'pre_nursery':      'pre_nursery',
    'nursery1':         'nursery',
    'nursery2':         'nursery',
    'moyenne_section':  'maternelle',
    'grande_section':   'maternelle',
    'sil':              'sil_cm1',
    'cp':               'sil_cm1',
    'ce1':              'sil_cm1',
    'ce2':              'sil_cm1',
    'cm1':              'sil_cm1',
    'cm2':              'cm2',
    'class1':           'primary_1_5',
    'class2':           'primary_1_5',
    'class3':           'primary_1_5',
    'class4':           'primary_1_5',
    'class5':           'primary_1_5',
    'class6':           'primary_6',
}

LEVEL_TO_SECTION = {
    'pre_nursery': 'anglo', 'nursery1': 'anglo', 'nursery2': 'anglo',
    'class1': 'anglo', 'class2': 'anglo', 'class3': 'anglo',
    'class4': 'anglo', 'class5': 'anglo', 'class6': 'anglo',
    'moyenne_section': 'bilingual', 'grande_section': 'bilingual',
    'sil': 'bilingual', 'cp': 'bilingual', 'ce1': 'bilingual',
    'ce2': 'bilingual', 'cm1': 'bilingual', 'cm2': 'bilingual',
}


def _get_fee_for_student(student, invoice_type, academic_year):
    """
    Return the FeeStructure that applies to this student, or None.
    Uses the new class_group + section model instead of fee_type/class_level.
    """
    if not student.current_class:
        return None
    level       = student.current_class.level
    class_group = LEVEL_TO_CLASS_GROUP.get(level)
    section     = student.current_class.section_type  # 'english', 'french', 'bilingual'

    # Map section_type to FeeStructure section field
    # french section uses bilingual fees; english uses anglo
    fee_section = 'bilingual' if section in ('bilingual', 'french') else 'anglo'

    # Special case: pre_nursery in bilingual/french section maps to maternelle tier
    if level == 'pre_nursery' and section in ('bilingual', 'french'):
        class_group = 'maternelle'

    return FeeStructure.objects.filter(
        is_active=True,
        academic_year=academic_year,
        section=fee_section,
        class_group=class_group,
    ).first()


def _invoice_amount_for_type(fee, invoice_type, is_new_pupil=False):
    """Return the correct XAF amount for the given invoice_type."""
    if invoice_type == 'registration':
        return fee.get_registration_fee(is_new_pupil)
    if invoice_type == 'installment_1':
        return int(fee.installment_1)
    if invoice_type == 'installment_2':
        return int(fee.installment_2)
    return int(fee.total_fees)


def _build_year_choices():
    current = timezone.now().year
    return [f"{y}-{y+1}" for y in range(current - 3, current + 8)]


def _filter_students_by_scope(active_students, scope, class_ids):
    if scope == 'nursery':
        return active_students.filter(current_class__level__in=NURSERY_LEVELS)
    if scope == 'primary':
        return active_students.filter(current_class__level__in=PRIMARY_LEVELS)
    if scope == 'class6':
        return active_students.filter(current_class__level=CLASS6_LEVEL)
    if scope == 'english':
        return active_students.filter(current_class__section_type='english')
    if scope == 'bilingual':
        return active_students.filter(current_class__section_type='bilingual')
    if scope == 'french':
        return active_students.filter(current_class__section_type='french')
    if scope == 'specific' and class_ids:
        return active_students.filter(current_class__id__in=class_ids)
    return active_students


class BulkInvoiceGenerateView(BursarRequiredMixin, TemplateView):
    """
    Generate invoices for all active students for a given invoice_type/academic_year.
    Shows a preview; creates on confirmed POST.
    Skips students who already have an invoice of the same type/year.
    """
    template_name = 'finance/bulk_invoice.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        current = timezone.now().year
        context['year_choices']  = _build_year_choices()
        context['default_year']  = f"{current}-{current+1}"
        context['all_classes']   = SchoolClass.objects.all().order_by('level', 'section')
        context['invoice_type_choices'] = Invoice.INVOICE_TYPE_CHOICES
        return context

    def post(self, request, *args, **kwargs):
        academic_year = request.POST.get('academic_year', '').strip()
        invoice_type  = request.POST.get('invoice_type', '').strip()
        due_date      = request.POST.get('due_date', '').strip()
        scope         = request.POST.get('scope', 'all')
        class_ids     = request.POST.getlist('class_ids')
        confirmed     = request.POST.get('confirmed') == 'yes'

        if not academic_year or not invoice_type or not due_date:
            messages.error(request, 'Academic year, invoice type, and due date are all required.')
            return redirect('finance:bulk_invoice')

        try:
            due_date_obj = date_type.fromisoformat(due_date)
        except (ValueError, TypeError):
            messages.error(request, 'Invalid due date.')
            return redirect('finance:bulk_invoice')

        due_date_str = due_date_obj.isoformat()

        active_students = Student.objects.filter(
            status='active'
        ).select_related('current_class').order_by('current_class__level', 'last_name')

        active_students = _filter_students_by_scope(active_students, scope, class_ids)

        preview    = []
        skip_count = 0

        for student in active_students:
            # Skip if this invoice type already exists for this student/year
            existing = Invoice.objects.filter(
                student=student,
                academic_year=academic_year,
                invoice_type=invoice_type,
            ).first()
            if existing:
                skip_count += 1
                continue

            fee = _get_fee_for_student(student, invoice_type, academic_year)
            if not fee:
                continue

            # Check if there's an enrollment record to determine new/returning
            enrollment = StudentEnrollmentFee.objects.filter(
                student=student, academic_year=academic_year
            ).first()
            is_new = enrollment.is_new_pupil if enrollment else False

            amount = _invoice_amount_for_type(fee, invoice_type, is_new)

            preview.append({
                'student':      student,
                'fee':          fee,
                'amount':       amount,
                'is_new_pupil': is_new,
            })

        if not confirmed:
            context = self.get_context_data()
            context.update({
                'preview':        preview,
                'skip_count':     skip_count,
                'academic_year':  academic_year,
                'invoice_type':   invoice_type,
                'due_date':       due_date_str,
                'scope':          scope,
                'class_ids':      class_ids,
                'total_to_create': len(preview),
                'total_amount':   sum(p['amount'] for p in preview),
            })
            return self.render_to_response(context)

        # ── Confirmed: create invoices ──────────────────────────────────────
        created_count = 0
        for item in preview:
            student = item['student']
            amount  = item['amount']
            fee     = item['fee']

            enrollment = StudentEnrollmentFee.objects.filter(
                student=student, academic_year=academic_year
            ).first()

            invoice = Invoice.objects.create(
                student=student,
                enrollment_fee=enrollment,
                academic_year=academic_year,
                invoice_type=invoice_type,
                due_date=due_date_obj,
                total_amount=amount,
                balance=amount,
                amount_paid=0,
                status='draft',
                created_by=request.user,
            )

            # Line item description based on type
            desc_map = {
                'registration':  f"Registration Fee ({'New' if item['is_new_pupil'] else 'Returning'} Pupil)",
                'installment_1': '1st Installment — Tuition Fee',
                'installment_2': '2nd Installment — Tuition Fee',
            }
            description = desc_map.get(invoice_type, invoice_type.replace('_', ' ').title())

            InvoiceItem.objects.create(
                invoice=invoice,
                fee_structure=fee,
                description=description,
                quantity=1,
                unit_price=amount,
                discount=0,
                total=amount,
            )
            created_count += 1

        messages.success(
            request,
            f'✓ {created_count} invoice{"s" if created_count != 1 else ""} created for '
            f'{academic_year} — {invoice_type.replace("_", " ").title()}. '
            f'{skip_count} student{"s" if skip_count != 1 else ""} skipped (already invoiced).'
        )
        return redirect('finance:invoice_list')