<?php

namespace App\Jobs;

use App\Models\PlatformOrder;
use App\Support\SubscriptionActivationService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class BatchMarkPaidAndActivateJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /** @var int[] */
    public array $orderIds;

    public int $adminId;

    public string $scope;

    public string $filterSummary;

    public int $limit;

    public int $matchedTotal;

    public int $processed;

    public string $runId;

    /**
     * @param  int[]  $orderIds
     */
    public function __construct(
        array $orderIds,
        int $adminId,
        string $scope,
        string $filterSummary,
        int $limit,
        int $matchedTotal,
        int $processed,
        string $runId,
    ) {
        $this->orderIds = array_values(array_map('intval', $orderIds));
        $this->adminId = $adminId;
        $this->scope = $scope;
        $this->filterSummary = $filterSummary;
        $this->limit = $limit;
        $this->matchedTotal = $matchedTotal;
        $this->processed = $processed;
        $this->runId = $runId;
    }

    public function handle(SubscriptionActivationService $service): void
    {
        $success = 0;
        $failed = 0;
        $failedReasonCounts = [];

        $now = now();
        $nowStr = $now->toDateTimeString();

        foreach ($this->orderIds as $orderId) {
            /** @var PlatformOrder|null $order */
            $order = PlatformOrder::query()->find($orderId);
            if (! $order) {
                continue;
            }

            try {
                // 双保险：仅推进 pending+unpaid
                if ((string) $order->status !== 'pending' || (string) $order->payment_status !== 'unpaid') {
                    throw new \InvalidArgumentException('订单不是待处理+未支付，不允许批量 BMPA。');
                }

                // 治理优先：续费单必须绑定订阅
                if ((string) ($order->order_type ?? '') === 'renewal' && ! (int) ($order->site_subscription_id ?? 0)) {
                    throw new \InvalidArgumentException('续费单未绑定订阅（site_subscription_id 为空），不允许批量标记支付并生效。');
                }

                // 治理优先：若该订单已有退款轨迹，则不允许推进
                if ((float) $order->refundTotal() > 0) {
                    throw new \InvalidArgumentException('订单存在退款轨迹，不允许批量标记支付并生效，请先完成退款治理。');
                }

                // 治理优先：若该订单已有回执证据，但回执总额与应付金额不一致，则不允许推进
                $receiptTotal = (float) $order->receiptTotal();
                $hasReceiptEvidence = (data_get($order->meta, 'payment_summary.total_amount') !== null)
                    || (data_get($order->meta, 'payment_receipts.0.amount') !== null);

                if ($hasReceiptEvidence) {
                    $expectedPaid = (float) ($order->payable_amount ?? 0);
                    $receiptCents = (int) round($receiptTotal * 100);
                    $expectedCents = (int) round($expectedPaid * 100);

                    $tol = (float) config('saasshop.amounts.tolerance', 0.01);
                    $tolCents = (int) round($tol * 100);
                    $tolCents = max(1, $tolCents);

                    if (abs($receiptCents - $expectedCents) >= $tolCents) {
                        throw new \InvalidArgumentException('订单回执总额与应付金额不一致，不允许批量推进，请先修正回执/金额后再处理。');
                    }
                }

                // 最小状态推进：标记为已支付 + 已生效，并补齐时间与金额字段
                $order->payment_status = 'paid';
                $order->status = 'activated';
                $order->paid_at = $order->paid_at ?: $now;
                $order->activated_at = $order->activated_at ?: $now;
                $order->paid_amount = (float) (($order->payable_amount ?? 0) > 0 ? $order->payable_amount : ($order->paid_amount ?? 0));

                // 若尚无回执数组，则补一条（可治理留痕，便于后续对账）
                $meta = (array) ($order->meta ?? []);
                $receipts = (array) (data_get($meta, 'payment_receipts', []) ?? []);
                if (count($receipts) === 0) {
                    $receipts[] = [
                        'type' => 'batch_mark_paid_and_activate',
                        'channel' => (string) ($order->payment_channel ?? ''),
                        'amount' => (float) ($order->paid_amount ?? 0),
                        'paid_at' => $order->paid_at ? $order->paid_at->format('Y-m-d H:i:s') : $nowStr,
                        'note' => '由【批量标记支付并生效】自动补记（可治理）',
                        'created_at' => $nowStr,
                        'admin_id' => $this->adminId,
                    ];
                    data_set($meta, 'payment_receipts', $receipts);

                    $totalPaid = 0.0;
                    foreach ($receipts as $r) {
                        $totalPaid += (float) (data_get($r, 'amount') ?? 0);
                    }
                    $latest = count($receipts) > 0 ? end($receipts) : null;
                    data_set($meta, 'payment_summary', [
                        'count' => count($receipts),
                        'total_amount' => $totalPaid,
                        'last_at' => (string) (data_get($latest, 'paid_at') ?? ''),
                        'last_amount' => (float) (data_get($latest, 'amount') ?? 0),
                        'last_channel' => (string) (data_get($latest, 'channel') ?? ''),
                    ]);
                }

                // 清理历史错误
                data_forget($meta, 'subscription_activation_error');
                data_forget($meta, 'batch_mark_paid_and_activate_error');

                // 便于追踪：记录最近一次批量推进信息（扁平字段）
                data_set($meta, 'batch_mark_paid_and_activate', [
                    'at' => $nowStr,
                    'admin_id' => $this->adminId,
                    'scope' => $this->scope,
                    'mode' => 'queue',
                    'run_id' => $this->runId,
                ]);

                $order->meta = $meta;
                $order->save();

                // 同步订阅
                $subscription = $service->activateOrder($order->id, $this->adminId);

                // 审计：记录批量推进（包含订阅同步）
                $order->refresh();
                $meta2 = (array) ($order->meta ?? []);
                $audit = (array) (data_get($meta2, 'audit', []) ?? []);
                $audit[] = [
                    'action' => 'batch_mark_paid_and_activate',
                    'scope' => $this->scope,
                    'at' => $nowStr,
                    'admin_id' => $this->adminId,
                    'subscription_id' => $subscription->id,
                    'filters' => $this->filterSummary,
                    'run_id' => $this->runId,
                    'note' => '批量标记支付并生效（queue, run_id=' . $this->runId . ', limit=' . $this->limit . ', matched=' . $this->matchedTotal . ', processed=' . $this->processed . ')',
                ];
                data_set($meta2, 'audit', $audit);

                // 再次写入扁平字段（activateOrder 内会更新 meta，避免被覆盖）
                $bmpa2 = (array) (data_get($meta2, 'batch_mark_paid_and_activate', []) ?? []);
                $bmpa2 = array_merge($bmpa2, [
                    'at' => $nowStr,
                    'admin_id' => $this->adminId,
                    'scope' => $this->scope,
                    'mode' => 'queue',
                    'run_id' => $this->runId,
                ]);
                data_set($meta2, 'batch_mark_paid_and_activate', $bmpa2);

                $order->meta = $meta2;
                $order->save();

                $success++;
            } catch (\Throwable $e) {
                $failed++;

                $reason = trim((string) $e->getMessage());
                $reason = $reason !== '' ? $reason : '未知错误';
                $failedReasonCounts[$reason] = ($failedReasonCounts[$reason] ?? 0) + 1;

                $meta = (array) ($order->meta ?? []);

                data_set($meta, 'batch_mark_paid_and_activate_error', [
                    'message' => $reason,
                    'at' => $nowStr,
                    'admin_id' => $this->adminId,
                    'scope' => $this->scope,
                    'run_id' => $this->runId,
                    'filters' => $this->filterSummary,
                ]);

                // 即使失败也写入 batch_mark_paid_and_activate（包含 run_id），确保本批次可追溯。
                data_set($meta, 'batch_mark_paid_and_activate', [
                    'at' => $nowStr,
                    'admin_id' => $this->adminId,
                    'scope' => $this->scope,
                    'mode' => 'queue',
                    'run_id' => $this->runId,
                ]);

                $order->meta = $meta;
                $order->save();
            }
        }

        // 最小结果汇总（写入到每个订单的 meta.batch_mark_paid_and_activate.last_result），便于运营在列表页直接看到“本次队列批量 BMPA 的执行结果”。
        $topReasons = [];
        if ($failed > 0 && count($failedReasonCounts) > 0) {
            arsort($failedReasonCounts);
            $top = array_slice($failedReasonCounts, 0, 3, true);
            foreach ($top as $reason => $cnt) {
                $topReasons[] = [
                    'reason' => mb_substr((string) $reason, 0, 80),
                    'count' => (int) $cnt,
                ];
            }
        }

        $summary = [
            'run_id' => $this->runId,
            'success' => $success,
            'failed' => $failed,
            'matched' => (int) $this->matchedTotal,
            'processed' => (int) $this->processed,
            'top_reasons' => $topReasons,
            'at' => now()->toDateTimeString(),
        ];

        foreach ($this->orderIds as $orderId) {
            $order = PlatformOrder::query()->find($orderId);
            if (! $order) {
                continue;
            }

            $meta = (array) ($order->meta ?? []);
            $bmpa = (array) (data_get($meta, 'batch_mark_paid_and_activate', []) ?? []);

            // 仅当 run_id 与当前 job 一致时才回写 last_result，避免并发覆盖。
            if ((string) (data_get($bmpa, 'run_id') ?? '') !== $this->runId) {
                continue;
            }

            data_set($bmpa, 'last_result', $summary);
            data_set($meta, 'batch_mark_paid_and_activate', $bmpa);

            $order->meta = $meta;
            $order->save();
        }
    }
}
