from django.db.models import Sum, Count, Q
from django.utils import timezone
from datetime import timedelta

from payments.models import Payment, Invoice, Expense
from properties.models import Unit, Building
from tenants.models import TenantProfile
from maintenance.models import RepairRequest, Complaint
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
from django.conf import settings
from .exports import export_pdf

from django.core.cache import cache



# =====================================================
# 🔹 FILTER ENGINE (BASE)
# =====================================================

def apply_filters(queryset, start_date=None, end_date=None, building=None, unit=None, tenant=None):
    if start_date:
        queryset = queryset.filter(created_at__date__gte=start_date)

    if end_date:
        queryset = queryset.filter(created_at__date__lte=end_date)

    if building:
        queryset = queryset.filter(unit__building=building)

    if unit:
        queryset = queryset.filter(unit=unit)

    if tenant:
        queryset = queryset.filter(tenant=tenant)

    return queryset


# =====================================================
# 🔹 FINANCIAL SUMMARY
# =====================================================

def get_financial_summary(filters={}):
    payments = apply_filters(
        Payment.objects.filter(status="completed"),
        **filters
    )

    invoices = apply_filters(
        Invoice.objects.all(),
        **filters
    )

    expenses = apply_filters(
        Expense.objects.filter(status="approved"),
        **filters
    )

    total_income = payments.aggregate(total=Sum("amount"))["total"] or 0
    total_expected = invoices.aggregate(total=Sum("amount"))["total"] or 0
    total_expenses = expenses.aggregate(total=Sum("amount"))["total"] or 0

    total_balance = total_expected - total_income
    net_profit = total_income - total_expenses

    return {
        "total_income": total_income,
        "total_expected": total_expected,
        "total_expenses": total_expenses,
        "total_balance": total_balance,
        "net_profit": net_profit,
    }


# =====================================================
# 🔹 ARREARS AGING
# =====================================================

def get_arrears_report(filters={}):
    today = timezone.now().date()

    invoices = apply_filters(
        Invoice.objects.filter(status__in=["pending", "partial", "overdue"]),
        **filters
    )

    buckets = {
        "0_7": 0,
        "8_30": 0,
        "31_60": 0,
        "60_plus": 0,
    }

    for inv in invoices:
        days_overdue = (today - inv.due_date).days

        if days_overdue <= 7:
            buckets["0_7"] += inv.balance
        elif days_overdue <= 30:
            buckets["8_30"] += inv.balance
        elif days_overdue <= 60:
            buckets["31_60"] += inv.balance
        else:
            buckets["60_plus"] += inv.balance

    return buckets


# =====================================================
# 🔹 EXPENSE BREAKDOWN
# =====================================================

def get_expense_breakdown(filters={}):
    expenses = apply_filters(
        Expense.objects.filter(status="approved"),
        **filters
    )

    return expenses.values("category__name").annotate(
        total=Sum("amount")
    ).order_by("-total")


# =====================================================
# 🔹 PROFIT & LOSS
# =====================================================

def get_profit_loss(filters={}):
    data = get_financial_summary(filters)

    return {
        "income": data["total_income"],
        "expenses": data["total_expenses"],
        "net_profit": data["net_profit"],
    }


# =====================================================
# 🔹 OCCUPANCY METRICS
# =====================================================

def get_occupancy_metrics(filters={}):
    units = Unit.objects.all()

    total_units = units.count()
    occupied_units = TenantProfile.objects.filter(unit__isnull=False).count()

    occupancy_rate = (occupied_units / total_units * 100) if total_units else 0

    return {
        "total_units": total_units,
        "occupied_units": occupied_units,
        "vacant_units": total_units - occupied_units,
        "occupancy_rate": round(occupancy_rate, 2),
    }


# =====================================================
# 🔹 UNIT PERFORMANCE
# =====================================================

def get_unit_performance(unit, filters={}):
    payments = apply_filters(
        unit.payments.filter(status="completed"),
        **filters
    )

    expenses = apply_filters(
        Expense.objects.filter(building=unit.building, status="approved"),
        **filters
    )

    total_income = payments.aggregate(total=Sum("amount"))["total"] or 0
    total_expenses = expenses.aggregate(total=Sum("amount"))["total"] or 0

    profit = total_income - total_expenses

    # Efficiency Score
    score = 100

    if total_expenses > total_income * 0.5:
        score -= 30

    if total_income == 0:
        score -= 50

    if score >= 80:
        rating = "High Performing"
    elif score >= 50:
        rating = "Moderate"
    else:
        rating = "Low Performing"

    return {
        "unit": unit,
        "income": total_income,
        "expenses": total_expenses,
        "profit": profit,
        "score": score,
        "rating": rating,
    }


# =====================================================
# 🔹 TENANT BEHAVIOR
# =====================================================

