from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.db.models import Sum, Count, Q, Avg
from django.utils import timezone
from django.core.paginator import Paginator
from django.http import JsonResponse, HttpResponse
from datetime import datetime, timedelta
from decimal import Decimal
import csv
from io import BytesIO

from .models import Expense
from .forms import ExpenseForm, ExpenseFilterForm
from users.decorators import staff_required
from users.models import Branch
from utils.filtering import apply_branch_and_portfolio_filters


def apply_expense_branch_filter(queryset, user):
    """Apply branch filtering to expense queryset based on user permissions"""
    if user.is_superuser:
        return queryset
    
    if hasattr(user, 'accessible_branches') and user.accessible_branches.exists():
        return queryset.filter(branch__in=user.accessible_branches.all())
    elif user.branch:
        return queryset.filter(branch=user.branch)
    
    return queryset


@login_required
@staff_required
def expenses_list(request):
    """List all expenses with filtering and search"""
    
    # Get base queryset with branch filtering
    expenses_qs = Expense.objects.select_related(
        'branch', 'staff', 'loan', 'approved_by'
    ).all()
    
    # Apply branch filtering based on user permissions
    expenses_qs = apply_expense_branch_filter(expenses_qs, request.user)
    
    # Initialize filter form with user
    filter_form = ExpenseFilterForm(request.GET, user=request.user)
    
    # Apply filters
    if filter_form.is_valid():
        # Search
        search = filter_form.cleaned_data.get('search')
        if search:
            expenses_qs = expenses_qs.filter(
                Q(title__icontains=search) |
                Q(description__icontains=search) |
                Q(paid_to__icontains=search) |
                Q(reference_number__icontains=search)
            )
        
        # Category filter
        category = filter_form.cleaned_data.get('category')
        if category:
            expenses_qs = expenses_qs.filter(category=category)
        
        # Payment method filter
        payment_method = filter_form.cleaned_data.get('payment_method')
        if payment_method:
            expenses_qs = expenses_qs.filter(payment_method=payment_method)
        
        # Status filter
        status = filter_form.cleaned_data.get('status')
        if status:
            expenses_qs = expenses_qs.filter(status=status)
        
        # Branch filter
        branch = filter_form.cleaned_data.get('branch')
        if branch:
            expenses_qs = expenses_qs.filter(branch=branch)
        
        # Date range filter
        date_from = filter_form.cleaned_data.get('date_from')
        if date_from:
            expenses_qs = expenses_qs.filter(expense_date__gte=date_from)
        
        date_to = filter_form.cleaned_data.get('date_to')
        if date_to:
            expenses_qs = expenses_qs.filter(expense_date__lte=date_to)
        
        # Amount range filter
        amount_min = filter_form.cleaned_data.get('amount_min')
        if amount_min:
            expenses_qs = expenses_qs.filter(amount__gte=amount_min)
        
        amount_max = filter_form.cleaned_data.get('amount_max')
        if amount_max:
            expenses_qs = expenses_qs.filter(amount__lte=amount_max)
        
        # Loan-related filter
        loan_related = filter_form.cleaned_data.get('loan_related')
        if loan_related == 'yes':
            expenses_qs = expenses_qs.filter(loan__isnull=False)
        elif loan_related == 'no':
            expenses_qs = expenses_qs.filter(loan__isnull=True)
    
    # Calculate summary statistics
    today = timezone.now().date()
    current_month_start = today.replace(day=1)
    current_year_start = today.replace(month=1, day=1)
    
    # Get filtered expenses for statistics
    stats_qs = expenses_qs.filter(status='approved')
    
    total_today = stats_qs.filter(expense_date=today).aggregate(
        total=Sum('amount'))['total'] or Decimal('0')
    
    total_month = stats_qs.filter(
        expense_date__gte=current_month_start
    ).aggregate(total=Sum('amount'))['total'] or Decimal('0')
    
    total_year = stats_qs.filter(
        expense_date__gte=current_year_start
    ).aggregate(total=Sum('amount'))['total'] or Decimal('0')
    
    pending_count = expenses_qs.filter(status='pending').count()
    
    # Get top spending category
    top_category = stats_qs.values('category').annotate(
        total=Sum('amount')
    ).order_by('-total').first()
    
    # Pagination
    paginator = Paginator(expenses_qs, 25)
    page_number = request.GET.get('page', 1)
    expenses = paginator.get_page(page_number)
    
    context = {
        'expenses': expenses,
        'filter_form': filter_form,
        'total_today': total_today,
        'total_month': total_month,
        'total_year': total_year,
        'pending_count': pending_count,
        'top_category': top_category,
        'total_expenses': expenses_qs.count(),
        'today': today.strftime('%Y-%m-%d'),
        'month_start': current_month_start.strftime('%Y-%m-%d'),
        'year_start': current_year_start.strftime('%Y-%m-%d'),
    }
    
    return render(request, 'expenses/expenses_list.html', context)


