"""
Audit local refund statuses against live Digistore purchase data.

Usage:
  python manage.py audit_refund_mismatches
  python manage.py audit_refund_mismatches --digistore-order-id MNXVZRXL
  python manage.py audit_refund_mismatches --retry-full-force
  python manage.py audit_refund_mismatches --retry-full-force --dry-run
"""
from decimal import Decimal, InvalidOperation

from django.core.management.base import BaseCommand

from orders.models import Order
from orders.services import OrderService
from products.services import DigistoreService
from webhooks.models import ShopifyRefundEvent


class Command(BaseCommand):
    help = (
        "Compare local refunded/partially refunded orders with Digistore live purchase "
        "transactions and optionally retry force refund for full-refund mismatches."
    )

    def add_arguments(self, parser):
        parser.add_argument(
            "--digistore-order-id",
            type=str,
            default="",
            help="Audit only one Digistore order id.",
        )
        parser.add_argument(
            "--limit",
            type=int,
            default=0,
            help="Limit number of local orders to audit (0 means all).",
        )
        parser.add_argument(
            "--retry-full-force",
            action="store_true",
            help="Retry full-refund mismatches via force=true.",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Preview retries without calling Digistore refund API.",
        )
        parser.add_argument(
            "--reason",
            type=str,
            default="Audit mismatch force retry",
            help="Reason sent when --retry-full-force is enabled.",
        )
        parser.add_argument(
            "--request-date",
            type=str,
            default="",
            help="Optional Digistore request_date for force retries.",
        )

    @staticmethod
    def _to_decimal(value) -> Decimal:
        try:
            return Decimal(str(value or "0").strip().replace(",", "."))
        except (InvalidOperation, ValueError, TypeError, AttributeError):
            return Decimal("0")

    @staticmethod
    def _evaluate_refund_result(res: dict) -> tuple[bool, str]:
        if not isinstance(res, dict):
            return False, "invalid_response"
        result = str(res.get("result") or "").strip().lower()
        data = res.get("data") if isinstance(res.get("data"), dict) else {}
        status = str(data.get("status") or "").strip().lower()
        if result != "success":
            return False, f"result={result or 'missing'}"
        if status and status != "success":
            msg = str(data.get("status_msg") or "").strip()
            return False, f"status={status}; status_msg={msg}"
        details = data.get("details")
        if isinstance(details, list):
            bad = []
            for d in details:
                if not isinstance(d, dict):
                    continue
                d_status = str(d.get("status") or "").strip().lower()
                if d_status in ("refused", "error", "failure"):
                    code = str(d.get("code") or "").strip()
                    msg = str(d.get("result_msg") or "").strip()
                    bad.append(f"{d_status}:{code}:{msg}".strip(":"))
            if bad:
                return False, "; ".join(bad)
        return True, ""

    @staticmethod
    def _mark_latest_refund_event_failed(digistore_order_id: str, reason: str) -> None:
        event = (
            ShopifyRefundEvent.objects.filter(digistore_order_id=digistore_order_id)
            .order_by("-created_at")
            .first()
        )
        if not event:
            return
        event.processed = False
        event.error = f"manual_retry_failed: {reason}"[:4000]
        event.save(update_fields=["processed", "error", "updated_at"])

    def handle(self, *args, **options):
        oid_filter = str(options.get("digistore_order_id") or "").strip()
        limit = max(0, int(options.get("limit") or 0))
        retry_full_force = bool(options.get("retry_full_force"))
        dry_run = bool(options.get("dry_run"))
        reason = str(options.get("reason") or "").strip()
        request_date = str(options.get("request_date") or "").strip()

        qs = Order.objects.filter(
            status__in=["refunded", "partially refunded"],
        ).exclude(digistore_order_id="")
        if oid_filter:
            qs = qs.filter(digistore_order_id=oid_filter)
        qs = qs.order_by("-updated_at")
        if limit > 0:
            qs = qs[:limit]

        checked = 0
        mismatches: list[dict] = []
        retries_attempted = 0
        retries_succeeded = 0
        retries_failed = 0

        for order in qs:
            checked += 1
            oid = str(order.digistore_order_id or "").strip()
            try:
                purchase = DigistoreService.get_purchase(oid, log=False)
            except Exception as exc:
                mismatches.append(
                    {
                        "digistore_order_id": oid,
                        "shopify_order_id": order.shopify_order_id or "",
                        "local_status": order.status,
                        "reason": f"get_purchase_error:{exc}",
                    }
                )
                continue

            txs = purchase.get("transaction_list") if isinstance(purchase, dict) else []
            if not isinstance(txs, list):
                txs = []

            has_refund_tx = False
            refunded_total = Decimal("0")
            for tx in txs:
                if not isinstance(tx, dict):
                    continue
                tx_type = str(tx.get("type") or "").strip().lower()
                if tx_type in ("refund", "chargeback"):
                    has_refund_tx = True
                    # Digistore refund transactions are often negative amounts.
                    # Use absolute values to represent refunded volume.
                    refunded_total += abs(self._to_decimal(tx.get("amount")))

            mismatch_reason = ""
            if order.status == "refunded" and not has_refund_tx:
                mismatch_reason = "no_refund_transaction_in_digistore"
            elif order.status == "partially refunded" and refunded_total <= 0:
                mismatch_reason = "no_partial_refund_amount_in_digistore"

            if not mismatch_reason:
                continue

            row = {
                "digistore_order_id": oid,
                "shopify_order_id": order.shopify_order_id or "",
                "local_status": order.status,
                "is_refundable_now": purchase.get("is_refundable_now"),
                "last_refund_possible_at": purchase.get("last_refund_possible_at"),
                "reason": mismatch_reason,
            }
            mismatches.append(row)

            should_retry = (
                retry_full_force
                and order.status == "refunded"
                and mismatch_reason == "no_refund_transaction_in_digistore"
            )
            if not should_retry:
                continue

            retries_attempted += 1
            if dry_run:
                row["retry"] = "dry_run"
                continue

            try:
                res = OrderService.refund_purchase(
                    oid,
                    reason=reason,
                    force=True,
                    request_date=request_date,
                )
                ok, retry_reason = self._evaluate_refund_result(res)
                if ok:
                    retries_succeeded += 1
                    row["retry"] = "sent"
                    row["retry_response"] = str(res)[:500]
                else:
                    retries_failed += 1
                    row["retry"] = "failed_response"
                    row["retry_response"] = str(res)[:500]
                    self._mark_latest_refund_event_failed(oid, retry_reason)
            except Exception as exc:
                retries_failed += 1
                row["retry"] = "exception"
                row["retry_response"] = str(exc)[:500]
                self._mark_latest_refund_event_failed(oid, str(exc))

        self.stdout.write(f"Orders checked: {checked}")
        self.stdout.write(f"Mismatches found: {len(mismatches)}")
        if retry_full_force:
            self.stdout.write(
                f"Retries attempted: {retries_attempted} | "
                f"succeeded: {retries_succeeded} | failed: {retries_failed}"
            )

        for m in mismatches:
            self.stdout.write(
                f"{m.get('digistore_order_id')} | "
                f"shopify={m.get('shopify_order_id') or '-'} | "
                f"status={m.get('local_status')} | "
                f"reason={m.get('reason')} | "
                f"is_refundable_now={m.get('is_refundable_now')} | "
                f"last_refund_possible_at={m.get('last_refund_possible_at')} | "
                f"retry={m.get('retry', '-')}"
            )
