from django.contrib.auth.decorators import login_required
from django.db.models import (
    Q,
)
from django.http import HttpResponse, Http404

from django.shortcuts import render, redirect
from django.utils import timezone
from django.views.decorators.http import require_http_methods

from apps.common.decorators.htmx import only_htmx
from apps.common.utils.dicts import remove_falsey_entries
from apps.monthly_overview.utils.daily_spending_allowance import (
    calculate_daily_allowance_currency,
)
from apps.transactions.filters import TransactionsFilter
from apps.transactions.models import FilterPreset, Transaction
from apps.transactions.utils.calculations import (
    calculate_currency_totals,
    calculate_percentage_distribution,
    calculate_account_totals,
)
from apps.transactions.utils.default_ordering import default_order


@login_required
def index(request):
    now = timezone.localdate(timezone.now())

    return redirect(to="monthly_overview", month=now.month, year=now.year)


@login_required
@require_http_methods(["GET"])
def monthly_overview(request, month: int, year: int):
    order = request.session.get("monthly_transactions_order", "default")
    summary_tab = request.session.get("monthly_summary_tab", "summary")

    if month < 1 or month > 12:
        raise Http404("Month is out of range")

    next_month = 1 if month == 12 else month + 1
    next_year = year + 1 if next_month == 1 and month == 12 else year

    previous_month = 12 if month == 1 else month - 1
    previous_year = year - 1 if previous_month == 12 and month == 1 else year

    f = TransactionsFilter(request.GET)

    return render(
        request,
        "monthly_overview/pages/overview.html",
        context={
            "month": month,
            "year": year,
            "next_month": next_month,
            "next_year": next_year,
            "previous_month": previous_month,
            "previous_year": previous_year,
            "filter": f,
            "filter_is_active": f.has_active_filters,
            "filter_presets": FilterPreset.objects.filter(owner=request.user),
            "order": order,
            "summary_tab": summary_tab,
        },
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def transactions_list(request, month: int, year: int):
    order = request.session.get("monthly_transactions_order", "default")

    if "order" in request.GET:
        order = request.GET["order"]
        if order != request.session.get("monthly_transactions_order", "default"):
            request.session["monthly_transactions_order"] = order

    today = timezone.localdate(timezone.now())

    f = TransactionsFilter(request.GET)
    transactions_filtered = f.qs.filter(
        reference_date__year=year,
        reference_date__month=month,
    ).prefetch_related(
        "account",
        "account__group",
        "category",
        "tags",
        "account__exchange_currency",
        "account__currency",
        "installment_plan",
        "entities",
        "dca_expense_entries",
        "dca_income_entries",
    )

    # Late transactions: date < today and is_paid = False (only shown for default ordering)
    late_transactions = None
    if order == "default":
        late_transactions = transactions_filtered.filter(
            date__lt=today,
            is_paid=False,
        ).order_by("date", "id")
        # Exclude late transactions from the main list
        transactions_filtered = transactions_filtered.exclude(
            date__lt=today,
            is_paid=False,
        )

    transactions_filtered = default_order(transactions_filtered, order=order)

    return render(
        request,
        "monthly_overview/fragments/list.html",
        context={
            "transactions": transactions_filtered,
            "late_transactions": late_transactions,
        },
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def monthly_summary(request, month: int, year: int):
    # Base queryset with all required filters
    base_queryset = Transaction.objects.filter(
        reference_date__year=year,
        reference_date__month=month,
    )

    # Apply filters and check if any are active
    f = TransactionsFilter(request.GET, queryset=base_queryset)

    # Check if any filter has a non-default value
    # Default values are: type=['IN', 'EX'], is_paid=['1', '0'], everything else empty
    has_active_filter = False
    if f.form.is_valid():
        for name, value in f.form.cleaned_data.items():
            # Skip fields with default/empty values
            if not value:
                continue
            # Skip type if it has both default values
            if name == "type" and set(value) == {"IN", "EX"}:
                continue
            # Skip is_paid if it has both default values (values are strings)
            if name == "is_paid" and set(value) == {"1", "0"}:
                continue
            # Skip mute_status if it has both default values
            if name == "mute_status" and set(value) == {"active", "muted"}:
                continue
            # If we get here, there's an active filter
            has_active_filter = True
            break

    if has_active_filter:
        queryset = f.qs
    else:
        queryset = (
            base_queryset.exclude(
                Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True)
            )
            .exclude(account__in=request.user.untracked_accounts.all())
            .exclude(account__is_asset=True)
        )

    data = calculate_currency_totals(queryset, ignore_empty=True)

    percentages = calculate_percentage_distribution(data)

    context = {
        "income_current": remove_falsey_entries(data, "income_current"),
        "income_projected": remove_falsey_entries(data, "income_projected"),
        "expense_current": remove_falsey_entries(data, "expense_current"),
        "expense_projected": remove_falsey_entries(data, "expense_projected"),
        "total_current": remove_falsey_entries(data, "total_current"),
        "total_final": remove_falsey_entries(data, "total_final"),
        "total_projected": remove_falsey_entries(data, "total_projected"),
        "daily_spending_allowance": calculate_daily_allowance_currency(
            currency_totals=data, month=month, year=year
        ),
        "percentages": percentages,
        "has_active_filter": has_active_filter,
    }

    return render(
        request,
        "monthly_overview/fragments/monthly_summary.html",
        context=context,
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def monthly_account_summary(request, month: int, year: int):
    # Base queryset with all required filters
    base_queryset = Transaction.objects.filter(
        reference_date__year=year,
        reference_date__month=month,
    )

    # Apply filters and check if any are active
    f = TransactionsFilter(request.GET, queryset=base_queryset)

    # Check if any filter has a non-default value
    has_active_filter = False
    if f.form.is_valid():
        for name, value in f.form.cleaned_data.items():
            if not value:
                continue
            if name == "type" and set(value) == {"IN", "EX"}:
                continue
            if name == "is_paid" and set(value) == {"1", "0"}:
                continue
            if name == "mute_status" and set(value) == {"active", "muted"}:
                continue
            has_active_filter = True
            break

    if has_active_filter:
        queryset = f.qs
    else:
        queryset = (
            base_queryset.exclude(
                Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True)
            )
            .exclude(account__in=request.user.untracked_accounts.all())
        )

    account_data = calculate_account_totals(transactions_queryset=queryset.all())
    account_percentages = calculate_percentage_distribution(account_data)

    context = {
        "account_data": account_data,
        "account_percentages": account_percentages,
    }

    return render(
        request,
        "monthly_overview/fragments/monthly_account_summary.html",
        context=context,
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def monthly_currency_summary(request, month: int, year: int):
    # Base queryset with all required filters
    base_queryset = Transaction.objects.filter(
        reference_date__year=year,
        reference_date__month=month,
    )

    # Apply filters and check if any are active
    f = TransactionsFilter(request.GET, queryset=base_queryset)

    # Check if any filter has a non-default value
    has_active_filter = False
    if f.form.is_valid():
        for name, value in f.form.cleaned_data.items():
            if not value:
                continue
            if name == "type" and set(value) == {"IN", "EX"}:
                continue
            if name == "is_paid" and set(value) == {"1", "0"}:
                continue
            if name == "mute_status" and set(value) == {"active", "muted"}:
                continue
            has_active_filter = True
            break

    if has_active_filter:
        queryset = f.qs
    else:
        queryset = (
            base_queryset.exclude(
                Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True)
            )
            .exclude(account__in=request.user.untracked_accounts.all())
        )

    currency_data = calculate_currency_totals(queryset.all(), ignore_empty=True)
    currency_percentages = calculate_percentage_distribution(currency_data)

    context = {
        "currency_data": currency_data,
        "currency_percentages": currency_percentages,
    }

    return render(
        request, "monthly_overview/fragments/monthly_currency_summary.html", context
    )


@login_required
@require_http_methods(["GET"])
def monthly_summary_select(request, selected):
    request.session["monthly_summary_tab"] = selected

    return HttpResponse(
        status=204,
    )
