from decimal import Decimal
from uuid import UUID

from django.core.management.base import BaseCommand
from django.db import OperationalError, connection
from django.db.models import Q

from apps.business.distributor.models import DistributorID
from apps.business.distributor.services.distributor_service import process_direct_incentive
from apps.business.pool.services.pool_service import create_pool_entry
from apps.business.schemes.services.distribution_service import calculate_distribution, resolve_distribution_base_amount
from apps.business.wallet.models import WalletLedger


class Command(BaseCommand):
    help = "Backfill seeded distributor codes and apply opportunity_bundle distribution (direct incentive + pool)"

    def add_arguments(self, parser):
        parser.add_argument(
            "--anchor-mobiles",
            nargs="+",
            default=["8668192080", "8096248999"],
            help="Anchor mobiles to include in seeded target set",
        )
        parser.add_argument("--batch-size", type=int, default=1000)
        parser.add_argument("--limit", type=int, default=0, help="Optional limit for testing")
        parser.add_argument("--skip-code-fix", action="store_true", help="Skip distributor code normalization")
        parser.add_argument("--skip-distribution", action="store_true", help="Skip distribution backfill")

    def handle(self, *args, **options):
        anchor_mobiles = options["anchor_mobiles"]
        batch_size = options["batch_size"]
        limit = options["limit"]
        skip_code_fix = options["skip_code_fix"]
        skip_distribution = options["skip_distribution"]

        qs = (
            DistributorID.objects.select_related("user", "product__opportunity_bundle", "sponsor_distributor__user")
            .filter(
                Q(user__email__endswith="@truewave.test") |
                Q(user__mobile__in=anchor_mobiles)
            )
            .order_by("global_position")
        )
        if limit and limit > 0:
            qs = qs[:limit]

        total = qs.count()
        self.stdout.write(self.style.NOTICE(f"Target distributors: {total}"))

        fixed_codes = 0
        pending_code_updates = []
        processed_distribution = 0
        direct_credited = 0
        pool_created = 0
        skipped_distribution = 0
        config_mismatches = 0
        errors = 0
        product_distribution_cache = {}

        # Help SQLite wait instead of failing immediately under concurrent access.
        with connection.cursor() as cursor:
            cursor.execute("PRAGMA busy_timeout = 10000")

        for idx, dist in enumerate(qs.iterator(chunk_size=batch_size), start=1):
            try:
                if not skip_code_fix:
                    new_code = self._build_distributor_code(dist.user, dist.product, dist.global_position)
                    if dist.distributor_code != new_code:
                        dist.distributor_code = new_code
                        pending_code_updates.append(dist)

                if not skip_code_fix and len(pending_code_updates) >= batch_size:
                    fixed_codes += self._flush_code_updates(pending_code_updates)
                    pending_code_updates = []

                if not skip_distribution:
                    if not dist.product or not dist.product.opportunity_bundle:
                        skipped_distribution += 1
                    else:
                        amount = resolve_distribution_base_amount(dist.product, dist.product.base_cost)
                        if amount <= 0:
                            skipped_distribution += 1
                        else:
                            cache_key = (dist.product_id, str(amount))
                            if cache_key in product_distribution_cache:
                                distribution_result = product_distribution_cache[cache_key]
                            else:
                                try:
                                    distribution_result = calculate_distribution(dist.product, amount)
                                    product_distribution_cache[cache_key] = distribution_result
                                except Exception:
                                    config_mismatches += 1
                                    skipped_distribution += 1
                                    continue
                            processed_distribution += 1

                            # Pool component backfill
                            pool_entry = create_pool_entry(dist, distribution_result)
                            if pool_entry:
                                pool_created += 1

                            # Direct incentive backfill (only when sponsor exists)
                            if dist.sponsor_distributor_id:
                                ref = f"DIRECT_INCENTIVE_{dist.id}"
                                if WalletLedger.objects.filter(
                                    reference_id=ref,
                                    source_type=WalletLedger.SourceType.DIRECT_INCENTIVE,
                                ).exists():
                                    pass
                                else:
                                    result = process_direct_incentive(dist, distribution_result, reference_id=None)
                                    if result.get("success"):
                                        direct_credited += 1
                            else:
                                skipped_distribution += 1

                if idx % 5000 == 0:
                    self.stdout.write(
                        f"Progress {idx}/{total} | code_fixed={fixed_codes} dist_processed={processed_distribution} direct={direct_credited} pool={pool_created} skipped={skipped_distribution} errors={errors}"
                    )

            except Exception as exc:
                errors += 1
                if errors <= 20:
                    self.stdout.write(self.style.WARNING(f"Distributor {dist.id} failed: {exc}"))

        if pending_code_updates:
            fixed_codes += self._flush_code_updates(pending_code_updates)

        self.stdout.write(self.style.SUCCESS("Backfill completed."))
        self.stdout.write(
            self.style.SUCCESS(
                f"Summary: total={total}, code_fixed={fixed_codes}, dist_processed={processed_distribution}, direct_credited={direct_credited}, pool_created={pool_created}, skipped={skipped_distribution}, config_mismatches={config_mismatches}, errors={errors}"
            )
        )

    def _flush_code_updates(self, rows):
        for _ in range(3):
            try:
                DistributorID.objects.bulk_update(rows, ["distributor_code"], batch_size=1000)
                return len(rows)
            except OperationalError:
                continue
        fixed = 0
        for row in rows:
            for _ in range(3):
                try:
                    DistributorID.objects.filter(pk=row.pk).update(distributor_code=row.distributor_code)
                    fixed += 1
                    break
                except OperationalError:
                    continue
        return fixed

    def _id_mod_100(self, value):
        if value is None:
            return 0
        if isinstance(value, UUID):
            return value.int % 100
        try:
            return int(value) % 100
        except Exception:
            text = str(value).replace("-", "")
            try:
                return int(text[:8], 16) % 100
            except Exception:
                return 0

    def _build_distributor_code(self, user, product, global_position):
        scheme_hint = self._id_mod_100(getattr(product, "opportunity_bundle_id", None))
        product_hint = self._id_mod_100(getattr(product, "id", None))
        position_part = int(global_position) % 100000000
        mobile = (getattr(user, "mobile", "") or "")
        mobile_suffix = mobile[-2:] if len(mobile) >= 2 else "00"
        return f"TW{scheme_hint:02d}{product_hint:02d}{position_part:08d}{mobile_suffix}"
