import datetime
import io
import os
from django.db.models import Q, Sum, Case, When, IntegerField, CharField, F, DecimalField
from decimal import Decimal, InvalidOperation
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.utils.timezone import make_aware
from django.views.generic import CreateView, DetailView, ListView, View
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models.functions import TruncDate
from django.utils.translation import gettext_lazy as _
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
from banking.forms import NewTransactionForm
from banking.models.account import Account
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from django.utils import timezone
from reportlab.lib import colors
from django.http import HttpResponse
from banking.models.payment_verification import PaymentVerification
from banking.forms import PaymentVerificationForm
from django.core.mail import send_mail
from banking.models.transaction import Transaction
from django.conf import settings
import random
from django.utils import translation
from django.db import transaction
from urllib.parse import urlparse, urlencode, parse_qsl, urlunparse
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont


from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.decorators import user_passes_test, login_required
from django.contrib.auth import login, get_user_model
from django.shortcuts import get_object_or_404, redirect
from django.contrib import messages

from django.contrib.auth import get_user_model, login
from django.contrib.auth.decorators import user_passes_test, login_required
from django.shortcuts import get_object_or_404, redirect

User = get_user_model()


@user_passes_test(lambda u: u.is_staff)
def impersonate_user(request, user_id):
    target = get_object_or_404(User, pk=user_id)


    orig_admin_id = request.user.pk


    target.backend = "django.contrib.auth.backends.ModelBackend"
    login(request, target)


    request.session["impersonate_original_id"] = orig_admin_id
    request.session["impersonate_user_id"] = target.pk
    request.session["is_impersonating"] = True
    request.session["impersonate_read_only"] = True

    request.session.save()

    return redirect("account_list")







from django.contrib.auth import login, logout, get_user_model
from django.shortcuts import redirect, get_object_or_404
from django.contrib.auth.decorators import login_required

User = get_user_model()

@login_required
def stop_impersonation(request):
    """
    Ukončí impersonáciu – admin je kompletne prihlásený späť.
    """
    orig_id = request.session.get("impersonate_original_id")

    if not orig_id:

        logout(request)
        return redirect("/admin/")

    admin_user = get_object_or_404(User, pk=orig_id)


    logout(request)


    request.session.flush()


    admin_user.backend = "django.contrib.auth.backends.ModelBackend"
    login(request, admin_user)


    request.session.pop("impersonate_user_id", None)
    request.session.pop("impersonate_original_id", None)
    request.session.pop("is_impersonating", None)
    request.session.pop("impersonate_read_only", None)


    return redirect("/admin/auth/user/")










# ------------------------------------------------------------------
# ZOZNAM ÚČTOV
# ------------------------------------------------------------------
class AccountsListView(LoginRequiredMixin, ListView):
    model = Account
    context_object_name = "accounts"
    template_name = "banking/account_list.html"

    def get_queryset(self):
        # zobrazí len účty prihláseného usera
        return Account.objects.filter(user=self.request.user)

    def get_context_data(self, **kwargs):
        from collections import defaultdict
        from django.utils import timezone

        context = super().get_context_data(**kwargs)
        context["is_accounts_list_page"] = True

        accounts = context["accounts"]

        # --- existujúci súhrn ---
        expenses_total = (
                Transaction.objects.filter(payer__in=accounts)
                .aggregate(total=Sum("amount"))["total"]
                or 0
        )
        income_total = (
                Transaction.objects.filter(beneficiary__in=accounts)
                .aggregate(total=Sum("amount"))["total"]
                or 0
        )

        context["profile_expenses_total"] = float(expenses_total)
        context["profile_income_total"] = float(income_total)


        account_ids = list(accounts.values_list("id", flat=True))
        today = timezone.localdate()

        tx_all = (
            Transaction.objects.filter(
                Q(payer_id__in=account_ids) | Q(beneficiary_id__in=account_ids),
                created__date__lte=today,
                )
            .select_related("payer", "beneficiary")
        )

        def is_income(tx):
            return tx.beneficiary_id in account_ids

        def is_expense(tx):
            return tx.payer_id in account_ids

        # ---- 30 dní (po dňoch) ----
        start_30 = today - datetime.timedelta(days=29)
        tx_30 = [t for t in tx_all if start_30 <= t.created.date() <= today]

        inc_by_day = defaultdict(float)
        exp_by_day = defaultdict(float)

        for t in tx_30:
            d = t.created.date()
            if is_income(t):
                inc_by_day[d] += float(t.amount)
            if is_expense(t):
                exp_by_day[d] += float(t.amount)

        days = [start_30 + datetime.timedelta(days=i) for i in range(30)]
        context["pchart_30_labels"] = [d.strftime("%d.%m.") for d in days]
        context["pchart_30_income"] = [inc_by_day.get(d, 0.0) for d in days]
        context["pchart_30_expenses"] = [exp_by_day.get(d, 0.0) for d in days]

        # ---- helper na posledných N mesiacov ----
        def last_n_months(n, end):
            y, m = end.year, end.month
            res = []
            for _ in range(n):
                res.append((y, m))
                m -= 1
                if m == 0:
                    m = 12
                    y -= 1
            res.reverse()
            return res

        # ---- 12 mesiacov ----
        months_12 = last_n_months(12, today)
        start_12 = datetime.date(months_12[0][0], months_12[0][1], 1)

        tx_12 = [t for t in tx_all if t.created.date() >= start_12]

        inc_by_month = defaultdict(float)
        exp_by_month = defaultdict(float)

        for t in tx_12:
            key = (t.created.year, t.created.month)
            if is_income(t):
                inc_by_month[key] += float(t.amount)
            if is_expense(t):
                exp_by_month[key] += float(t.amount)

        labels_12 = [f"{m:02d}/{y}" for (y, m) in months_12]
        context["pchart_12_labels"] = labels_12
        context["pchart_12_income"] = [
            inc_by_month.get((y, m), 0.0) for (y, m) in months_12
        ]
        context["pchart_12_expenses"] = [
            exp_by_month.get((y, m), 0.0) for (y, m) in months_12
        ]

        # ---- 6 mesiacov = výrez z 12 ----
        months_6 = months_12[-6:]
        context["pchart_6_labels"] = [f"{m:02d}/{y}" for (y, m) in months_6]
        context["pchart_6_income"] = [
            inc_by_month.get((y, m), 0.0) for (y, m) in months_6
        ]
        context["pchart_6_expenses"] = [
            exp_by_month.get((y, m), 0.0) for (y, m) in months_6
        ]

        return context


