import datetime
from copy import deepcopy

from apps.common.decorators.demo import disabled_on_demo
from apps.common.decorators.htmx import only_htmx
from apps.rules.signals import transaction_created, transaction_updated
from apps.transactions.filters import TransactionsFilter
from apps.transactions.forms import (
    BulkEditTransactionForm,
    TransactionAttachmentForm,
    TransactionForm,
    TransferForm,
)
from apps.transactions.models import FilterPreset, Transaction, TransactionAttachment
from apps.transactions.utils.calculations import (
    calculate_account_totals,
    calculate_currency_totals,
    calculate_percentage_distribution,
)
from apps.transactions.utils.default_ordering import default_order
from dateutil.relativedelta import relativedelta
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Case, IntegerField, Q, Value, When
from django.http import FileResponse, Http404, HttpResponse, JsonResponse, QueryDict
from django.shortcuts import get_object_or_404, render
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from django.views.decorators.http import require_http_methods


def _get_accessible_transaction_or_404(transaction_id):
    return get_object_or_404(Transaction.objects, id=transaction_id)


def _get_accessible_attachment_or_404(attachment_id):
    attachment = get_object_or_404(
        TransactionAttachment.objects.select_related("transaction"),
        id=attachment_id,
    )
    if not Transaction.objects.filter(id=attachment.transaction_id).exists():
        raise Http404()
    return attachment


@only_htmx
@login_required
@disabled_on_demo
@require_http_methods(["GET", "POST"])
def transaction_attachments(request, transaction_id):
    transaction = _get_accessible_transaction_or_404(transaction_id)

    if request.method == "POST":
        form = TransactionAttachmentForm(request.POST, request.FILES)
        if form.is_valid():
            form.save(transaction=transaction, uploaded_by=request.user)
            messages.success(request, _("Attachment uploaded successfully"))
            form = TransactionAttachmentForm()
    else:
        form = TransactionAttachmentForm()

    response = render(
        request,
        "transactions/fragments/attachments_manage.html",
        {"form": form, "transaction": transaction},
    )

    response["HX-Trigger"] = "toasts, updated"

    return response


@only_htmx
@login_required
@disabled_on_demo
@require_http_methods(["GET"])
def transaction_attachments_list(request, transaction_id):
    transaction = _get_accessible_transaction_or_404(transaction_id)
    return render(
        request,
        "transactions/fragments/attachments.html",
        {"transaction": transaction},
    )


@login_required
@disabled_on_demo
@require_http_methods(["GET"])
def transaction_attachment_download(request, attachment_id):
    attachment = _get_accessible_attachment_or_404(attachment_id)
    return FileResponse(
        attachment.file.open("rb"),
        as_attachment=False,
        filename=attachment.original_name,
        content_type=attachment.content_type or "application/octet-stream",
    )


@only_htmx
@login_required
@disabled_on_demo
@require_http_methods(["DELETE"])
def transaction_attachment_delete(request, attachment_id):
    attachment = _get_accessible_attachment_or_404(attachment_id)
    transaction = attachment.transaction
    attachment.file.delete(save=False)
    attachment.delete()
    messages.success(request, _("Attachment deleted successfully"))
    response = render(
        request,
        "transactions/fragments/attachments.html",
        {"transaction": transaction},
    )
    response["HX-Trigger"] = "toasts, updated"
    return response


