import json

from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.db.models import Sum, Q
from django.utils import timezone

from accounts.models import User
from properties.models import Unit, Building
from .models import Invoice, Payment, Announcement, AnnouncementLog, Expense, ExpenseCategory
from .forms import ExpenseForm, ExpenseCategoryForm
from .mpesa import stk_push
from .utils import generate_monthly_invoices
from payments.services.notifications import broadcast_to_tenants


# ================= TENANT FINANCE =================
@login_required
def tenant_finance(request):
    user = request.user

    invoices = user.invoices.select_related("unit").order_by('-created_at')

    # ✅ Ensure statuses are always fresh
    for invoice in invoices:
        invoice.update_status()

    payments = user.payments.select_related("unit").order_by('-created_at')

    total_expected = invoices.aggregate(total=Sum('amount'))['total'] or 0
    total_paid = payments.aggregate(total=Sum('amount'))['total'] or 0
    total_balance = total_expected - total_paid

    return render(request, "payments/tenant_finance.html", {
        "invoices": invoices,
        "payments": payments,
        "total_expected": total_expected,
        "total_paid": total_paid,
        "total_balance": total_balance,
    })


# ================= LANDLORD FINANCE =================
@login_required
def landlord_finance(request):
    user = request.user

    buildings = Building.objects.filter(landlord=user)
    building_id = request.GET.get("building")

    units = Unit.objects.filter(building__landlord=user)

    if building_id:
        units = units.filter(building_id=building_id)

    data = []
    total_expected = 0
    total_paid = 0

    for unit in units:
        # ✅ USE MODEL METHODS (CRITICAL FIX)
        expected = unit.total_rent_expected()
        paid = unit.total_collected()
        balance = expected - paid

        total_expected += expected
        total_paid += paid

        tenant = unit.tenants.first().user if unit.tenants.exists() else None

        data.append({
            "unit": unit,
            "tenant": tenant,
            "expected": expected,
            "paid": paid,
            "balance": balance,
        })

    total_arrears = total_expected - total_paid

    return render(request, "payments/landlord_finance.html", {
        "data": data,
        "buildings": buildings,
        "total_expected": total_expected,
        "total_paid": total_paid,
        "total_arrears": total_arrears,
    })


# ================= MANUAL RENT RUN =================
def run_rent_cycle_view(request):
    result = generate_monthly_invoices()
    return HttpResponse(result)


# ================= MPESA STK =================
@login_required
def initiate_payment(request, invoice_id):
    invoice = get_object_or_404(Invoice, id=invoice_id, tenant=request.user)

    response = stk_push(
        phone=request.user.phone_number,
        amount=invoice.balance,
        account_reference=f"INV-{invoice.id}",
        transaction_desc="Rent Payment"
    )

    if response.get("ResponseCode") == "0":
        messages.success(request, "Payment request sent to your phone")
    else:
        messages.error(request, "Failed to initiate payment")

    return redirect("tenant_finance")


# ================= MPESA CALLBACK =================
@csrf_exempt
def mpesa_callback(request):
    try:
        data = json.loads(request.body)
        callback = data["Body"]["stkCallback"]

        if callback["ResultCode"] != 0:
            return HttpResponse("Failed", status=400)

        metadata = callback.get("CallbackMetadata", {}).get("Item", [])

        amount = None
        phone = None
        receipt = None

        for item in metadata:
            if item["Name"] == "Amount":
                amount = item.get("Value")
            elif item["Name"] == "PhoneNumber":
                phone = str(item.get("Value"))
            elif item["Name"] == "MpesaReceiptNumber":
                receipt = item.get("Value")

        if not phone or not amount or not receipt:
            return HttpResponse("Invalid data", status=400)

        # ✅ FIND ACTIVE INVOICE SAFELY
        invoice = Invoice.objects.filter(
            tenant__phone_number=phone,
            status__in=["pending", "partial", "overdue"]
        ).order_by("created_at").first()

        if not invoice:
            return HttpResponse("No invoice", status=404)

        # ✅ PREVENT DUPLICATE TRANSACTION
        if Payment.objects.filter(transaction_id=receipt).exists():
            return HttpResponse("Duplicate", status=200)

        Payment.objects.create(
            tenant=invoice.tenant,
            invoice=invoice,
            unit=invoice.unit,
            amount=amount,
            phone_number=phone,
            transaction_id=receipt,
            status="completed"
        )

        return HttpResponse("Success")

    except Exception as e:
        return HttpResponse(str(e), status=500)