# ------------------------------------------------------------------
# DETAIL ÚČTU + FILTRE + ZORADENIE
# ------------------------------------------------------------------
import datetime

from django.db.models import Q, Sum
from django.db.models.functions import TruncDate
from django.utils.timezone import make_aware
from django.views.generic import DetailView
from django.contrib.auth.mixins import LoginRequiredMixin

from banking.models.account import Account
from banking.models.transaction import Transaction


class AccountDetailView(LoginRequiredMixin, DetailView):
    model = Account
    context_object_name = "account"
    template_name = "banking/account_detail.html"
    slug_field = "slug"

    def get_queryset(self):
        # len účty aktuálne prihláseného užívateľa
        return Account.objects.filter(user=self.request.user)

    def get_context_data(self, **kwargs):
        from django.db.models import Q, Sum
        from django.urls import reverse
        from django.utils import timezone
        from decimal import Decimal, InvalidOperation
        import datetime
        from collections import defaultdict

        context = super().get_context_data(**kwargs)
        account = context["account"]
        request = self.request
        params = request.GET


        is_clear = "clear" in params

        # ---------- GET parametre (filtre + sort) ----------
        if is_clear:
            beneficiary_q = ""
            date_from = ""
            date_to = ""
            amount_min = ""
            amount_max = ""
            tx_direction = ""
        else:
            beneficiary_q = (params.get("beneficiary_q") or "").strip()
            date_from = (params.get("date_from") or "").strip()
            date_to = (params.get("date_to") or "").strip()
            amount_min = (params.get("amount_min") or "").strip()
            amount_max = (params.get("amount_max") or "").strip()
            tx_direction = (params.get("tx_direction") or "").strip()

        sort = (params.get("sort") or "-date").strip()  # default: najnovšie hore

        # ---------- základný queryset: všetky platby k danému účtu ----------
        qs = Transaction.objects.filter(
            Q(payer=account) | Q(beneficiary=account)
        )


        if not is_clear:
            # dátum od / do
            if date_from:
                try:
                    d_from = datetime.date.fromisoformat(date_from)
                    qs = qs.filter(created__date__gte=d_from)
                except ValueError:
                    pass

            if date_to:
                try:
                    d_to = datetime.date.fromisoformat(date_to)
                    qs = qs.filter(created__date__lte=d_to)
                except ValueError:
                    pass

            # IBAN (payer aj beneficiary)
            if beneficiary_q:
                qs = qs.filter(
                    Q(payer__iban__icontains=beneficiary_q)
                    | Q(beneficiary__iban__icontains=beneficiary_q)
                )

            # suma min / max
            def to_decimal(val: str):
                if not val:
                    return None
                try:
                    return Decimal(val.replace(",", "."))
                except InvalidOperation:
                    return None

            min_val = to_decimal(amount_min)
            max_val = to_decimal(amount_max)

            if min_val is not None:
                qs = qs.filter(amount__gte=min_val)
            if max_val is not None:
                qs = qs.filter(amount__lte=max_val)

            if tx_direction == "sent":
                qs = qs.filter(payer=account)
            elif tx_direction == "received":
                qs = qs.filter(beneficiary=account)

        # ---------- sortovanie podľa hlavičiek ----------
        # amount riešime špeciálne (podpísaná hodnota), ostatné cez mapu
        # ---------- sortovanie podľa hlavičiek ----------
        # amount a type riešime špeciálne, ostatné cez mapu
        if sort in ("amount", "-amount", "type", "-type"):
            # podpísaná suma: odoslaná = záporná, prijatá = kladná
            # tx_kind: 0 = prijatá, 1 = odoslaná
            qs = qs.annotate(
                signed_amount=Case(
                    When(payer=account, then=-F("amount")),
                    default=F("amount"),
                    output_field=DecimalField(max_digits=12, decimal_places=2),
                ),
                tx_kind=Case(
                    When(payer=account, then=1),
                    default=0,
                    output_field=IntegerField(),
                ),
            )

            if sort == "amount":
                # od najnižšej (najviac záporné) po najvyššiu
                qs = qs.order_by("signed_amount", "-created")
            elif sort == "-amount":
                # od najvyššej (najväčšie príjmy) po najnižšiu (najväčšie výdavky)
                qs = qs.order_by("-signed_amount", "-created")
            elif sort == "type":
                # najprv prijaté (0), potom odoslané (1)
                qs = qs.order_by("tx_kind", "-created")
            else:  # "-type"
                # najprv odoslané (1), potom prijaté (0)
                qs = qs.order_by("-tx_kind", "-created")
        else:
            sort_map = {
                "date": "created",
                "-date": "-created",
                "iban": "beneficiary__iban",
                "-iban": "-beneficiary__iban",
            }

            order_by = sort_map.get(sort, "-created")
            qs = qs.order_by(order_by, "-created")



        # ---------- dáta pre koláčový graf (expenses / income) ----------
        expenses_total = (
                Transaction.objects.filter(payer=account).aggregate(total=Sum("amount"))[
                    "total"
                ]
                or 0
        )
        income_total = (
                Transaction.objects.filter(beneficiary=account).aggregate(
                    total=Sum("amount")
                )["total"]
                or 0
        )

        context["beneficiaries"] = ["expenses", "income"]
        context["amounts"] = [float(expenses_total), float(income_total)]

        # ---------- dáta pre EXTEND panel – stĺpcové grafy ----------
        today = timezone.localdate()

        all_for_charts = (
            Transaction.objects.filter(
                Q(payer=account) | Q(beneficiary=account),
                created__date__lte=today,
                )
            .select_related("payer", "beneficiary")
        )

        def is_income(tx):
            return tx.beneficiary_id == account.id

        def is_expense(tx):
            return tx.payer_id == account.id

        # --- 1) posledných 30 dní – po dňoch ---
        start_30 = today - datetime.timedelta(days=29)
        tx_30 = [
            tx for tx in all_for_charts if start_30 <= tx.created.date() <= today
        ]

        income_by_day = defaultdict(float)
        expenses_by_day = defaultdict(float)

        for tx in tx_30:
            d = tx.created.date()
            if is_income(tx):
                income_by_day[d] += float(tx.amount)
            if is_expense(tx):
                expenses_by_day[d] += float(tx.amount)

        days = [start_30 + datetime.timedelta(days=i) for i in range(30)]
        context["chart_30_labels"] = [d.strftime("%d.%m.") for d in days]
        context["chart_30_income"] = [income_by_day.get(d, 0.0) for d in days]
        context["chart_30_expenses"] = [expenses_by_day.get(d, 0.0) for d in days]

        # --- helper na posledných N mesiacov ---
        def last_n_months(n, end_date):
            y = end_date.year
            m = end_date.month
            result = []
            for _ in range(n):
                result.append((y, m))
                m -= 1
                if m == 0:
                    m = 12
                    y -= 1
            result.reverse()
            return result

        # --- 2) posledných 12 mesiacov ---
        months_12 = last_n_months(12, today)
        start_12 = datetime.date(months_12[0][0], months_12[0][1], 1)

        tx_12 = [tx for tx in all_for_charts if tx.created.date() >= start_12]

        income_by_month = defaultdict(float)
        expenses_by_month = defaultdict(float)

        for tx in tx_12:
            key = (tx.created.year, tx.created.month)
            if is_income(tx):
                income_by_month[key] += float(tx.amount)
            if is_expense(tx):
                expenses_by_month[key] += float(tx.amount)

        labels_12 = [f"{m:02d}/{y}" for (y, m) in months_12]
        income_12 = [income_by_month.get((y, m), 0.0) for (y, m) in months_12]
        expenses_12 = [expenses_by_month.get((y, m), 0.0) for (y, m) in months_12]

        context["chart_12_labels"] = labels_12
        context["chart_12_income"] = income_12
        context["chart_12_expenses"] = expenses_12

        # --- 3) posledných 6 mesiacov ---
        months_6 = months_12[-6:]
        labels_6 = [f"{m:02d}/{y}" for (y, m) in months_6]
        income_6 = [income_by_month.get((y, m), 0.0) for (y, m) in months_6]
        expenses_6 = [expenses_by_month.get((y, m), 0.0) for (y, m) in months_6]

        context["chart_6_labels"] = labels_6
        context["chart_6_income"] = income_6
        context["chart_6_expenses"] = expenses_6

        # ---------- do template ----------
        context["all_transactions"] = qs
        context["beneficiary_q"] = beneficiary_q
        context["date_from"] = date_from
        context["date_to"] = date_to
        context["amount_min"] = amount_min
        context["amount_max"] = amount_max
        context["sort"] = sort
        context["tx_direction"] = tx_direction
        context["new_tx_url"] = reverse(
            "transaction_create", kwargs={"slug": account.slug}
        )

        return context