@only_htmx
@login_required
@require_http_methods(["GET", "POST"])
def transaction_add(request):
    month = int(request.GET.get("month", timezone.localdate(timezone.now()).month))
    year = int(request.GET.get("year", timezone.localdate(timezone.now()).year))
    transaction_type = Transaction.Type(request.GET.get("type", "IN"))

    now = timezone.localdate(timezone.now())
    expected_date = datetime.datetime(
        day=now.day if month == now.month and year == now.year else 1,
        month=month,
        year=year,
    ).date()

    update = False

    if request.method == "POST":
        form = TransactionForm(request.POST)
        if form.is_valid():
            saved_instance = form.save()
            messages.success(request, _("Transaction added successfully"))

            if "submit" in request.POST:
                return HttpResponse(
                    status=204,
                    headers={"HX-Trigger": "updated, hide_offcanvas"},
                )
            elif "submit_and_another" in request.POST:
                form = TransactionForm(
                    initial={
                        "date": expected_date,
                        "type": transaction_type,
                    },
                )
                update = True
            elif "submit_and_similar" in request.POST:
                initial_data = {}

                # Define fields to copy from the SAVED instance
                direct_fields_to_copy = [
                    "account",  # ForeignKey -> will copy the ID
                    "type",  # ChoiceField -> will copy the value
                    "is_paid",  # BooleanField -> will copy True/False
                    "date",  # DateField -> will copy the date object
                    "reference_date",  # DateField -> will copy the date object
                    "amount",  # DecimalField -> will copy the decimal
                    "description",  # CharField -> will copy the string
                    "notes",  # TextField -> will copy the string
                    "category",  # ForeignKey -> will copy the ID
                ]
                m2m_fields_to_copy = [
                    "tags",  # ManyToManyField -> will copy list of IDs
                    "entities",  # ManyToManyField -> will copy list of IDs
                ]

                # Copy direct fields from the saved instance
                for field_name in direct_fields_to_copy:
                    value = getattr(saved_instance, field_name, None)
                    if value is not None:
                        # Handle ForeignKey: use the pk
                        if hasattr(value, "pk"):
                            initial_data[field_name] = value.pk
                        # Handle Date/DateTime/Decimal/Boolean/etc.: use the Python object directly
                        else:
                            initial_data[field_name] = (
                                value  # This correctly handles date objects!
                            )

                # Copy M2M fields: provide a list of related object pks
                for field_name in m2m_fields_to_copy:
                    m2m_manager = getattr(saved_instance, field_name)
                    initial_data[field_name] = list(
                        m2m_manager.values_list("name", flat=True)
                    )

                # Create a new form instance pre-filled with the correctly typed initial data
                form = TransactionForm(initial=initial_data)
                update = True  # Signal HTMX to update the form area

    else:
        form = TransactionForm(
            initial={
                "date": expected_date,
                "type": transaction_type,
            },
        )

    response = render(
        request,
        "transactions/fragments/add.html",
        {"form": form},
    )
    if update:
        response["HX-Trigger"] = "updated"

    return response


@login_required
@require_http_methods(["GET", "POST"])
def transaction_simple_add(request):
    month = int(request.GET.get("month", timezone.localdate(timezone.now()).month))
    year = int(request.GET.get("year", timezone.localdate(timezone.now()).year))
    transaction_type = Transaction.Type(request.GET.get("type", "IN"))

    now = timezone.localdate(timezone.now())
    expected_date = datetime.datetime(
        day=now.day if month == now.month and year == now.year else 1,
        month=month,
        year=year,
    ).date()

    # Build initial data from query parameters
    initial_data = {
        "date": expected_date,
        "type": transaction_type,
    }

    # Handle date param (ISO format: YYYY-MM-DD) - overrides expected_date
    date_param = request.GET.get("date")
    if date_param:
        try:
            initial_data["date"] = datetime.datetime.strptime(
                date_param, "%Y-%m-%d"
            ).date()
        except ValueError:
            pass

    # Handle reference_date param (ISO format: YYYY-MM-DD)
    reference_date_param = request.GET.get("reference_date")
    if reference_date_param:
        try:
            initial_data["reference_date"] = datetime.datetime.strptime(
                reference_date_param, "%Y-%m-%d"
            ).date()
        except ValueError:
            pass

    # Handle account param (by ID or name)
    account_param = request.GET.get("account")
    if account_param:
        try:
            initial_data["account"] = int(account_param)
        except (ValueError, TypeError):
            # Try to find by name
            from apps.accounts.models import Account

            account = Account.objects.filter(
                name__iexact=account_param, is_archived=False
            ).first()
            if account:
                initial_data["account"] = account.pk

    # Handle is_paid param (boolean)
    is_paid = request.GET.get("is_paid")
    if is_paid is not None:
        initial_data["is_paid"] = is_paid.lower() in ("true", "1", "yes")

    # Handle amount param (decimal)
    amount = request.GET.get("amount")
    if amount:
        try:
            initial_data["amount"] = amount
        except (ValueError, TypeError):
            pass

    # Handle description param (string)
    description = request.GET.get("description")
    if description:
        initial_data["description"] = description

    # Handle notes param (string)
    notes = request.GET.get("notes")
    if notes:
        initial_data["notes"] = notes

    # Handle category param (by ID or name)
    category_param = request.GET.get("category")
    if category_param:
        try:
            initial_data["category"] = int(category_param)
        except (ValueError, TypeError):
            # Try to find by name
            from apps.transactions.models import TransactionCategory

            category = TransactionCategory.objects.filter(
                name__iexact=category_param, active=True
            ).first()
            if category:
                initial_data["category"] = category.pk

    # Handle tags param (comma-separated names)
    tags = request.GET.get("tags")
    if tags:
        initial_data["tags"] = [t.strip() for t in tags.split(",") if t.strip()]

    # Handle entities param (comma-separated names)
    entities = request.GET.get("entities")
    if entities:
        initial_data["entities"] = [e.strip() for e in entities.split(",") if e.strip()]

    if request.method == "POST":
        form = TransactionForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, _("Transaction added successfully"))
            # Only reset form after successful save
            form = TransactionForm(initial=initial_data)

    else:
        form = TransactionForm(initial=initial_data)

    return render(
        request,
        "transactions/pages/add.html",
        {"form": form},
    )


