"""
Ask Stripe what was paid, rather than waiting to be told.

On 22 August 2026 a live payment went through, Stripe took the money, and the
order sat at pending. Both of the paths that record a payment had failed:

  The return page. Confirms only if the customer comes back. Close the tab,
  lose signal, pay on a phone that switches to standby, and it never runs.

  The webhook. Confirms only if Stripe can reach the endpoint and the
  signature verifies. That day the signing secret did not match, so every
  delivery was rejected and Stripe was still retrying hours later.

Both are best-effort. Neither retries forever. So this is a third path that
depends on nothing arriving: it walks unpaid records that have a Stripe
session, asks Stripe directly, and records whatever Stripe says.

It is safe to run as often as you like. Nothing here decides anything: Stripe
is the only source of truth, and an already-paid record is skipped.

Cron, every fifteen minutes:
    */15 * * * * cd /home/<user>/public_html/backend && \\
      venv/bin/python manage.py reconcile_payments --settings=config.settings.production \\
      >> /var/log/ft-reconcile.log 2>&1
"""
from datetime import timedelta

from django.core.management.base import BaseCommand
from django.utils import timezone


class Command(BaseCommand):
    help = 'Mark orders and renewal invoices paid when Stripe says they were paid.'

    def add_arguments(self, parser):
        parser.add_argument(
            '--days', type=int, default=14,
            help='How far back to look. Older sessions expire at Stripe anyway.',
        )
        parser.add_argument(
            '--dry-run', action='store_true',
            help='Report what would change and write nothing.',
        )

    def handle(self, *args, **options):
        import stripe
        from decimal import Decimal

        from apps.ecommerce.models import Order
        from apps.ecommerce.stripe_utils import _get_stripe_secret_key
        from apps.ftlink.models import JobRun
        from apps.tenants.models import TenantInvoice

        dry = options['dry_run']
        since = timezone.now() - timedelta(days=options['days'])
        fixed = checked = failed = 0

        # ── Orders ───────────────────────────────────────────────────────────
        orders = (Order.objects
                  .filter(created_at__gte=since)
                  .exclude(stripe_session_id='')
                  .exclude(stripe_session_id__isnull=True)
                  .exclude(status__in=[Order.STATUS_PAID,
                                       Order.STATUS_COMPLETED,
                                       Order.STATUS_CANCELLED])
                  .order_by('pk'))

        for order in orders:
            checked += 1
            try:
                stripe.api_key = _get_stripe_secret_key(getattr(order, 'tenant', None))
                session = stripe.checkout.Session.retrieve(order.stripe_session_id)
            except Exception as exc:
                failed += 1
                self.stderr.write(f'{order.order_number}: could not ask Stripe: {exc}')
                continue

            if session.get('payment_status') != 'paid':
                continue

            amount = Decimal(str((session.get('amount_total') or 0) / 100))
            if dry:
                self.stdout.write(
                    f'[dry run] {order.order_number}: Stripe says paid £{amount}, '
                    f'record says {order.status}'
                )
                fixed += 1
                continue

            recorded = order.mark_paid(
                amount=amount,
                payment_intent_id=session.get('payment_intent') or '')
            if not recorded:
                # Another path got there first. Nothing to record, nothing to
                # send, and nothing to report as reconciled.
                continue
            fixed += 1
            self.stdout.write(self.style.SUCCESS(
                f'{order.order_number}: marked paid, £{amount}'
            ))

            # The emails belong to the payment, not to the path that noticed
            # it. A customer who paid and heard nothing has no way of knowing
            # the order exists.
            try:
                from apps.ecommerce.email_utils import (
                    send_order_confirmation, send_order_admin_notification,
                )
                send_order_confirmation(order)
                send_order_admin_notification(order)
            except Exception as exc:
                self.stderr.write(f'{order.order_number}: paid, but email failed: {exc}')

        # ── Renewal invoices ─────────────────────────────────────────────────
        # These have only ever had one confirmation path, the return page, so
        # they need this more than orders do.
        invoices = (TenantInvoice.objects
                    .filter(status=TenantInvoice.STATUS_PENDING)
                    .exclude(stripe_session_id='')
                    .exclude(stripe_session_id__isnull=True)
                    .order_by('pk'))

        for invoice in invoices:
            checked += 1
            try:
                stripe.api_key = _get_stripe_secret_key(invoice.tenant)
                session = stripe.checkout.Session.retrieve(invoice.stripe_session_id)
            except Exception as exc:
                failed += 1
                self.stderr.write(f'{invoice.invoice_number}: could not ask Stripe: {exc}')
                continue

            if session.get('payment_status') != 'paid':
                continue

            if dry:
                self.stdout.write(
                    f'[dry run] {invoice.invoice_number}: Stripe says paid, '
                    f'record says {invoice.status}'
                )
                fixed += 1
                continue

            from apps.tenants.renewal_views import mark_invoice_paid
            mark_invoice_paid(invoice)
            fixed += 1
            self.stdout.write(self.style.SUCCESS(
                f'{invoice.invoice_number}: marked paid, renewal moved to '
                f'{invoice.tenant.next_renewal_date}'
            ))

        summary = f'{checked} checked, {fixed} reconciled, {failed} unreachable'
        self.stdout.write(self.style.SUCCESS(f'Done. {summary}'))

        if not dry:
            # A heartbeat, so System Info can tell the difference between
            # "nothing needed fixing" and "this has not run since Tuesday".
            JobRun.record('reconcile_payments', ok=(failed == 0), note=summary)