FONT_DIR = os.path.join(settings.BASE_DIR, 'static', 'fonts')
DEFAULT_FONT_NAME = "DejaVuSans"

try:
    pdfmetrics.registerFont(TTFont(DEFAULT_FONT_NAME, os.path.join(FONT_DIR, 'DejaVuSans.ttf')))
    pdfmetrics.registerFont(TTFont(f"{DEFAULT_FONT_NAME}-Bold", os.path.join(FONT_DIR, 'DejaVuSans-Bold.ttf')))
    DEFAULT_FONT_NAME_BOLD = f"{DEFAULT_FONT_NAME}-Bold"
except FileNotFoundError:
    # Ak fonty nenájde, použije štandardný font, ale diakritika sa nemusí zobraziť správne.
    DEFAULT_FONT_NAME = "Helvetica"
    DEFAULT_FONT_NAME_BOLD = "Helvetica-Bold"

class BaseAccountStatementPDFView(LoginRequiredMixin, View):
    """
    Spoločný základ pre PDF výpisy účtu.
    Vizuál napodobňuje webovú aplikáciu "VirtualBank" z fotiek,
    bez zasahovania do inej logiky.
    """

    period_label = ""

    def get_account(self):
        return get_object_or_404(
            Account,
            slug=self.kwargs["slug"],
            user=self.request.user,
        )

    def get_transactions(self, account):
        """
        Túto časť musíš implementovať vo svojom potomkovi.
        Vráti queryset/zoznam transakcií.
        """
        raise NotImplementedError

    def get_filter_rows(self):
        """
        Dodatočné riadky filtrov do informačnej tabuľky v hlavičke PDF.
        Každá položka má tvar: [label, value]
        """
        return []

    def render_pdf(self, account, transactions, period_label: str, filter_rows=None) -> bytes:
        """Vygeneruje PDF a vráti bytes s moderným vizuálom VirtualBank."""
        buffer = io.BytesIO()

        doc = SimpleDocTemplate(
            buffer,
            pagesize=A4,
            leftMargin=20 * mm,
            rightMargin=20 * mm,
            topMargin=20 * mm,
            bottomMargin=20 * mm,
        )

        styles = getSampleStyleSheet()

        # --- Vlastné štýly podľa webu ---
        header_bar_style = ParagraphStyle(
            "HeaderBar",
            fontName=DEFAULT_FONT_NAME_BOLD,
            fontSize=16,
            textColor=colors.white,
            backColor=colors.Color(0.1, 0.15, 0.3),
            leftIndent=10 * mm,
            rightIndent=10 * mm,
            spaceBefore=10,
            spaceAfter=15,
            alignment=0,
            leading=24,
            paddingTop=10,
            paddingBottom=10
        )

        logo_text_style = ParagraphStyle(
            "LogoText",
            parent=header_bar_style,
            fontSize=16,
            textColor=colors.white,
            alignment=0,
        )

        normal_style = ParagraphStyle(
            "NormalSlovak",
            fontName=DEFAULT_FONT_NAME,
            fontSize=10,
            textColor=colors.black,
            leading=14,
        )

        sub_title_style = ParagraphStyle(
            "SubTitle",
            parent=normal_style,
            fontName=DEFAULT_FONT_NAME_BOLD,
            fontSize=12,
            textColor=colors.Color(0.1, 0.15, 0.3),
            spaceBefore=15,
            spaceAfter=10
        )

        elements = []

        # ----- 1. Branding v hlavičke -----
        elements.append(Paragraph(
            f'<font name="{DEFAULT_FONT_NAME_BOLD}">Virtual</font><font name="{DEFAULT_FONT_NAME}">Bank</font>',
            logo_text_style
        ))
        elements.append(Spacer(1, 5 * mm))

        # ----- 2. Informácie o účte -----
        account_info_data = [
            [Paragraph(_('Account name'), normal_style), f": {account.name}"],
            [Paragraph('IBAN', normal_style), f": {account.iban}"],
            [
                Paragraph(_('Generated'), normal_style),
                f": {timezone.localtime(timezone.now()).strftime('%d.%m.%Y %H:%M')}"
            ],
        ]

        if period_label:
            account_info_data.append([Paragraph(_('Period'), normal_style), f": {period_label}"])

        if filter_rows:
            for label, value in filter_rows:
                account_info_data.append([Paragraph(label, normal_style), f": {value}"])

        account_info_table = Table(account_info_data, colWidths=[30 * mm, None])
        account_info_table.setStyle(TableStyle([
            ('FONT', (0,0), (-1,-1), DEFAULT_FONT_NAME),
            ('FONT', (0,0), (0,-1), DEFAULT_FONT_NAME_BOLD),
            ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
            ('ALIGN', (0,0), (-1,-1), 'LEFT'),
            ('LEFTPADDING', (0,0), (0,-1), 0),
        ]))

        elements.append(account_info_table)
        elements.append(Spacer(1, 15 * mm))
        elements.append(Paragraph(_("Transaction Details"), sub_title_style))

        # ----- 3. Tabuľka transakcií -----
        data = [
            [
                _("Date"),
                _("Counterparty IBAN"),
                _("Amount [EUR]"),
                _("Type"),
                _("Details"),
            ]
        ]

        row_colors = []

        for tx in transactions:
            is_outgoing = tx.payer_id == account.id
            counterparty = tx.beneficiary if is_outgoing else tx.payer

            date_str = timezone.localtime(tx.created).strftime("%d.%m.%Y %H:%M")
            amount_str = f"{tx.amount:.2f}"

            if is_outgoing:
                amount_str = f"-{amount_str}"
                row_colors.append(colors.Color(0.8, 0.1, 0.1))
            else:
                row_colors.append(colors.Color(0.1, 0.6, 0.1))

            ttype = _("Sent") if is_outgoing else _("Received")

            info_parts = []
            if tx.information:
                info_parts.append(tx.information)
            if tx.variable_symbol:
                info_parts.append(f"VS: {tx.variable_symbol}")
            if tx.specific_symbol:
                info_parts.append(f"SS: {tx.specific_symbol}")
            if tx.constant_symbol:
                info_parts.append(f"KS: {tx.constant_symbol}")
            info = ", ".join(info_parts)

            data.append(
                [
                    date_str,
                    counterparty.iban,
                    amount_str,
                    ttype,
                    info,
                ]
            )

        table = Table(data, repeatRows=1, colWidths=[30*mm, 50*mm, 25*mm, 20*mm, 45*mm])

        ts = TableStyle([
            ('FONTNAME', (0, 0), (-1, -1), DEFAULT_FONT_NAME),
            ('FONTSIZE', (0, 0), (-1, -1), 8),
            ('BACKGROUND', (0, 0), (-1, 0), colors.Color(0.1, 0.15, 0.3)),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('FONTNAME', (0, 0), (-1, 0), DEFAULT_FONT_NAME_BOLD),
            ('ALIGN', (0, 0), (-1, 0), 'CENTER'),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('LINEBELOW', (0, 1), (-1, -1), 0.25, colors.Color(0.8, 0.8, 0.8)),
            ('ALIGN', (2, 1), (2, -1), 'RIGHT'),
            ('TOPPADDING', (0, 0), (-1, -1), 5),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
            ('LEFTPADDING', (0, 0), (-1, -1), 8),
            ('RIGHTPADDING', (0, 0), (-1, -1), 8),
        ])

        for row_index, row_color in enumerate(row_colors):
            real_row_index = row_index + 1
            ts.add('BACKGROUND', (0, real_row_index), (-1, real_row_index), row_color)
            ts.add('TEXTCOLOR', (0, real_row_index), (-1, real_row_index), colors.white)

        table.setStyle(ts)
        elements.append(table)

        # Pomocná funkcia na vykreslenie čísla strany
        def add_page_number(canvas, doc):
            page_num = canvas.getPageNumber()
            text = f"{page_num}"

            canvas.saveState()
            # Nastavíme font a veľkosť písma pre číslovanie
            canvas.setFont(DEFAULT_FONT_NAME, 9)
            # Vykreslíme text na stred (105 mm je polovica A4) a 10 mm odspodu
            canvas.drawCentredString(105 * mm, 10 * mm, text)
            canvas.restoreState()

        doc.build(
            elements,
            onFirstPage=add_page_number,
            onLaterPages=add_page_number
        )
        pdf = buffer.getvalue()
        buffer.close()
        return pdf

    def get_period_label(self) -> str:
        return self.period_label

    def get(self, request, *args, **kwargs):
        lang = getattr(request, "LANGUAGE_CODE", None) or translation.get_language()
        with translation.override(lang):
            account = self.get_account()
            txs = self.get_transactions(account)
            period_label = self.get_period_label()
            filter_rows = self.get_filter_rows()

            pdf_bytes = self.render_pdf(account, txs, period_label, filter_rows)

            lang_suffix = lang.upper() if lang else "EN"
            filename = f"statement_{account.iban}_{lang_suffix}.pdf"

            response = HttpResponse(pdf_bytes, content_type="application/pdf")
            response["Content-Disposition"] = f'inline; filename="{filename}"'
            return response


