<?php

namespace App\Console\Commands;

use App\Models\Order;
use App\Services\CapturedPaymentRecoveryService;
use App\Services\InventoryService;
use App\Support\PaymentLogger;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class ReconcileCapturedPaymentOrders extends Command
{
    protected $signature = 'payments:reconcile-captured
                            {--days=14 : Look back this many days}
                            {--order= : Reconcile a single order ID}
                            {--payment-id= : Explicit Razorpay payment ID (e.g. pay_xxx)}';

    protected $description = 'Recover orders marked failed/cancelled when Razorpay payment is already captured';

    public function __construct(
        private readonly CapturedPaymentRecoveryService $capturedPaymentRecovery,
        private readonly InventoryService $inventoryService
    ) {
        parent::__construct();
    }

    public function handle(): int
    {
        $orderId = $this->option('order');
        $explicitPaymentId = (string) ($this->option('payment-id') ?? '');
        $days = max(1, (int) $this->option('days'));
        $graceSeconds = (int) config('services.razorpay.failed_webhook_grace_seconds', 300);

        if ($orderId && $explicitPaymentId !== '') {
            $order = Order::find((int) $orderId);
            if (!$order) {
                $this->error("Order #{$orderId} not found.");
                return self::FAILURE;
            }

            $ok = DB::transaction(fn () => $this->capturedPaymentRecovery->recoverCapturedPayment(
                $order,
                $explicitPaymentId,
                'manual_reconcile'
            ));

            $this->info($ok ? "Recovered order #{$orderId} using {$explicitPaymentId}" : 'Recovery failed (payment not captured at gateway).');

            return $ok ? self::SUCCESS : self::FAILURE;
        }

        $query = Order::query()
            ->where('payment_method', 'online')
            ->whereNotNull('razorpay_payment_id')
            ->where('payment_status', '!=', Order::PAYMENT_PAID)
            ->where('created_at', '>=', now()->subDays($days));

        if ($orderId) {
            $query->whereKey((int) $orderId);
        } else {
            $query->where(function ($builder) {
                $builder->where('payment_status', Order::PAYMENT_FAILED)
                    ->orWhere('status', Order::STATUS_CANCELLED);
            });
        }

        $recovered = 0;
        $released = 0;
        $skipped = 0;

        $query->orderBy('id')->chunkById(50, function ($orders) use (&$recovered, &$released, &$skipped, $graceSeconds) {
            foreach ($orders as $order) {
                $paymentId = $this->capturedPaymentRecovery->resolveCapturedPaymentId($order) ?? '';

                if ($paymentId === '') {
                    $skipped++;
                    continue;
                }

                if ($this->capturedPaymentRecovery->isGatewayPaymentCaptured($paymentId)) {
                    $ok = DB::transaction(function () use ($order, $paymentId) {
                        return $this->capturedPaymentRecovery->recoverCapturedPayment(
                            $order,
                            $paymentId,
                            'scheduled_reconcile'
                        );
                    });

                    if ($ok) {
                        $recovered++;
                        $this->line("Recovered order #{$order->id} ({$paymentId})");
                    } else {
                        $skipped++;
                    }

                    continue;
                }

                if (
                    $order->payment_status === Order::PAYMENT_FAILED
                    && $order->status === Order::STATUS_AWAITING_PAYMENT
                    && $order->updated_at <= now()->subSeconds($graceSeconds)
                ) {
                    DB::transaction(function () use ($order) {
                        $this->inventoryService->releaseByOrderId($order->id, true);
                    });

                    $released++;
                    PaymentLogger::info('Deferred payment failure finalized after grace window', [
                        'order_id' => $order->id,
                        'payment_id' => $paymentId,
                        'grace_seconds' => $graceSeconds,
                    ]);
                    $this->line("Released stock for unpaid order #{$order->id} after grace");
                } else {
                    $skipped++;
                }
            }
        });

        $this->info("Reconcile complete. recovered={$recovered}, released={$released}, skipped={$skipped}");

        return self::SUCCESS;
    }
}