@login_required
@staff_required
def add_expense(request):
    """Add a new expense"""
    
    if request.method == 'POST':
        form = ExpenseForm(request.POST, request.FILES, user=request.user)
        if form.is_valid():
            expense = form.save(commit=False)
            expense.staff = request.user
            expense.save()
            messages.success(request, f'Expense "{expense.title}" has been created successfully.')
            return redirect('expenses:expenses_list')
    else:
        form = ExpenseForm(user=request.user)
    
    context = {
        'form': form,
        'title': 'Add New Expense',
    }
    
    return render(request, 'expenses/expense_form.html', context)


@login_required
@staff_required
def edit_expense(request, pk):
    """Edit an existing expense"""
    
    expense = get_object_or_404(Expense, pk=pk)
    
    # Check permissions
    if not request.user.is_superuser:
        if expense.status != 'pending':
            messages.error(request, 'You can only edit pending expenses.')
            return redirect('expenses:expense_detail', pk=pk)
        
        if expense.staff != request.user and not request.user.role in ['admin', 'team_leader']:
            messages.error(request, 'You do not have permission to edit this expense.')
            return redirect('expenses:expense_detail', pk=pk)
    
    if request.method == 'POST':
        form = ExpenseForm(request.POST, request.FILES, instance=expense, user=request.user)
        if form.is_valid():
            form.save()
            messages.success(request, f'Expense "{expense.title}" has been updated successfully.')
            return redirect('expenses:expense_detail', pk=pk)
    else:
        form = ExpenseForm(instance=expense, user=request.user)
    
    context = {
        'form': form,
        'expense': expense,
        'title': 'Edit Expense',
    }
    
    return render(request, 'expenses/expense_form.html', context)


@login_required
@staff_required
def expense_detail(request, pk):
    """View expense details"""
    
    expense = get_object_or_404(
        Expense.objects.select_related('branch', 'staff', 'loan', 'approved_by'),
        pk=pk
    )
    
    # Check branch permissions
    if not request.user.is_superuser:
        allowed_branches = []
        if hasattr(request.user, 'accessible_branches') and request.user.accessible_branches.exists():
            allowed_branches = list(request.user.accessible_branches.all())
        elif request.user.branch:
            allowed_branches = [request.user.branch]
        
        if expense.branch not in allowed_branches:
            messages.error(request, 'You do not have permission to view this expense.')
            return redirect('expenses:expenses_list')
    
    context = {
        'expense': expense,
    }
    
    return render(request, 'expenses/expense_detail.html', context)


@login_required
@staff_required
def delete_expense(request, pk):
    """Delete an expense"""
    
    expense = get_object_or_404(Expense, pk=pk)
    
    # Check permissions
    if not request.user.is_superuser and not request.user.role in ['admin', 'team_leader']:
        messages.error(request, 'You do not have permission to delete expenses.')
        return redirect('expenses:expense_detail', pk=pk)
    
    if request.method == 'POST':
        title = expense.title
        expense.delete()
        messages.success(request, f'Expense "{title}" has been deleted successfully.')
        return redirect('expenses:expenses_list')
    
    context = {
        'expense': expense,
    }
    
    return render(request, 'expenses/expense_confirm_delete.html', context)