class AccountStatementPDFView(BaseAccountStatementPDFView):
    """PDF s *všetkými* transakciami účtu."""

    period_label = _("All transactions")

    def get_transactions(self, account):
        return (
            Transaction.objects.filter(
                Q(payer=account) | Q(beneficiary=account)
            )
            .select_related("payer", "beneficiary")
            .order_by("-created")
        )


class AccountStatementFilteredPDFView(BaseAccountStatementPDFView):
    """PDF, ktoré rešpektuje aktuálny filter z detailu účtu."""

    def get_period_label(self) -> str:
        df = (self.request.GET.get("date_from") or "").strip()
        dt = (self.request.GET.get("date_to") or "").strip()

        if df and dt:
            return _("From %(df)s to %(dt)s") % {"df": df, "dt": dt}
        elif df:
            return _("From %(df)s") % {"df": df}
        elif dt:
            return _("Up to %(dt)s") % {"dt": dt}
        return _("Current filter")

        def get_filter_rows(self):
            params = self.request.GET

        beneficiary_q = (params.get("beneficiary_q") or "").strip()
        amount_min = (params.get("amount_min") or "").strip()
        amount_max = (params.get("amount_max") or "").strip()
        tx_direction = (params.get("tx_direction") or "").strip()

        rows = []

        if beneficiary_q:
            rows.append((_("Counterparty IBAN"), beneficiary_q))

        if tx_direction == "sent":
            rows.append((_("Transaction type"), _("Sent")))
        elif tx_direction == "received":
            rows.append((_("Transaction type"), _("Received")))

        if amount_min and amount_max:
            rows.append((_("Amount"), _("%(amount_min)s - %(amount_max)s EUR") % {
                "amount_min": amount_min,
                "amount_max": amount_max,
            }))
        elif amount_min:
            rows.append((_("Amount"), _("from %(amount_min)s EUR") % {
                "amount_min": amount_min,
            }))
        elif amount_max:
            rows.append((_("Amount"), _("up to %(amount_max)s EUR") % {
                "amount_max": amount_max,
            }))

        return rows

    def get_transactions(self, account):
        qs = Transaction.objects.filter(
            Q(payer=account) | Q(beneficiary=account)
        ).select_related("payer", "beneficiary")

        params = self.request.GET

        date_from = params.get("date_from")
        date_to = params.get("date_to")

        if date_from:
            try:
                d_from = datetime.date.fromisoformat(date_from)
                qs = qs.filter(created__date__gte=d_from)
            except ValueError:
                pass

        if date_to:
            try:
                d_to = datetime.date.fromisoformat(date_to)
                qs = qs.filter(created__date__lte=d_to)
            except ValueError:
                pass

        beneficiary_q = (params.get("beneficiary_q") or "").strip()
        if beneficiary_q:
            qs = qs.filter(
                Q(payer__iban__icontains=beneficiary_q)
                | Q(beneficiary__iban__icontains=beneficiary_q)
            )

        amount_min = (params.get("amount_min") or "").strip()
        amount_max = (params.get("amount_max") or "").strip()
        tx_direction = (params.get("tx_direction") or "").strip()

        def to_float(val):
            try:
                return float(val.replace(",", "."))
            except ValueError:
                return None

        min_val = to_float(amount_min)
        max_val = to_float(amount_max)

        if min_val is not None:
            qs = qs.filter(amount__gte=min_val)
        if max_val is not None:
            qs = qs.filter(amount__lte=max_val)

        if tx_direction == "sent":
            qs = qs.filter(payer=account)
        elif tx_direction == "received":
            qs = qs.filter(beneficiary=account)

        sort_param = params.get("sort", "-date")
        if sort_param == "date":
            qs = qs.order_by("created")
        else:
            qs = qs.order_by("-created")

        return qs





