import datetime
from copy import deepcopy

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Q
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from django.utils.translation import gettext_lazy as _, ngettext_lazy
from django.views.decorators.http import require_http_methods

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 (
    TransactionForm,
    TransferForm,
    BulkEditTransactionForm,
)
from apps.transactions.models import Transaction
from apps.transactions.utils.calculations import (
    calculate_currency_totals,
    calculate_account_totals,
    calculate_percentage_distribution,
)
from apps.transactions.utils.default_ordering import default_order


@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()

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

            return HttpResponse(
                status=204,
                headers={"HX-Trigger": "updated, hide_offcanvas"},
            )
    else:
        form = TransactionForm(
            initial={
                "date": expected_date,
                "type": transaction_type,
            },
        )

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


@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()

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

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

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

    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:
                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)

            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)
    new_is_paid = False if transaction.is_paid else True
    transaction.is_paid = new_is_paid
    transaction.save()
    transaction_updated.send(sender=transaction)

    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


@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, "order": order, "summary_tab": summary_tab},
    )


@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

    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()

    transactions = default_order(transactions, order=order)

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

    page_number = request.GET.get("page", 1)
    paginator = Paginator(f.qs, 100)
    page_obj = paginator.get_page(page_number)

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


@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.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()

    # Base queryset with selected fields
    queryset = (
        Transaction.objects.filter(deleted=False)
        .select_related("account", "category")
        .order_by("-created_at")
    )

    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)
