# payments/management/commands/find_users_with_duplicate_wallet_credits.py
from __future__ import annotations

from datetime import datetime, date, time, timedelta
from typing import Optional

from django.core.management.base import BaseCommand, CommandError
from django.db.models import Count
from django.utils import timezone

try:
    from zoneinfo import ZoneInfo  # Python 3.9+
except Exception:  # pragma: no cover
    ZoneInfo = None  # type: ignore


def local_day_window_utc(target_date: date, tz) -> tuple[datetime, datetime]:
    start_local = datetime.combine(target_date, time.min).replace(tzinfo=tz)
    end_local = start_local + timedelta(days=1)
    return (start_local.astimezone(timezone.utc), end_local.astimezone(timezone.utc))


class Command(BaseCommand):
    help = (
        "Find users whose wallet has SAME-DAY duplicate CREDIT usages.\n"
        "Duplicates = same local DATE + transaction_type='credit' + amount_used + purpose + purpose_note (order_id ignored).\n"
        "Prints mobile numbers and duplicate groups per day. Use --csv to export."
    )

    def add_arguments(self, parser):
        parser.add_argument(
            "--date",
            dest="on_date",
            help="Target local date YYYY-MM-DD. (Use either --date OR a range with --from/--to)",
        )
        parser.add_argument("--from", dest="date_from", help="Start local date YYYY-MM-DD (inclusive)")
        parser.add_argument("--to", dest="date_to", help="End local date YYYY-MM-DD (inclusive)")
        parser.add_argument(
            "--tz",
            dest="tzname",
            default=None,
            help="IANA timezone for local day (e.g. Asia/Kolkata). Defaults to Django's current timezone.",
        )
        parser.add_argument(
            "--min",
            dest="min_count",
            type=int,
            default=2,
            help="Minimum duplicates threshold per group (default: 2)",
        )
        parser.add_argument(
            "--csv",
            dest="csv_path",
            help="Optional CSV output path",
        )

    def handle(self, *args, **opts):
        from payments.models import ViralPeWalletUsage  # local import
        from accounts.models import User

        on_date = opts.get("on_date")
        date_from = opts.get("date_from")
        date_to = opts.get("date_to")
        min_count: int = opts.get("min_count") or 2
        csv_path: Optional[str] = opts.get("csv_path")

        # Resolve timezone
        tzname: Optional[str] = opts.get("tzname")
        if tzname:
            if ZoneInfo is None:
                raise CommandError("zoneinfo not available; remove --tz or upgrade Python (3.9+).")
            tz = ZoneInfo(tzname)  # type: ignore[arg-type]
        else:
            tz = timezone.get_current_timezone()

        # Parse dates
        if on_date and (date_from or date_to):
            raise CommandError("Use either --date OR (--from and --to), not both.")
        if on_date:
            try:
                d = datetime.strptime(on_date, "%Y-%m-%d").date()
            except ValueError:
                raise CommandError("Invalid --date. Use YYYY-MM-DD.")
            days = [d]
        else:
            if not date_from or not date_to:
                raise CommandError("Provide both --from and --to for a range (YYYY-MM-DD).")
            try:
                d1 = datetime.strptime(date_from, "%Y-%m-%d").date()
                d2 = datetime.strptime(date_to, "%Y-%m-%d").date()
            except ValueError:
                raise CommandError("Invalid --from/--to. Use YYYY-MM-DD.")
            if d2 < d1:
                raise CommandError("--to cannot be earlier than --from.")
            # build inclusive date list
            days = []
            cur = d1
            while cur <= d2:
                days.append(cur)
                cur += timedelta(days=1)

        # CSV init
        writer = None
        f = None
        if csv_path:
            import csv
            f = open(csv_path, "w", newline="", encoding="utf-8")
            writer = csv.writer(f)
            writer.writerow([
                "local_date",
                "mobile_number",
                "user_id",
                "amount_used",
                "purpose",
                "purpose_note",
                "records",
                "sample_ids",
            ])

        total_groups = 0
        users_with_dupes = set()

        self.stdout.write(
            f"Scanning {len(days)} day(s) in timezone {tz} with duplicate threshold >= {min_count}\n"
        )

        for target_date in days:
            start_utc, end_utc = local_day_window_utc(target_date, tz)

            # Constrain to the local day's UTC window
            base_qs = ViralPeWalletUsage.objects.filter(
                transaction_type="credit",
                used_on__gte=start_utc,
                used_on__lt=end_utc,
            )

            # Group by user + duplicate keys (order_id intentionally ignored)
            dup_groups = (
                base_qs.values(
                    "user_id",
                    "user__mobile_number",
                    "amount_used",
                    "purpose",
                    "purpose_note",
                )
                .annotate(n=Count("id"))
                .filter(n__gte=min_count)
            )

            day_groups = dup_groups.count()
            if day_groups == 0:
                continue

            self.stdout.write(f"Date {target_date}: found {day_groups} duplicate group(s)")
            total_groups += day_groups

            # For each group, print details + sample IDs
            for g in dup_groups:
                uid = g["user_id"]
                mobile = g["user__mobile_number"]
                amount = g["amount_used"]
                purpose = g["purpose"]
                pnote = g["purpose_note"]
                n = g["n"]

                # fetch a few sample IDs for debugging/verification
                sample_ids = list(
                    base_qs.filter(
                        user_id=uid,
                        amount_used=amount,
                        purpose=purpose,
                        purpose_note=pnote,
                    )
                    .order_by("used_on", "id")
                    .values_list("id", flat=True)[:10]
                )

                users_with_dupes.add((uid, mobile))
                msg = (
                    f"  {mobile} (user_id={uid}) -> x{n}  "
                    f"amount={amount}  purpose={purpose!r}  note={pnote!r}  sample_ids={sample_ids}"
                )
                self.stdout.write(msg)

                if writer:
                    writer.writerow([
                        target_date,
                        mobile,
                        uid,
                        amount,
                        purpose,
                        pnote,
                        n,
                        " ".join(map(str, sample_ids)),
                    ])

        # Summary
        self.stdout.write("\n=== Summary ===")
        self.stdout.write(f"Total duplicate groups: {total_groups}")
        self.stdout.write(f"Unique users with duplicates: {len(users_with_dupes)}")
        if users_with_dupes:
            mobiles = sorted({m for (_uid, m) in users_with_dupes})
            self.stdout.write("Mobiles: " + ", ".join(mobiles))

        if writer:
            f.close()
            self.stdout.write(f"\nCSV written to: {csv_path}")


# Examples

# Single date (Oct 24 IST):

# python manage.py find_users_with_duplicate_wallet_credits --date 2025-10-24 --tz Asia/Kolkata


# Date range (the last 7 days):

# python manage.py find_users_with_duplicate_wallet_credits --from 2025-10-18 --to 2025-10-24 --tz Asia/Kolkata


# Require 3+ duplicates per group (stricter):

# python manage.py find_users_with_duplicate_wallet_credits --date 2025-10-24 --min 3 --tz Asia/Kolkata


# Export to CSV:

# python manage.py find_users_with_duplicate_wallet_credits --date 2025-10-24 --tz Asia/Kolkata --csv /tmp/dupe_cred