# ================= SEND ANNOUNCEMENT =================
@login_required
def send_announcement(request):

    if request.user.role not in ["landlord", "caretaker"]:
        return redirect("login")

    buildings = request.user.buildings.all() if request.user.role == "landlord" else None
    tenants = User.objects.filter(role="tenant")

    if request.method == "POST":

        title = request.POST.get("title")
        message = request.POST.get("message")

        audience_type = request.POST.get("audience_type")
        building_id = request.POST.get("building")
        send_method = request.POST.get("send_method")

        is_automated = request.POST.get("is_automated") == "on"
        frequency = request.POST.get("frequency")
        scheduled_time = request.POST.get("scheduled_time")

        # FILTER TENANTS
        if audience_type == "filtered" and building_id:
            tenants = tenants.filter(tenantprofile__unit__building_id=building_id)

        building = Building.objects.filter(id=building_id).first() if building_id else None

        announcement = Announcement.objects.create(
            title=title,
            message=message,
            sender=request.user,
            audience_type=audience_type,
            building=building,
            send_method=send_method,
            is_automated=is_automated,
            frequency=frequency if is_automated else "once",
            scheduled_time=scheduled_time if is_automated else timezone.now(),
        )

        # SEND IMMEDIATELY
        if not is_automated:

            broadcast_to_tenants(tenants, title, message)

            # ✅ LOG DELIVERY (IMPORTANT FIX)
            logs = [
                AnnouncementLog(
                    announcement=announcement,
                    user=tenant,
                    delivered_via=send_method,
                    status="sent"
                )
                for tenant in tenants
            ]
            AnnouncementLog.objects.bulk_create(logs)

            announcement.is_sent = True
            announcement.save(update_fields=["is_sent"])

        return redirect("landlord_dashboard")

    return render(request, "payments/send_announcement.html", {
        "buildings": buildings
    })


# ================= NOTIFICATIONS =================
@login_required
def notifications_list(request):
    notifications = request.user.notifications.order_by('-created_at')

    return render(request, "payments/notifications.html", {
        "notifications": notifications
    })


@login_required
def mark_notification_read(request, notif_id):
    notif = get_object_or_404(request.user.notifications, id=notif_id)
    notif.is_read = True
    notif.save(update_fields=["is_read"])

    return redirect("notifications")


# ================= TENANT ANNOUNCEMENTS =================
@login_required
def tenant_announcements(request):

    if request.user.role != "tenant":
        return redirect("login")

    profile = getattr(request.user, "tenantprofile", None)

    announcements = Announcement.objects.filter(
        Q(audience_type="all") |
        Q(building=profile.unit.building if profile and profile.unit else None)
    ).order_by("-created_at")

    return render(request, "tenants/announcements.html", {
        "announcements": announcements
    })


# ================= EXPENSES =================
@login_required
def create_expense(request):

    if request.user.role not in ["landlord", "caretaker"]:
        return redirect("login")

    if request.method == "POST":
        form = ExpenseForm(request.POST)

        if form.is_valid():
            expense = form.save(commit=False)
            expense.recorded_by = request.user

            if request.user.role == "caretaker":
                expense.status = "pending"
            else:
                expense.status = "approved"
                expense.approved_by = request.user

            expense.save()

            messages.success(request, "Expense recorded successfully")
            return redirect("expense_list")

    else:
        form = ExpenseForm()

    return render(request, "payments/expense_form.html", {"form": form})


@login_required
def expense_list(request):

    if request.user.role == "landlord":
        expenses = Expense.objects.filter(building__landlord=request.user)

    elif request.user.role == "caretaker":
        expenses = Expense.objects.filter(building__caretaker=request.user)

    else:
        expenses = Expense.objects.none()

    return render(request, "payments/expense_list.html", {
        "expenses": expenses
    })


@login_required
def approve_expense(request, expense_id):

    if request.user.role != "landlord":
        return redirect("login")

    expense = get_object_or_404(Expense, id=expense_id)

    expense.status = "approved"
    expense.approved_by = request.user
    expense.save(update_fields=["status", "approved_by"])

    messages.success(request, "Expense approved")
    return redirect("expense_list")


@login_required
def reject_expense(request, expense_id):

    if request.user.role != "landlord":
        return redirect("login")

    expense = get_object_or_404(Expense, id=expense_id)

    expense.status = "rejected"
    expense.save(update_fields=["status"])

    messages.error(request, "Expense rejected")
    return redirect("expense_list")


@login_required
def create_category(request):

    if request.user.role != "landlord":
        return redirect("login")

    if request.method == "POST":
        form = ExpenseCategoryForm(request.POST)

        if form.is_valid():
            category = form.save(commit=False)
            category.created_by = request.user
            category.save()

            return redirect("expense_list")

    else:
        form = ExpenseCategoryForm()

    return render(request, "payments/category_form.html", {"form": form})