"""
Backfill card details and the real Stripe fee onto orders that were paid
before those fields existed.

Reads each order's stored Stripe payment intent and copies across only the
non-sensitive details plus the fee from the balance transaction. Nothing is
written unless Stripe returns a charge, so running this twice is safe.

    python manage.py backfill_card_details            # orders missing card or fee
    python manage.py backfill_card_details --dry-run  # report only
    python manage.py backfill_card_details --order 5  # a single order
    python manage.py backfill_card_details --all      # every paid order, refresh both
"""
from django.core.management.base import BaseCommand

from apps.ecommerce.models import Order


class Command(BaseCommand):
    help = 'Copy card details from Stripe onto orders that predate the card fields.'

    def add_arguments(self, parser):
        parser.add_argument('--dry-run', action='store_true',
                            help='Show what would change without saving.')
        parser.add_argument('--order', type=int, default=None,
                            help='Backfill a single order by primary key.')
        parser.add_argument('--all', action='store_true',
                            help='Re-read every order with a payment intent, not just incomplete ones.')

    def handle(self, *args, **options):
        import stripe
        from decimal import Decimal
        from django.db.models import Q
        from apps.ecommerce.stripe_utils import _get_stripe_secret_key

        dry_run = options['dry_run']
        qs = Order.objects.exclude(stripe_payment_intent_id='')
        if options['order']:
            qs = qs.filter(pk=options['order'])
        elif not options['all']:
            qs = qs.filter(Q(card_last4='') | Q(stripe_fee=0))

        total = qs.count()
        if not total:
            self.stdout.write('Nothing to do. No orders are missing card details.')
            return

        self.stdout.write(f'Checking {total} order(s) against Stripe.')
        updated = skipped = failed = 0

        for order in qs.iterator():
            try:
                stripe.api_key = _get_stripe_secret_key(getattr(order, 'tenant', None))
                intent = stripe.PaymentIntent.retrieve(
                    order.stripe_payment_intent_id,
                    expand=['latest_charge.balance_transaction'],
                )
                charge = intent.get('latest_charge')
                if not charge:
                    skipped += 1
                    self.stdout.write(f'  {order.display_number}: no charge on the intent, skipped.')
                    continue

                order.apply_card_details(charge)

                fee = None
                bt = charge.get('balance_transaction')
                if bt and getattr(bt, 'get', None) and bt.get('fee'):
                    fee = Decimal(str(bt['fee'])) / 100
                    order.stripe_fee = fee

                if not order.card_last4 and fee is None:
                    skipped += 1
                    self.stdout.write(f'  {order.display_number}: nothing on the charge to copy, skipped.')
                    continue

                bits = []
                if order.card_last4:
                    bits.append(f'{order.card_brand} ending {order.card_last4}')
                if fee is not None:
                    bits.append(f'fee GBP {fee}')
                label = ', '.join(bits)

                if dry_run:
                    self.stdout.write(f'  {order.display_number}: would set {label}.')
                else:
                    order.save(update_fields=[
                        'card_brand', 'card_last4', 'cardholder_name',
                        'card_country', 'card_postcode', 'stripe_fee',
                    ])
                    self.stdout.write(f'  {order.display_number}: set {label}.')
                updated += 1

            except Exception as exc:
                failed += 1
                self.stdout.write(self.style.WARNING(
                    f'  {order.display_number}: {exc.__class__.__name__}: {exc}'
                ))

        verb = 'would update' if dry_run else 'updated'
        self.stdout.write(self.style.SUCCESS(
            f'Done. {verb} {updated}, skipped {skipped}, failed {failed}.'
        ))