# ------------------------------------------------------------------
# DETAIL TRANSAKCIE
# ------------------------------------------------------------------
class TransactionDetailView(LoginRequiredMixin, DetailView):
    model = Transaction
    slug_field = "slug"

    def dispatch(self, request, *args, **kwargs):
        obj = self.get_object()
        if obj.payer.user != request.user and obj.beneficiary.user != request.user:
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)


# ------------------------------------------------------------------
# ZMENA NÁZVU ÚČTU
# ------------------------------------------------------------------
class UpdateAccountNameView(LoginRequiredMixin, View):
    def post(self, request, slug):
        account = get_object_or_404(Account, slug=slug, user=request.user)
        account.name = request.POST.get("new_account_name")
        account.save()
        return redirect("account_detail", slug=slug)


# ------------------------------------------------------------------
# NOVÁ PLATBA Z KONKRÉTNEHO ÚČTU
# ------------------------------------------------------------------
class NewTransactionFromAccountCreateView(LoginRequiredMixin, CreateView):
    model = Transaction
    form_class = NewTransactionForm
    template_name = "banking/NEW_PAYMENT.html"

    def get_template_names(self):
        # ak je požiadavka z bottom panelu (AJAX), vrátime len vnútro bez base.html
        if self.request.headers.get("x-requested-with") == "XMLHttpRequest":
            return ["banking/payment_form_inner.html"]
        return [self.template_name]

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["user"] = self.request.user
        account = get_object_or_404(Account, slug=self.kwargs["slug"])
        kwargs["initial"] = {"payer_iban": account}
        return kwargs

    def form_valid(self, form):
        return _start_payment_verification(self.request, form)