def get_tenant_behavior(tenant):
    invoices = tenant.invoices.all()
    payments = tenant.payments.filter(status="completed")

    total_invoices = invoices.count()
    total_paid = payments.aggregate(total=Sum("amount"))["total"] or 0

    late_invoices = invoices.filter(
        status__in=["overdue", "partial"]
    ).count()

    score = 100

    if total_invoices:
        late_ratio = late_invoices / total_invoices
        score -= late_ratio * 50

    if total_paid == 0:
        score = 0

    if score >= 80:
        risk = "Low"
    elif score >= 50:
        risk = "Medium"
    else:
        risk = "High"

    return {
        "tenant": tenant,
        "score": round(score, 2),
        "risk": risk,
        "late_invoices": late_invoices,
    }


# =====================================================
# 🔹 MAINTENANCE SUMMARY
# =====================================================

def get_maintenance_summary(filters={}):
    repairs = RepairRequest.objects.all()
    complaints = Complaint.objects.all()

    total_repairs = repairs.count()
    pending = repairs.filter(status="pending").count()
    in_progress = repairs.filter(status="in_progress").count()
    completed = repairs.filter(status="completed").count()

    return {
        "total_repairs": total_repairs,
        "pending": pending,
        "in_progress": in_progress,
        "completed": completed,
        "complaints": complaints.count(),
    }


# =====================================================
# 🔹 COST LOSS ANALYSIS
# =====================================================

def get_cost_loss_analysis(filters={}):
    repairs = RepairRequest.objects.filter(status="completed")

    total_loss = 0

    for repair in repairs:
        if repair.started_at and repair.completed_at and repair.unit:
            days = (repair.completed_at - repair.started_at).days
            daily_rent = repair.unit.rent_amount / 30
            total_loss += days * daily_rent

    return {
        "estimated_loss": total_loss
    }


# =====================================================
# 🔹 TAX CALCULATIONS (KENYA COMPLIANT)
# =====================================================

# ---------- MRI ----------
def calculate_mri_tax(gross_rent):
    return gross_rent * 0.075


# ---------- ANNUAL ----------
def calculate_annual_tax(gross, expenses, is_company=False):
    net = gross - expenses

    if net <= 0:
        return 0

    if is_company:
        return net * 0.30

    # Simplified individual tax (can refine later)
    if net <= 288000:
        return net * 0.10
    elif net <= 600000:
        return net * 0.20
    else:
        return net * 0.30


# ---------- NON RESIDENT ----------
def calculate_non_resident_tax(gross):
    return gross * 0.30


def get_tax_summary(filters={}, tax_mode="mri", is_company=False):
    payments = apply_filters(
        Payment.objects.filter(status="completed"),
        **filters
    )

    expenses = apply_filters(
        Expense.objects.filter(status="approved"),
        **filters
    )

    gross = payments.aggregate(total=Sum("amount"))["total"] or 0
    total_expenses = expenses.aggregate(total=Sum("amount"))["total"] or 0

    if tax_mode == "mri":
        tax = calculate_mri_tax(gross)
        taxable_income = gross

    elif tax_mode == "annual":
        taxable_income = gross - total_expenses
        tax = calculate_annual_tax(gross, total_expenses, is_company)

    elif tax_mode == "non_resident":
        taxable_income = gross
        tax = calculate_non_resident_tax(gross)

    else:
        tax = 0
        taxable_income = 0

    return {
        "gross_income": gross,
        "expenses": total_expenses,
        "taxable_income": taxable_income,
        "tax_payable": tax,
        "tax_mode": tax_mode,
    }




def generate_monthly_landlord_reports():
    from accounts.models import User
    from datetime import date

    landlords = User.objects.filter(role="landlord")

    for landlord in landlords:
        filters = {}  # You can scope per landlord later

        summary = get_financial_summary(filters)

        pdf_response = export_pdf(summary, title="Monthly Report")

        email = EmailMessage(
            subject="Monthly Property Report",
            body="Attached is your monthly report.",
            from_email=settings.DEFAULT_FROM_EMAIL,
            to=[landlord.email],
        )

        email.attach("report.pdf", pdf_response.content, "application/pdf")
        email.send()



def get_cached_financial_summary(filters, cache_key="financial_summary"):
    """
    Safe Redis cache wrapper:
    - Uses Redis if available
    - Falls back to DB silently if Redis is down
    """

    key = f"{cache_key}:{str(filters)}"

    try:
        # 1. Try cache
        data = cache.get(key)
        if data:
            return data

        # 2. Compute fresh data
        data = get_financial_summary(filters)

        # 3. Store safely (ignore Redis failures)
        try:
            cache.set(key, data, timeout=60 * 10)  # 10 min cache
        except Exception:
            pass

        return data

    except Exception:
        # 4. HARD fallback (no caching at all)
        return get_financial_summary(filters)