@only_htmx
@login_required
@require_http_methods(["GET", "POST"])
def transaction_edit(request, transaction_id, **kwargs):
    transaction = get_object_or_404(Transaction, id=transaction_id)

    if request.method == "POST":
        form = TransactionForm(request.POST, instance=transaction)
        if form.is_valid():
            form.save()
            messages.success(request, _("Transaction updated successfully"))

            return HttpResponse(
                status=204,
                headers={"HX-Trigger": "updated, hide_offcanvas"},
            )
    else:
        form = TransactionForm(instance=transaction)

    return render(
        request,
        "transactions/fragments/edit.html",
        {"form": form, "transaction": transaction},
    )


@only_htmx
@login_required
@require_http_methods(["GET", "POST"])
def transactions_bulk_edit(request):
    # Get selected transaction IDs from the URL parameter
    transaction_ids = request.GET.getlist("transactions") or request.POST.getlist(
        "transactions"
    )
    # Load the selected transactions
    transactions = Transaction.objects.filter(id__in=transaction_ids)
    count = transactions.count()

    if request.method == "POST":
        form = BulkEditTransactionForm(request.POST)
        if form.is_valid():
            # Apply changes from the form to all selected transactions
            for transaction in transactions:
                old_data = deepcopy(transaction)
                for field_name, value in form.cleaned_data.items():
                    if value or isinstance(
                        value, bool
                    ):  # Only update fields that have been filled in the form
                        if field_name == "tags":
                            transaction.tags.set(value)
                        elif field_name == "entities":
                            transaction.entities.set(value)
                        else:
                            setattr(transaction, field_name, value)

                transaction.save()
                transaction_updated.send(sender=transaction, old_data=old_data)

            messages.success(
                request,
                ngettext_lazy(
                    "%(count)s transaction updated successfully",
                    "%(count)s transactions updated successfully",
                    count,
                )
                % {"count": count},
            )
            return HttpResponse(
                status=204,
                headers={"HX-Trigger": "updated, hide_offcanvas"},
            )
    else:
        form = BulkEditTransactionForm(initial={"is_paid": None, "type": None})

    context = {
        "form": form,
        "transactions": transactions,
    }
    return render(request, "transactions/fragments/bulk_edit.html", context)