# ------------------------------------------------------------------
# NOVÁ PLATBA (bez predvyplneného účtu)
# ------------------------------------------------------------------
class NewTransactionCreateView(LoginRequiredMixin, CreateView):
    model = Transaction
    form_class = NewTransactionForm
    template_name = "banking/NEW_PAYMENT.html"

    def get_template_names(self):
        if self.request.headers.get("x-requested-with") == "XMLHttpRequest":
            return ["banking/payment_form_inner.html"]
        return [self.template_name]

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["user"] = self.request.user
        return kwargs

    def form_valid(self, form):
        return _start_payment_verification(self.request, form)


class ProcessPaymentView(NewTransactionCreateView):
    """
    Číta parametre z URL query-stringu a pred-vyplní nimi formulár.
    Používa tú istú validáciu + ukladanie ako NewTransactionCreateView.
    GET  – zobrazenie formulára s prefillom
    POST – odoslanie platby
    """

    def dispatch(self, request, *args, **kwargs):
        order_id = request.GET.get("order_id")
        token = request.GET.get("token")
        callback = request.GET.get("callback")

        if order_id and token and callback:
            request.session["eshop_payment"] = {
                "order_id": str(order_id),
                "token": str(token),
                "callback": str(callback),
            }
            request.session.modified = True

        return super().dispatch(request, *args, **kwargs)

    def get_initial(self):
        initial = super().get_initial()
        qs = self.request.GET

        mapping = {
            "amount": "amount",
            "beneficiary_iban": "beneficiary_iban",
            "iban": "beneficiary_iban",   # kvôli URL z e-shopu
            "information": "information",
            "variable_symbol": "variable_symbol",
            "specific_symbol": "specific_symbol",
            "constant_symbol": "constant_symbol",
        }

        for qs_key, form_key in mapping.items():
            if qs.get(qs_key):
                initial[form_key] = qs.get(qs_key)

        # order_id -> specific_symbol, ak specific_symbol neprišiel explicitne
        if qs.get("order_id") and not initial.get("specific_symbol"):
            initial["specific_symbol"] = str(qs.get("order_id"))

        return initial

# ------------------------------------------------------------------
# DUPLIKOVANIE EXISTUJÚCEJ PLATBY
# ------------------------------------------------------------------
class DuplicateTransactionView(LoginRequiredMixin, CreateView):
    model = Transaction
    form_class = NewTransactionForm
    template_name = "banking/NEW_PAYMENT.html"

    def get_template_names(self):
        if self.request.headers.get("x-requested-with") == "XMLHttpRequest":
            return ["banking/payment_form_inner.html"]
        return [self.template_name]

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["user"] = self.request.user
        account = get_object_or_404(Account, slug=self.kwargs["slug"])
        tx = get_object_or_404(
            Transaction, id=self.kwargs["transaction_id"], payer=account
        )
        kwargs["initial"] = dict(
            payer_iban=account,
            beneficiary_iban=tx.beneficiary.iban,
            amount=tx.amount,
            variable_symbol=tx.variable_symbol,
            specific_symbol=tx.specific_symbol,
            constant_symbol=tx.constant_symbol,
            information=tx.information,
        )
        return kwargs

    def form_valid(self, form):
        return _start_payment_verification(self.request, form)

