# views.py
from django.db import transaction
from django.utils import timezone
from django.views import View
from django.shortcuts import render
from django.http import JsonResponse, HttpRequest
from django.db.models import Q

from payments.models import RechargePaymentSummary
from recharge.models import RechargeTransaction

def _norm(s):
    return (s or "").strip().lower()

def _as_bool(v) -> bool:
    if v is None:
        return False
    s = str(v).strip().lower()
    return s in {"1", "true", "yes", "y", "on"}

def run_sync_rps_from_rt(*, dry_run: bool, limit: int|None, verbose_list: bool, latest_first: bool):
    """
    Core logic: for each RPS (non-success), if there exists an RT Success for the same order_id,
    update RPS to Success (carry status_message where possible).
    Returns a dict with summary + optional change lines (for verbose).
    """
    order_by = "-id" if latest_first else "id"

    qs = RechargePaymentSummary.objects.all().order_by(order_by)
    qs = qs.exclude(recharge_status__iexact="Success")
    if limit:
        qs = qs[:limit]

    scanned = 0
    changed = 0
    no_order = 0
    changes_log = []

    ctx = transaction.atomic() if not dry_run else _NullContext()
    with ctx:
        # iterate in chunks for safety
        for rps in qs.iterator(chunk_size=500):
            scanned += 1
            order_id = getattr(rps, "order_id", None)
            if not order_id:
                no_order += 1
                continue

            # Prefer most recent RT success for the order
            rt_success = (
                RechargeTransaction.objects
                .filter(order_id=order_id, status__iexact="Success")
                .order_by("-created_at")
                .first()
            )
            if not rt_success:
                continue

            old_status = rps.recharge_status
            old_msg = getattr(rps, "status_message", "") or ""
            new_msg = getattr(rt_success, "status_message", None) or old_msg

            rps.recharge_status = "Success"
            try:
                rps.status_message = new_msg
            except Exception:
                pass

            if hasattr(rps, "updated_at"):
                try:
                    rps.updated_at = timezone.now()
                except Exception:
                    pass

            if not dry_run:
                rps.save(update_fields=[
                    "recharge_status",
                    *(["status_message"] if hasattr(rps, "status_message") else []),
                    *(["updated_at"] if hasattr(rps, "updated_at") else []),
                ])

            changed += 1
            if verbose_list:
                changes_log.append(
                    f"{order_id}: {old_status!r} -> 'Success' (msg: {old_msg!r} -> {new_msg!r})"
                )

    return {
        "scanned": scanned,
        "updated_to_success": changed,
        "skipped_no_order_id": no_order,
        "dry_run": dry_run,
        "latest_first": latest_first,
        "changes": changes_log if verbose_list else [],
    }

class _NullContext:
    def __enter__(self): return self
    def __exit__(self, exc_type, exc, tb): return False


class AdminSyncRPSView(View):
    template_name = "admin_tools/sync_rps_from_rt.html"

    def get(self, request):
        return render(request, self.template_name, {
            "result": None,
            "defaults": {
                "dry_run": True,          # default UI state only
                "verbose_list": False,
                "limit": "",
                "order": "desc",
            }
        })

    def post(self, request):
        # Because of the hidden inputs, we always receive "0" or "1"
        dry_run = _as_bool(request.POST.get("dry_run"))           # "1" -> True, "0" -> False
        verbose_list = _as_bool(request.POST.get("verbose_list")) # same
        try:
            limit = int(request.POST.get("limit") or 0) or None
        except Exception:
            limit = None
        order = (request.POST.get("order") or "desc").lower()
        latest_first = (order != "asc")

        result = run_sync_rps_from_rt(
            dry_run=dry_run,
            limit=limit,
            verbose_list=verbose_list,
            latest_first=latest_first,
        )

        return render(request, self.template_name, {
            "result": result,
            "defaults": {
                "dry_run": dry_run,
                "verbose_list": verbose_list,
                "limit": "" if limit is None else limit,
                "order": "desc" if latest_first else "asc",
            }
        })


