<?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 BatchActivateSubscriptionsJob 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;

        // 说明：优先使用投递时生成的 run_id（便于控制台立即可见/可复制）；若未提供则在 job 内兜底生成。
        $this->runId = trim($runId) !== ''
            ? (string) $runId
            : ('BAS' . now()->format('YmdHis') . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT));
    }

    public function handle(SubscriptionActivationService $service): void
    {
        // 批次号：用于把一次队列批量执行关联起来，便于后续追溯/筛选/可观测。
        $runId = (string) $this->runId;

        $success = 0;
        $failed = 0;
        $failedReasonCounts = [];

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

            try {
                // 双保险：续费单必须绑定订阅
                if ((string) ($order->order_type ?? '') === 'renewal' && ! (int) ($order->site_subscription_id ?? 0)) {
                    throw new \InvalidArgumentException('续费单未绑定订阅（site_subscription_id 为空），不允许批量同步订阅。');
                }

                $subscription = $service->activateOrder($orderId, $this->adminId);

                // 注意：activateOrder 内会更新 meta；这里 refresh 避免覆盖
                $order->refresh();

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

                $nowStr = now()->toDateTimeString();
                $audit[] = [
                    'action' => 'batch_activate_subscription',
                    'scope' => $this->scope,
                    'at' => $nowStr,
                    'admin_id' => $this->adminId,
                    'subscription_id' => $subscription->id,
                    'filters' => $this->filterSummary,
                    'run_id' => $runId,
                    'note' => '批量同步订阅（queue, run_id=' . $runId . ', limit=' . $this->limit . ', matched=' . $this->matchedTotal . ', processed=' . $this->processed . ')',
                ];
                data_set($meta, 'audit', $audit);

                // 便于筛选/统计：记录最近一次批量同步信息（扁平字段）
                data_set($meta, 'batch_activation', [
                    'at' => $nowStr,
                    'admin_id' => $this->adminId,
                    'scope' => $this->scope,
                    'mode' => 'queue',
                    'run_id' => $runId,
                ]);

                $order->meta = $meta;
                $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 ?? []);
                $nowStr = now()->toDateTimeString();

                data_set($meta, 'subscription_activation_error', [
                    'message' => $reason,
                    'at' => $nowStr,
                    'admin_id' => $this->adminId,
                ]);

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

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

        // 最小结果汇总（写入到每个订单的 meta.batch_activation.last_result），便于运营在列表页直接看到“本次队列批量的执行结果”。
        // 注意：为避免引入新表，当前阶段采取“冗余写入到每条订单”策略；后续可演进为独立批次表或日志表。
        $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' => $runId,
            'success' => $success,
            'failed' => $failed,
            'matched' => $this->matchedTotal,
            'processed' => $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 ?? []);
            $ba = (array) (data_get($meta, 'batch_activation', []) ?? []);

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

            data_set($ba, 'last_result', $summary);
            data_set($meta, 'batch_activation', $ba);

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