@login_required
@staff_required
def approve_expense(request, pk):
    """Approve an expense"""
    
    expense = get_object_or_404(Expense, pk=pk)
    
    # Check permissions
    if not request.user.role in ['admin', 'team_leader']:
        messages.error(request, 'You do not have permission to approve expenses.')
        return redirect('expenses:expense_detail', pk=pk)
    
    if expense.status != 'pending':
        messages.warning(request, 'This expense has already been processed.')
        return redirect('expenses:expense_detail', pk=pk)
    
    if request.method == 'POST':
        expense.approve(request.user)
        messages.success(request, f'Expense "{expense.title}" has been approved.')
        return redirect('expenses:expense_detail', pk=pk)
    
    context = {
        'expense': expense,
    }
    
    return render(request, 'expenses/expense_approve.html', context)


@login_required
@staff_required
def reject_expense(request, pk):
    """Reject an expense"""
    
    expense = get_object_or_404(Expense, pk=pk)
    
    # Check permissions
    if not request.user.role in ['admin', 'team_leader']:
        messages.error(request, 'You do not have permission to reject expenses.')
        return redirect('expenses:expense_detail', pk=pk)
    
    if expense.status != 'pending':
        messages.warning(request, 'This expense has already been processed.')
        return redirect('expenses:expense_detail', pk=pk)
    
    if request.method == 'POST':
        reason = request.POST.get('reason', '')
        if not reason:
            messages.error(request, 'Please provide a reason for rejection.')
            return redirect('expenses:reject_expense', pk=pk)
        
        expense.reject(request.user, reason)
        messages.success(request, f'Expense "{expense.title}" has been rejected.')
        return redirect('expenses:expense_detail', pk=pk)
    
    context = {
        'expense': expense,
    }
    
    return render(request, 'expenses/expense_reject.html', context)


@login_required
@staff_required
def pending_approvals(request):
    """List pending expense approvals"""
    
    # Check permissions
    if not request.user.role in ['admin', 'team_leader']:
        messages.error(request, 'You do not have permission to access this page.')
        return redirect('expenses:expenses_list')
    
    # Get pending expenses with branch filtering
    expenses_qs = Expense.objects.filter(status='pending').select_related(
        'branch', 'staff', 'loan'
    )
    
    # Apply branch filtering
    expenses_qs = apply_expense_branch_filter(expenses_qs, request.user)
    
    # Pagination
    paginator = Paginator(expenses_qs, 25)
    page_number = request.GET.get('page', 1)
    expenses = paginator.get_page(page_number)
    
    context = {
        'expenses': expenses,
        'total_pending': expenses_qs.count(),
    }
    
    return render(request, 'expenses/pending_approvals.html', context)