class AdminSyncRPSJSON(View):
    """
    Optional JSON endpoint to trigger via AJAX or scripts:
    GET /devadmin/api/sync-rps/?dry_run=1&verbose=1&limit=200&order=desc
    """
    def get(self, request: HttpRequest):
        dry_run = _as_bool(request.GET.get("dry_run", "1"), True)
        verbose_list = _as_bool(request.GET.get("verbose", "0"), False)
        try:
            limit = int(request.GET.get("limit") or 0) or None
        except Exception:
            limit = None
        order = (request.GET.get("order") or "desc").lower()
        latest_first = (order != "asc")

        result = run_sync_rps_from_rt(
            dry_run=dry_run,
            limit=limit,
            verbose_list=verbose_list,
            latest_first=latest_first,
        )
        return JsonResponse(result, safe=False)



# views.py
from datetime import datetime
from django.db.models import OuterRef, Exists, Q
from django.shortcuts import render
from django.views import View
from django.core.paginator import Paginator
from django.utils.timezone import make_aware

from payments.models import PaymentTransaction
from recharge.models import RechargeTransaction
from payments.razorpay_snap import fetch_razorpay_snapshot
from django.db.models import Q, OuterRef, Exists
def _parse_date(s: str, end=False):
    if not s:
        return None
    dt = datetime.strptime(s, "%Y-%m-%d")
    if end:
        dt = dt.replace(hour=23, minute=59, second=59, microsecond=999999)
    return make_aware(dt)

STATUS_OPTIONS = [
    ("initiated", "Initiated"),
    ("verified", "Verified"),
    ("success", "Success"),
    ("failed", "Failed"),
    ("error", "Error"),
    ("cancelled", "Cancelled"),
    ("initiating", "Initiating"),
    ("notrequired", "NotRequired"),
]

def _truthy(x) -> bool:
    return str(x).strip().lower() in {"1","true","on","yes"}