def _send_payment_success_emails(payer_acc, beneficiary_acc, amount):
    """
    E-maily pre oboch účastníkov transakcie.
    """
    payer_user = payer_acc.user
    beneficiary_user = beneficiary_acc.user

    payer_name = payer_user.get_full_name() or payer_user.username
    beneficiary_name = beneficiary_user.get_full_name() or beneficiary_user.username

    # ----- Email pre odosielateľa -----
    if payer_user.email:
        send_mail(
            subject=_("Payment sent"),
            message=_(
                "You have sent a payment of %(amount).2f EUR to %(name)s (%(iban)s)."
            ) % {
                        "amount": amount,
                        "name": beneficiary_name,
                        "iban": beneficiary_acc.iban,
                    },
            from_email=getattr(settings, "DEFAULT_FROM_EMAIL", None),
            recipient_list=[payer_user.email],
            fail_silently=False,
        )

    # ----- Email pre príjemcu -----
    if beneficiary_user.email:
        send_mail(
            subject=_("Payment received"),
            message=_(
                "You have received a payment of %(amount).2f EUR from %(name)s (%(iban)s)."
            ) % {
                        "amount": amount,
                        "name": payer_name,
                        "iban": payer_acc.iban,
                    },
            from_email=getattr(settings, "DEFAULT_FROM_EMAIL", None),
            recipient_list=[beneficiary_user.email],
            fail_silently=False,
        )


def _start_payment_verification(request, form):
    """
    1) Validácia
    2) Vytvor PaymentVerification (amount ako FLOAT)
    3) Pošli kód na email odosielateľa
    4) Redirect na verify stránku
    """
    payer_acc = form.cleaned_data["payer_iban"]
    beneficiary_iban = form.cleaned_data["beneficiary_iban"]

    try:
        beneficiary_acc = Account.objects.get(iban=beneficiary_iban)
    except Account.DoesNotExist:
        form.add_error("beneficiary_iban", _("Please enter correct beneficiary IBAN."))
        return render(request, "banking/NEW_PAYMENT.html", {"form": form})

    if beneficiary_iban == payer_acc.iban:
        form.add_error("beneficiary_iban", _("Payer IBAN cannot be the same as beneficiary IBAN."))
        return render(request, "banking/NEW_PAYMENT.html", {"form": form})

    # --- FLOAT ---
    amount = float(form.cleaned_data["amount"])

    if amount > float(payer_acc.balance):
        form.add_error("amount", _("Transaction amount cannot be greater than the payer's account balance."))
        return render(request, "banking/NEW_PAYMENT.html", {"form": form})

    if amount <= 0:
        form.add_error("amount", _("Transaction amount must be greater than zero."))
        return render(request, "banking/NEW_PAYMENT.html", {"form": form})

    information = form.cleaned_data.get("information") or ""
    variable_symbol = form.cleaned_data.get("variable_symbol") or ""
    specific_symbol = form.cleaned_data.get("specific_symbol") or ""
    constant_symbol = form.cleaned_data.get("constant_symbol") or ""

    code = f"{random.randint(0, 999_999):06d}"

    verification = PaymentVerification.objects.create(
        user=request.user,
        payer_account=payer_acc,
        beneficiary_account=beneficiary_acc,
        amount=amount,  # FLOAT, nie Decimal
        information=information,
        variable_symbol=variable_symbol,
        specific_symbol=specific_symbol,
        constant_symbol=constant_symbol,
        code=code,
    )

    # email s kódom len odosielateľovi (payer = request.user)
    if request.user.email:
        send_mail(
            subject=_("Payment verification code"),
            message=_("Your verification code for payment of %(amount).2f EUR is %(code)s.") % {
                "amount": amount,
                "code": code,
            },
            from_email=getattr(settings, "DEFAULT_FROM_EMAIL", None),
            recipient_list=[request.user.email],
            fail_silently=False,
        )

    return HttpResponseRedirect(reverse("verify_payment", kwargs={"pk": verification.pk}))

def _append_query_params(url: str, extra: dict) -> str:
    """
    Bezpečne pridá query parametre do existujúcej URL (zachová pôvodné).
    """
    parts = urlparse(url)
    query = dict(parse_qsl(parts.query))
    query.update(extra)
    new_query = urlencode(query, doseq=True)
    return urlunparse((parts.scheme, parts.netloc, parts.path, parts.params, new_query, parts.fragment))

class VerifyPaymentView(LoginRequiredMixin, View):
    template_name = "banking/payment_verification.html"

    def get_object(self, pk):
        return get_object_or_404(
            PaymentVerification,
            pk=pk,
            user=self.request.user,
            is_used=False,
        )

    def get(self, request, pk):
        verification = self.get_object(pk)
        form = PaymentVerificationForm()
        return render(request, self.template_name, {"verification": verification, "form": form})

    def post(self, request, pk):
        verification = self.get_object(pk)
        form = PaymentVerificationForm(request.POST)

        if not form.is_valid():
            return render(request, self.template_name, {"verification": verification, "form": form})

        # expirácia
        if verification.is_expired():
            form.add_error("code", _("The verification code has expired. Please create the payment again."))
            return render(request, self.template_name, {"verification": verification, "form": form})

        # zlý kód
        if form.cleaned_data["code"] != verification.code:
            form.add_error("code", _("Invalid verification code."))
            return render(request, self.template_name, {"verification": verification, "form": form})

        # všetko OK → spracuj transakciu
        payer_acc = verification.payer_account
        beneficiary_acc = verification.beneficiary_account
        amount = float(verification.amount)

        with transaction.atomic():
            # lock účtov
            payer_acc = Account.objects.select_for_update().get(pk=payer_acc.pk)
            beneficiary_acc = Account.objects.select_for_update().get(pk=beneficiary_acc.pk)

            # kontrola zostatku
            if amount > float(payer_acc.balance):
                form.add_error(None, _("Insufficient balance on the payer account."))
                return render(request, self.template_name, {"verification": verification, "form": form})

            # zmena zostatkov
            payer_acc.balance = float(payer_acc.balance) - amount
            beneficiary_acc.balance = float(beneficiary_acc.balance) + amount

            payer_acc.save(update_fields=["balance"])
            beneficiary_acc.save(update_fields=["balance"])

            tx = Transaction.objects.create(
                payer=payer_acc,
                beneficiary=beneficiary_acc,
                amount=amount,
                variable_symbol=verification.variable_symbol or "",
                specific_symbol=verification.specific_symbol or "",
                constant_symbol=verification.constant_symbol or "",
                information=verification.information or "",
            )

            verification.is_used = True
            verification.save(update_fields=["is_used"])

        # emaily po úspechu
        _send_payment_success_emails(payer_acc, beneficiary_acc, amount)

        messages.success(request, _("Transaction successfully sent!"))

        payload = request.session.pop("eshop_payment", None)
        request.session.modified = True

        if payload:
            callback = payload.get("callback")
            order_id = payload.get("order_id")
            token = payload.get("token")

            if callback and order_id and token:
                # redirect späť do e-shopu: callback + order_id + token
                redirect_url = _append_query_params(callback, {"order_id": order_id, "token": token})
                return HttpResponseRedirect(redirect_url)

        # fallback pre klasickú platbu
        return HttpResponseRedirect(
            reverse("account_detail", kwargs={"slug": payer_acc.iban.lower()})
        )