@only_htmx
@login_required
@require_http_methods(["GET", "POST"])
def transaction_clone(request, transaction_id, **kwargs):
    transaction = get_object_or_404(Transaction, id=transaction_id)
    new_transaction = deepcopy(transaction)
    new_transaction.pk = None
    new_transaction.installment_plan = None
    new_transaction.installment_id = None
    new_transaction.recurring_transaction = None
    new_transaction.internal_id = None
    new_transaction.save()

    new_transaction.tags.add(*transaction.tags.all())
    new_transaction.entities.add(*transaction.entities.all())

    messages.success(request, _("Transaction duplicated successfully"))

    transaction_created.send(sender=transaction)

    # THIS HAS BEEN DISABLE DUE TO HTMX INCOMPATIBILITY
    # SEE https://github.com/bigskysoftware/htmx/issues/3115 and https://github.com/bigskysoftware/htmx/issues/2706

    # if request.GET.get("edit") == "true":
    #     return HttpResponse(
    #         status=200,
    #         headers={
    #             "HX-Trigger": "updated",
    #             "HX-Push-Url": "false",
    #             "HX-Location": json.dumps(
    #                 {
    #                     "path": reverse(
    #                         "transaction_edit",
    #                         kwargs={"transaction_id": new_transaction.id},
    #                     ),
    #                     "target": "#generic-offcanvas",
    #                     "swap": "innerHTML",
    #                 }
    #             ),
    #         },
    #     )
    # else:
    #     transaction_created.send(sender=transaction)

    return HttpResponse(
        status=204,
        headers={"HX-Trigger": "updated"},
    )