class AdminPaymentAuditView(View):
    """
    Admin page to audit PaymentTransaction with optional live Razorpay status for current page.
    - Filters: created date range, status (multi), Only Razorpay
    - Flags RT presence (annotation)
    - Optional live fetch for Razorpay order + latest payment per row on the current page
    - Suggests actions:
      * Verified + no RT -> Investigate refund
      * Initiated + missing Razorpay IDs + no RT -> Verify manually
      * (With live data) Captured + INR + amount match + no RT -> Process refund
    """
    template_name = "admin_tools/payment_audit.html"

    def get(self, request):
        # Filters
        date_from = request.GET.get("date_from", "")
        date_to   = request.GET.get("date_to", "")
        statuses  = request.GET.getlist("status") or ["initiated", "verified"]
        only_rzp  = request.GET.get("only_razorpay", "1")
        load_rzp  = request.GET.get("load_rzp", "0")  # <-- persisted toggle, default OFF
        page      = int(request.GET.get("page", "1") or 1)
        page_size = int(request.GET.get("page_size", "50") or 50)
        only_rt_refund_pending = request.GET.get("only_rt_refund_pending", "0")
        only_rt_refund_pending_flag = _truthy(only_rt_refund_pending)


        dt_from = _parse_date(date_from) if date_from else None
        dt_to   = _parse_date(date_to, end=True) if date_to else None

        qs = PaymentTransaction.objects.all()
        if dt_from:
            qs = qs.filter(created_at__gte=dt_from)
        if dt_to:
            qs = qs.filter(created_at__lte=dt_to)
        if statuses:
            qs = qs.filter(status__in=statuses)

        # Only Razorpay-linked (has rzp order OR gateway_amount>0)
        if _truthy(only_rzp):
            qs = qs.filter(Q(razorpay_order_id__isnull=False) | Q(gateway_amount__gt=0))

        # Does matching RechargeTransaction exist?
        # rt_subq = RechargeTransaction.objects.filter(order_id=OuterRef("order_id"))
        rt_any = RechargeTransaction.objects.filter(order_id=OuterRef("order_id"))
        # qs = qs.annotate(has_rt=Exists(rt_any))
        qs = qs.annotate(has_rt=Exists(rt_any)).order_by("-created_at")


        # rt_refund_not_processed = (
        #     RechargeTransaction.objects
        #     .filter(order_id=OuterRef("order_id"))
        #     .filter(Q(refund_status__isnull=True) | ~Q(refund_status__iexact="processed"))
        # )

        # qs = qs.annotate(rt_refund_not_processed=Exists(rt_refund_not_processed))

        # if only_rt_refund_pending_flag:
        #     qs = qs.filter(rt_refund_not_processed=True)
        
        rt_refund_not_processed = (
            RechargeTransaction.objects
            .filter(order_id=OuterRef("order_id"))
            # exclude successful recharge rows from this “pending refund” bucket
            .filter(~Q(status__iexact="success"))
            # refund not processed yet (NULL/empty/anything not 'processed')
            .filter(Q(refund_status__isnull=True) | ~Q(refund_status__iexact="processed"))
        )
        qs = qs.annotate(rt_refund_not_processed=Exists(rt_refund_not_processed))

        if only_rt_refund_pending_flag:
            qs = qs.filter(rt_refund_not_processed=True)


        # if only_rt_refund_pending.strip() in {"1","true","on","yes"}:
        #     qs = qs.filter(rt_refund_not_processed=True)


        # qs = qs.annotate(has_rt=Exists(rt_subq)).order_by("-created_at")

        paginator = Paginator(qs, page_size)
        page_obj = paginator.get_page(page)

        # Fetch live RZP for this page only if toggle is on
        fetch_live = _truthy(load_rzp)

        rows = []
        for tx in page_obj.object_list:
            is_verified   = (tx.status == "verified")
            is_initiated  = (tx.status == "initiated")
            has_rzp_oid   = bool(tx.razorpay_order_id)
            has_rzp_pid   = bool(tx.razorpay_payment_id)
            has_rt        = bool(getattr(tx, "has_rt", False))

            rzp = None
            if fetch_live and has_rzp_oid and not has_rt and (is_verified or is_initiated):
                rzp = fetch_razorpay_snapshot(tx.razorpay_order_id)

            # Suggested action (may be upgraded by live info)
            action = None
            action_hint = None
            action_href = None

            if rzp and rzp.get("ok"):
                r_order = rzp.get("order") or {}
                r_pay   = rzp.get("latest_payment") or {}
                order_status = (r_order.get("status") or "").lower()     # 'paid', 'created', ...
                pay_status   = (r_pay.get("status") or "").lower()       # 'captured', 'authorized', ...
                pay_amt      = r_pay.get("amount")
                pay_curr     = r_pay.get("currency")
                expected_amt = int(round(float(tx.amount) * 100))

                if (order_status == "paid" and pay_status == "captured" and
                    pay_curr == "INR" and pay_amt == expected_amt and not has_rt):
                    action = "Process refund"
                    action_hint = "Captured payment without Recharge txn."
                    action_href = "/accounts/tools/refund"  # adjust to your refund tool
            # Fallbacks
            if not action:
                if is_verified and not has_rt:
                    action = "Investigate refund"
                    action_hint = "Verified payment but no Recharge txn found."
                    action_href = "/accounts/tools/sync-rps"
                elif is_initiated and not (has_rzp_oid and has_rzp_pid) and not has_rt:
                    action = "Verify manually"
                    action_hint = "Initiated without Razorpay IDs and no Recharge txn."
                    action_href = "/payments/inspect/"

            rows.append({
                "tx": tx,
                "is_verified": is_verified,
                "is_initiated": is_initiated,
                "has_rzp_oid": has_rzp_oid,
                "has_rzp_pid": has_rzp_pid,
                "has_rt": has_rt,
                "rzp": rzp,  # snapshot dict or None
                "action": action,
                "action_hint": action_hint,
                "action_href": action_href,
            })

        context = {
            "filters": {
                "date_from": date_from,
                "date_to": date_to,
                "statuses": statuses,
                "only_razorpay": _truthy(only_rzp),
                "only_rt_refund_pending": only_rt_refund_pending_flag,   # <-- add this
                "page_size": page_size,
                "load_rzp": _truthy(load_rzp),  # persist toggle back to UI
            },
            "status_options": STATUS_OPTIONS,
            "page_obj": page_obj,
            "rows": rows,
        }
        return render(request, self.template_name, context)