# ------------------------------------------------------------------
# LANDING PAGE
# ------------------------------------------------------------------
def landing_page_view(request):
    return render(request, "banking/landing_page.html", {"current_path": request.path})


# ==================================================================
# Pomocná funkcia – spracuje a uloží transakciu
# ==================================================================

def _start_payment_verification(request, form):
    """
    1. Overí dáta platby (rovnaká logika ako doteraz v _process_transaction_form).
    2. Vytvorí PaymentVerification.
    3. Pošle mail s kódom.
    4. Redirectne na stránku s overením kódu.
    """
    payer_acc = form.cleaned_data["payer_iban"]
    beneficiary_iban = form.cleaned_data["beneficiary_iban"]

    # --- pôvodná logika validácie príjemcu ---
    try:
        beneficiary_acc = Account.objects.get(iban=beneficiary_iban)
    except Account.DoesNotExist:
        form.add_error("beneficiary_iban", _("Please enter correct beneficiary iban"))
        return render(request, "banking/NEW_PAYMENT.html", {"form": form})

    if beneficiary_iban == payer_acc.iban:
        form.add_error(
            "beneficiary_iban",
            _("Payer iban cannot be the same as the beneficiary's iban"),
        )
        return render(request, "banking/NEW_PAYMENT.html", {"form": form})

    amount = form.cleaned_data["amount"]
    if amount > payer_acc.balance:
        form.add_error(
            "amount",
            _("Transaction amount cannot be greater than the payer's account balance"),
        )
        return render(request, "banking/NEW_PAYMENT.html", {"form": form})
    if amount <= 0:
        form.add_error("amount", _("Transaction amount must be greater than zero"))
        return render(request, "banking/NEW_PAYMENT.html", {"form": form})

    # --- všetko OK, vytvoríme verifikáciu ---
    information = form.cleaned_data.get("information") or ""
    variable_symbol = form.cleaned_data.get("variable_symbol") or ""
    specific_symbol = form.cleaned_data.get("specific_symbol") or ""
    constant_symbol = form.cleaned_data.get("constant_symbol") or ""

    code = f"{random.randint(0, 999_999):06d}"

    verification = PaymentVerification.objects.create(
        user=request.user,
        payer_account=payer_acc,
        beneficiary_account=beneficiary_acc,
        amount=amount,
        information=information,
        variable_symbol=variable_symbol,
        specific_symbol=specific_symbol,
        constant_symbol=constant_symbol,
        code=code,
    )

    # --- pošleme email s kódom (použijeme existujúce email nastavenia) ---
    if request.user.email:
        send_mail(
            subject=_("Payment verification code"),
            message=_(
                "Your verification code for payment of %(amount).2f EUR is %(code)s."
            )
                    % {"amount": amount, "code": code},
            from_email=getattr(settings, "DEFAULT_FROM_EMAIL", None),
            recipient_list=[request.user.email],
            fail_silently=False,
        )

    # presmerujeme na stránku, kde zadá kód
    return HttpResponseRedirect(
        reverse("verify_payment", kwargs={"pk": verification.pk})
    )


def _process_transaction_form(request, form):
    payer_acc = form.cleaned_data["payer_iban"]
    beneficiary_iban = form.cleaned_data["beneficiary_iban"]

    try:
        beneficiary_acc = Account.objects.get(iban=beneficiary_iban)
    except Account.DoesNotExist:
        form.add_error("beneficiary_iban", _("Please enter correct beneficiary iban"))
        return render(request, form.template_name, {"form": form})

    if beneficiary_iban == payer_acc.iban:
        form.add_error(
            "beneficiary_iban",
            "Payer iban cannot be the same as the beneficiary's iban",
        )
        return render(request, form.template_name, {"form": form})

    amount = form.cleaned_data["amount"]
    if amount > payer_acc.balance:
        form.add_error(
            "amount",
            "Transaction amount cannot be greater than the payer's account balance",
        )
        return render(request, form.template_name, {"form": form})
    if amount <= 0:
        form.add_error("amount", "Transaction amount must be greater than zero")
        return render(request, form.template_name, {"form": form})

    payer_acc.balance -= amount
    payer_acc.save()
    beneficiary_acc.balance += amount
    beneficiary_acc.save()

    tx = form.save(commit=False)
    tx.payer = payer_acc
    tx.beneficiary = beneficiary_acc
    tx.save()

    messages.success(request, _("Transaction successfully sent!"))
    return HttpResponseRedirect(
        reverse("account_detail", kwargs={"slug": payer_acc.iban.lower()})
    )