@only_htmx
@login_required
@require_http_methods(["DELETE"])
def transaction_delete(request, transaction_id, **kwargs):
    transaction = get_object_or_404(Transaction.all_objects, id=transaction_id)

    transaction.delete()

    messages.success(request, _("Transaction deleted successfully"))

    return HttpResponse(
        status=204,
        headers={"HX-Trigger": "updated"},
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_undelete(request, transaction_id, **kwargs):
    transaction = get_object_or_404(Transaction.deleted_objects, id=transaction_id)

    transaction.deleted = False
    transaction.deleted_at = None
    transaction.save()

    messages.success(request, _("Transaction restored successfully"))

    return HttpResponse(
        status=204,
        headers={"HX-Trigger": "updated"},
    )


@only_htmx
@login_required
@require_http_methods(["GET", "POST"])
def transactions_transfer(request):
    month = int(request.GET.get("month", timezone.localdate(timezone.now()).month))
    year = int(request.GET.get("year", timezone.localdate(timezone.now()).year))

    now = timezone.localdate(timezone.now())
    expected_date = datetime.datetime(
        day=now.day if month == now.month and year == now.year else 1,
        month=month,
        year=year,
    ).date()

    if request.method == "POST":
        form = TransferForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, _("Transfer added successfully"))
            return HttpResponse(
                status=204,
                headers={"HX-Trigger": "updated, hide_offcanvas"},
            )
    else:
        form = TransferForm(
            initial={
                "reference_date": expected_date,
                "date": expected_date,
            },
        )

    return render(request, "transactions/fragments/transfer.html", {"form": form})


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_pay(request, transaction_id):
    transaction = get_object_or_404(Transaction, pk=transaction_id)
    old_data = deepcopy(transaction)

    new_is_paid = False if transaction.is_paid else True
    transaction.is_paid = new_is_paid
    transaction.save()

    transaction_updated.send(sender=transaction, old_data=old_data)

    response = render(
        request,
        "transactions/fragments/item.html",
        context={"transaction": transaction, **request.GET},
    )
    response.headers["HX-Trigger"] = (
        f"{'paid' if new_is_paid else 'unpaid'}, selective_update"
    )
    return response


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_mute(request, transaction_id):
    transaction = get_object_or_404(Transaction, pk=transaction_id)
    old_data = deepcopy(transaction)

    new_mute = False if transaction.mute else True
    transaction.mute = new_mute
    transaction.save()
    transaction_updated.send(sender=transaction, old_data=old_data)

    response = render(
        request,
        "transactions/fragments/item.html",
        context={"transaction": transaction, **request.GET},
    )
    response.headers["HX-Trigger"] = "selective_update"
    return response


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_change_month(request, transaction_id, change_type):
    transaction: Transaction = get_object_or_404(Transaction, pk=transaction_id)
    old_data = deepcopy(transaction)

    if change_type == "next":
        transaction.reference_date = transaction.reference_date + relativedelta(
            months=1
        )
        transaction.save()
        transaction_updated.send(sender=transaction, old_data=old_data)
    elif change_type == "previous":
        transaction.reference_date = transaction.reference_date - relativedelta(
            months=1
        )
        transaction.save()
        transaction_updated.send(sender=transaction, old_data=old_data)

    return HttpResponse(
        status=204,
        headers={"HX-Trigger": "updated"},
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_move_to_today(request, transaction_id):
    transaction: Transaction = get_object_or_404(Transaction, pk=transaction_id)

    old_data = deepcopy(transaction)

    transaction.date = timezone.localdate(timezone.now())
    transaction.save()
    transaction_updated.send(sender=transaction, old_data=old_data)

    return HttpResponse(
        status=204,
        headers={"HX-Trigger": "updated"},
    )


@login_required
@require_http_methods(["GET"])
def transaction_all_index(request):
    order = request.session.get("all_transactions_order", "default")
    summary_tab = request.session.get("transaction_all_summary_tab", "currency")

    f = TransactionsFilter(request.GET)
    return render(
        request,
        "transactions/pages/transactions.html",
        {
            "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(["POST"])
def filter_preset_create(request):
    name = request.POST.get("name", "").strip()
    if not name or len(name) > 100:
        return HttpResponse(status=400)

    parameters = {
        key: request.POST.getlist(key)
        for key in TransactionsFilter.base_filters
        if key in request.POST
    }
    FilterPreset.objects.create(
        owner=request.user,
        name=name,
        parameters=parameters,
    )
    return render(
        request,
        "transactions/fragments/filter_presets.html",
        {"filter_presets": FilterPreset.objects.filter(owner=request.user)},
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def filter_preset_apply(request, preset_id):
    preset = get_object_or_404(FilterPreset, pk=preset_id, owner=request.user)
    data = QueryDict(mutable=True)
    for key, values in preset.parameters.items():
        if key in TransactionsFilter.base_filters:
            data.setlist(key, values)

    transaction_filter = TransactionsFilter(data)
    response = render(
        request,
        "transactions/fragments/filter_form.html",
        {
            "filter": transaction_filter,
            "filter_is_active": transaction_filter.has_active_filters,
            "swap_filter_indicator": True,
        },
    )
    response.headers["HX-Trigger-After-Settle"] = "updated"
    return response


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_filter_clear(request):
    transaction_filter = TransactionsFilter(QueryDict())
    response = render(
        request,
        "transactions/fragments/filter_form.html",
        {
            "filter": transaction_filter,
            "filter_is_active": False,
            "swap_filter_indicator": True,
        },
    )
    response.headers["HX-Trigger-After-Settle"] = "updated"
    return response


@only_htmx
@login_required
@require_http_methods(["POST"])
def filter_preset_delete(request, preset_id):
    get_object_or_404(FilterPreset, pk=preset_id, owner=request.user).delete()
    return render(
        request,
        "transactions/fragments/filter_presets.html",
        {"filter_presets": FilterPreset.objects.filter(owner=request.user)},
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_all_list(request):
    order = request.session.get("all_transactions_order", "default")

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

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

    transactions = Transaction.objects.prefetch_related(
        "account",
        "account__group",
        "category",
        "tags",
        "account__exchange_currency",
        "account__currency",
        "installment_plan",
        "entities",
        "dca_expense_entries",
        "dca_income_entries",
    ).all()

    f = TransactionsFilter(request.GET, queryset=transactions)

    # Late transactions: date < today and is_paid = False (only shown for default ordering on first page)
    late_transactions = None
    page_number = request.GET.get("page", 1)
    if order == "default" and str(page_number) == "1":
        late_transactions = f.qs.filter(
            date__lt=today,
            is_paid=False,
        ).order_by("date", "id")
        # Exclude late transactions from the main paginated list
        main_transactions = f.qs.exclude(
            date__lt=today,
            is_paid=False,
        )
    else:
        main_transactions = f.qs

    main_transactions = default_order(main_transactions, order=order)

    paginator = Paginator(main_transactions, 100)
    page_obj = paginator.get_page(page_number)

    return render(
        request,
        "transactions/fragments/list_all.html",
        {
            "page_obj": page_obj,
            "paginator": paginator,
            "late_transactions": late_transactions,
        },
    )


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_all_summary(request):
    transactions = Transaction.objects.prefetch_related(
        "account",
        "account__group",
        "category",
        "tags",
        "account__exchange_currency",
        "account__currency",
        "installment_plan",
        "entities",
        "dca_expense_entries",
        "dca_income_entries",
    ).all()

    f = TransactionsFilter(request.GET, queryset=transactions)

    currency_data = calculate_currency_totals(f.qs.all(), ignore_empty=True)
    currency_percentages = calculate_percentage_distribution(currency_data)
    account_data = calculate_account_totals(transactions_queryset=f.qs.all())
    account_percentages = calculate_percentage_distribution(account_data)

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

    return render(request, "transactions/fragments/summary.html", context)


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_all_account_summary(request):
    transactions = Transaction.objects.prefetch_related(
        "account",
        "account__group",
        "category",
        "tags",
        "account__exchange_currency",
        "account__currency",
        "installment_plan",
        "entities",
        "dca_expense_entries",
        "dca_income_entries",
    ).all()

    f = TransactionsFilter(request.GET, queryset=transactions)

    account_data = calculate_account_totals(transactions_queryset=f.qs.all())
    account_percentages = calculate_percentage_distribution(account_data)

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

    return render(request, "transactions/fragments/all_account_summary.html", context)


@only_htmx
@login_required
@require_http_methods(["GET"])
def transaction_all_currency_summary(request):
    transactions = Transaction.objects.prefetch_related(
        "account",
        "account__group",
        "category",
        "tags",
        "account__exchange_currency",
        "account__currency",
        "installment_plan",
        "entities",
        "dca_expense_entries",
        "dca_income_entries",
    ).all()

    f = TransactionsFilter(request.GET, queryset=transactions)

    currency_data = calculate_currency_totals(
        f.qs.exclude(account__in=request.user.untracked_accounts.all()),
        ignore_empty=True,
    )
    currency_percentages = calculate_percentage_distribution(currency_data)

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

    return render(request, "transactions/fragments/all_currency_summary.html", context)


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

    return HttpResponse(
        status=204,
    )


@login_required
@require_http_methods(["GET"])
def transactions_trash_can_index(request):
    return render(request, "transactions/pages/trash.html")


@only_htmx
@login_required
@require_http_methods(["GET"])
def transactions_trash_can_list(request):
    transactions = Transaction.deleted_objects.prefetch_related(
        "account",
        "account__group",
        "category",
        "tags",
        "account__exchange_currency",
        "account__currency",
        "installment_plan",
        "entities",
        "entities",
        "dca_expense_entries",
        "dca_income_entries",
    ).all()

    return render(
        request,
        "transactions/fragments/trash_list.html",
        {"transactions": transactions},
    )


@login_required
@require_http_methods(["GET"])
def get_recent_transactions(request, filter_type=None):
    """Return the 100 most recent non-deleted transactions with optional search."""
    # Get search term from query params
    search_term = request.GET.get("q", "").strip()

    today = timezone.localdate(timezone.now())
    yesterday = today - timezone.timedelta(days=1)
    tomorrow = today + timezone.timedelta(days=1)

    # Base queryset with selected fields
    queryset = (
        Transaction.objects.filter(deleted=False)
        .annotate(
            date_order=Case(
                When(date=today, then=Value(0)),
                When(date=tomorrow, then=Value(1)),
                When(date=yesterday, then=Value(2)),
                When(date__gt=tomorrow, then=Value(3)),
                When(date__lt=yesterday, then=Value(4)),
                default=Value(5),
                output_field=IntegerField(),
            )
        )
        .select_related("account", "category")
        .order_by("date_order", "date", "id")
    )

    if filter_type:
        if filter_type == "expenses":
            queryset = queryset.filter(type=Transaction.Type.EXPENSE)
        elif filter_type == "income":
            queryset = queryset.filter(type=Transaction.Type.INCOME)

    # Apply search if provided
    if search_term:
        queryset = queryset.filter(
            Q(description__icontains=search_term)
            | Q(notes__icontains=search_term)
            | Q(internal_note__icontains=search_term)
            | Q(tags__name__icontains=search_term)
            | Q(category__name__icontains=search_term)
        )

    # Prepare data for JSON response
    data = []
    for t in queryset:
        data.append({"text": str(t), "value": str(t.id)})

    return JsonResponse(data, safe=False)