@login_required
@staff_required
def export_expenses_excel(request):
    """Export expenses to Excel (CSV)"""
    
    # Get filtered expenses
    expenses_qs = Expense.objects.select_related('branch', 'staff', 'loan', 'approved_by').all()
    
    # Apply same filters as list view
    filter_form = ExpenseFilterForm(request.GET, user=request.user)
    if filter_form.is_valid():
        # Apply all filters (same logic as expenses_list)
        search = filter_form.cleaned_data.get('search')
        if search:
            expenses_qs = expenses_qs.filter(
                Q(title__icontains=search) |
                Q(description__icontains=search) |
                Q(paid_to__icontains=search)
            )
        
        category = filter_form.cleaned_data.get('category')
        if category:
            expenses_qs = expenses_qs.filter(category=category)
        
        status = filter_form.cleaned_data.get('status')
        if status:
            expenses_qs = expenses_qs.filter(status=status)
        
        # Add other filters as needed
    
    # Create CSV response
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = f'attachment; filename="expenses_{timezone.now().strftime("%Y%m%d_%H%M%S")}.csv"'
    
    writer = csv.writer(response)
    writer.writerow([
        'Date', 'Title', 'Category', 'Amount', 'Payment Method',
        'Paid To', 'Branch', 'Staff', 'Status', 'Approved By', 'Reference'
    ])
    
    for expense in expenses_qs:
        writer.writerow([
            expense.expense_date.strftime('%Y-%m-%d'),
            expense.title,
            expense.get_category_display(),
            expense.amount,
            expense.get_payment_method_display(),
            expense.paid_to,
            expense.branch.name,
            expense.staff.get_full_name() if expense.staff else '',
            expense.get_status_display(),
            expense.approved_by.get_full_name() if expense.approved_by else '',
            expense.reference_number or '',
        ])
    
    return response


@login_required
@staff_required
def expense_analytics(request):
    """Expense analytics and reports"""
    
    # Get base queryset with branch filtering
    expenses_qs = Expense.objects.filter(status='approved')
    
    # Apply branch filtering
    expenses_qs = apply_expense_branch_filter(expenses_qs, request.user)
    
    # Date ranges
    today = timezone.now().date()
    current_month_start = today.replace(day=1)
    current_year_start = today.replace(month=1, day=1)
    last_month_start = (current_month_start - timedelta(days=1)).replace(day=1)
    
    # Category breakdown with percentage
    category_breakdown = list(expenses_qs.values('category').annotate(
        total=Sum('amount'),
        count=Count('id'),
        average=Avg('amount')
    ).order_by('-total'))
    
    # Calculate percentages for progress bars
    if category_breakdown:
        max_total = category_breakdown[0]['total'] if category_breakdown[0]['total'] else Decimal('1')
        for item in category_breakdown:
            item['percentage'] = round((item['total'] / max_total) * 100, 2) if item['total'] else 0
    
    # Branch breakdown with percentage
    branch_breakdown = list(expenses_qs.values('branch__name').annotate(
        total=Sum('amount'),
        count=Count('id'),
        average=Avg('amount')
    ).order_by('-total'))
    
    # Calculate percentages for progress bars
    if branch_breakdown:
        max_total = branch_breakdown[0]['total'] if branch_breakdown[0]['total'] else Decimal('1')
        for item in branch_breakdown:
            item['percentage'] = round((item['total'] / max_total) * 100, 2) if item['total'] else 0
    
    # Monthly trend (last 6 months)
    monthly_trend = []
    max_total = Decimal('0')
    
    # First pass: collect data and find max
    for i in range(6):
        # Calculate month start by going back i months
        year = today.year
        month = today.month - i
        while month <= 0:
            month += 12
            year -= 1
        
        month_start = today.replace(year=year, month=month, day=1)
        
        # Calculate month end
        if month == 12:
            month_end = today.replace(year=year + 1, month=1, day=1) - timedelta(days=1)
        else:
            month_end = today.replace(year=year, month=month + 1, day=1) - timedelta(days=1)
        
        month_total = expenses_qs.filter(
            expense_date__gte=month_start,
            expense_date__lte=month_end
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0')
        
        if month_total > max_total:
            max_total = month_total
        
        monthly_trend.insert(0, {
            'month': month_start.strftime('%b %Y'),
            'total': float(month_total),
            'percentage': 0
        })
    
    # Second pass: calculate percentages
    if max_total > 0:
        for item in monthly_trend:
            item['percentage'] = round((Decimal(str(item['total'])) / max_total) * 100, 2)
    
    context = {
        'category_breakdown': category_breakdown,
        'branch_breakdown': branch_breakdown,
        'monthly_trend': monthly_trend,
    }
    
    return render(request, 'expenses/expense_analytics.html', context)
