<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Concerns\ResolvesPlatformAdminContext;
use App\Http\Controllers\Controller;
use App\Models\Merchant;
use App\Models\Plan;
use App\Models\PlatformOrder;
use App\Models\SiteSubscription;
use App\Support\BackUrl;
use App\Support\SubscriptionActivationService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Cache;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

class PlatformOrderController extends Controller
{
    use ResolvesPlatformAdminContext;

    public function create(Request $request): View
    {
        $this->ensurePlatformAdmin($request);

        $merchants = Merchant::query()->orderBy('id')->get(['id', 'name']);
        $plans = Plan::query()->orderBy('sort')->orderByDesc('id')->get();

        // 支持从其它页面（例如订阅详情）带默认值跳转过来，提高运营效率
        $orderTypeFromQuery = $request->query('order_type');
        // require_subscription：用于“续费必须先选订阅”的治理链路。
        // - 优先读 query（用于各种入口跳转）
        // - 同时兼容 old input（用于提交校验失败后 redirect back，仍保持治理 UI 口径）
        $requireSubscription = ((string) $request->query('require_subscription', '') === '1')
            || ((string) $request->old('require_subscription', '') === '1');

        $defaults = [
            'merchant_id' => (int) $request->query('merchant_id', 0),
            'plan_id' => (int) $request->query('plan_id', 0),
            'site_subscription_id' => (int) $request->query('site_subscription_id', 0),
            // 未显式传 order_type 时默认 new_purchase；若是从订阅进入（site_subscription_id 存在），稍后会进一步偏向 renewal。
            'order_type' => $orderTypeFromQuery === null ? 'new_purchase' : (string) $orderTypeFromQuery,
            'quantity' => (int) $request->query('quantity', 1),
            'discount_amount' => (float) $request->query('discount_amount', 0),
            'payment_channel' => (string) $request->query('payment_channel', ''),
            'remark' => (string) $request->query('remark', ''),
            // back：用于创建成功后回到来源页（例如订阅详情）
            'back' => (string) $request->query('back', ''),
            // 线索联动：用于从开通线索进入下单页，需透传到 form hidden input
            'lead_id' => (int) $request->query('lead_id', 0),
        ];

        // back 安全阀：必须为站内相对路径，并拒绝引号/尖括号。
        // 说明：form 页会把 defaults.back 透传到 hidden input 与返回按钮；因此这里提前清洗，避免 unsafe back 在页面中出现。
        $incomingBack = (string) ($defaults['back'] ?? '');
        $defaults['back'] = BackUrl::sanitizeForLinks($incomingBack);

        $siteSubscription = null;
        $siteSubscriptionId = (int) ($defaults['site_subscription_id'] ?? 0);
        if ($siteSubscriptionId > 0) {
            $siteSubscription = SiteSubscription::query()->with(['merchant', 'plan'])->find($siteSubscriptionId);
        }

        // 治理口径：当来源页要求“续费必须绑定订阅”时，若未带订阅ID，则强制回退为新购，避免误导。
        if ($requireSubscription && $siteSubscriptionId <= 0) {
            $defaults['order_type'] = 'new_purchase';
        }

        // 续费下单场景：若带了 site_subscription_id，且未显式指定 order_type，则默认视为续费。
        // 说明：允许用户在“带订阅上下文”的情况下显式选择 new_purchase（用于特殊补单/演示等场景），避免 UI 与测试口径冲突。
        if ($siteSubscriptionId > 0 && $orderTypeFromQuery === null) {
            $defaults['order_type'] = 'renewal';
        }

        // 续费下单场景：若带了 site_subscription_id，但未显式指定 merchant/plan，则从订阅上补齐默认值。
        // 目的：让“从订阅维度跳转到下单页”的链路更稳，不必每次手工二次选择。
        if ($siteSubscription) {
            if ((int) ($defaults['merchant_id'] ?? 0) <= 0) {
                $defaults['merchant_id'] = (int) ($siteSubscription->merchant_id ?? 0);
            }
            if ((int) ($defaults['plan_id'] ?? 0) <= 0) {
                $defaults['plan_id'] = (int) ($siteSubscription->plan_id ?? 0);
            }

            // 治理口径：从订阅进入（site_subscription_id 存在）时，若未显式指定 order_type，则默认偏向续费。
            if ($orderTypeFromQuery === null && trim((string) ($defaults['order_type'] ?? '')) === 'new_purchase') {
                $defaults['order_type'] = 'renewal';
            }

            // 续费默认备注：若未显式传 remark，自动补齐“来自订阅：{subscription_no}”用于追溯/检索。
            if ((string) ($defaults['order_type'] ?? '') === 'renewal' && trim((string) ($defaults['remark'] ?? '')) === '') {
                $remarkPrefix = (string) config('saasshop.platform_orders.renewal_order_remark_prefix', '来自订阅：');
                $defaults['remark'] = $remarkPrefix . (string) $siteSubscription->subscription_no;
            }
        }

        return view('admin.platform_orders.form', [
            'merchants' => $merchants,
            'plans' => $plans,
            'siteSubscription' => $siteSubscription,
            'billingCycleLabels' => $this->billingCycleLabels(),
            'orderTypeLabels' => $this->orderTypeLabels(),
            'requireSubscription' => $requireSubscription,
            'defaults' => $defaults,
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $data = $request->validate([
            'merchant_id' => ['required', 'integer', 'exists:merchants,id'],
            'plan_id' => ['required', 'integer', 'exists:plans,id'],
            // 治理口径：续费单必须绑定订阅（否则无法做到“续期/延长到期”这一核心闭环）
            'site_subscription_id' => [
                Rule::requiredIf(fn () => (string) $request->input('order_type') === 'renewal'),
                'nullable',
                'integer',
                'exists:site_subscriptions,id',
            ],
            'order_type' => ['required', Rule::in(array_keys($this->orderTypeLabels()))],
            'quantity' => ['required', 'integer', 'min:1', 'max:120'],
            'discount_amount' => ['nullable', 'numeric', 'min:0'],
            'payment_channel' => ['nullable', 'string', 'max:30'],
            'remark' => ['nullable', 'string', 'max:2000'],
            'back' => ['nullable', 'string', 'max:2000'],
            // 可选：从开通线索进入下单（用于轻量闭环联动）
            'lead_id' => ['nullable', 'integer', 'exists:platform_leads,id'],
        ]);

        // 治理口径：续费单必须与订阅上下文一致（站点/套餐）。
        // 当前阶段暂不支持“跨站点/跨套餐续费”，避免误续费/串单风险；升级/降级后续再单独建设。
        if ((string) ($data['order_type'] ?? '') === 'renewal') {
            $subId = (int) ($data['site_subscription_id'] ?? 0);
            $sub = $subId > 0 ? SiteSubscription::query()->find($subId) : null;
            if (! $sub) {
                return redirect()->back()->withErrors([
                    'site_subscription_id' => '续费单必须绑定有效订阅。',
                ])->withInput();
            }

            if ((int) ($data['merchant_id'] ?? 0) !== (int) ($sub->merchant_id ?? 0)) {
                return redirect()->back()->withErrors([
                    'merchant_id' => '续费单站点必须与订阅所属站点一致（请从订阅维度进入下单）。',
                ])->withInput();
            }

            if ((int) ($data['plan_id'] ?? 0) !== (int) ($sub->plan_id ?? 0)) {
                return redirect()->back()->withErrors([
                    'plan_id' => '续费单套餐必须与订阅当前套餐一致（当前阶段暂不支持跨套餐续费）。',
                ])->withInput();
            }
        }

        $plan = Plan::query()->findOrFail((int) $data['plan_id']);

        $periodMonths = $this->periodMonthsFromBillingCycle((string) $plan->billing_cycle);
        $quantity = (int) $data['quantity'];

        $listAmount = (float) $plan->price * $quantity;
        $discount = (float) ($data['discount_amount'] ?? 0);
        $discount = max(0, min($listAmount, $discount));

        $payable = max(0, $listAmount - $discount);

        $now = now();

        // 订单号：PO + 时间 + 4位随机数（足够用于当前阶段演示与手工补单）
        $orderNo = 'PO' . $now->format('YmdHis') . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT);

        $order = PlatformOrder::query()->create([
            'merchant_id' => (int) $data['merchant_id'],
            'plan_id' => $plan->id,
            'site_subscription_id' => (int) ($data['site_subscription_id'] ?? 0) ?: null,
            'created_by_admin_id' => $admin->id,
            'order_no' => $orderNo,
            'order_type' => (string) $data['order_type'],
            'status' => 'pending',
            'payment_status' => 'unpaid',
            'payment_channel' => $data['payment_channel'] ?? null,
            'plan_name' => $plan->name,
            'billing_cycle' => $plan->billing_cycle,
            'period_months' => $periodMonths,
            'quantity' => $quantity,
            'list_amount' => $listAmount,
            'discount_amount' => $discount,
            'payable_amount' => $payable,
            'paid_amount' => 0,
            'placed_at' => $now,
            'plan_snapshot' => [
                'plan_id' => $plan->id,
                'code' => $plan->code,
                'name' => $plan->name,
                'billing_cycle' => $plan->billing_cycle,
                'price' => (float) $plan->price,
                'list_price' => (float) $plan->list_price,
                'status' => $plan->status,
                'published_at' => optional($plan->published_at)->toDateTimeString(),
            ],
            'meta' => [
                'created_from' => 'manual_form',
                // 轻量联动：从开通线索创建订单时记录 lead_id（用于后续转化漏斗与追溯）
                'platform_lead_id' => (int) ($data['lead_id'] ?? 0) ?: null,
            ],
            'remark' => $data['remark'] ?? null,
        ]);

        // 轻量闭环：从线索创建订单后，自动把线索标记为 converted。
        // 注意：只做“前进”流转，避免误把已关闭/已确认需求的线索回写。
        $leadId = (int) ($data['lead_id'] ?? 0);
        if ($leadId > 0) {
            $lead = \App\Models\PlatformLead::query()->find($leadId);
            if ($lead && !in_array((string) $lead->status, ['converted', 'closed'], true)) {
                $lead->status = 'converted';
                $lead->save();
            }
        }

        $back = (string) ($data['back'] ?? '');
        // back 安全护栏：统一收敛到 BackUrl::sanitizeForLinks，避免口径漂移。
        $safeBack = BackUrl::sanitizeForLinks($back);

        $redirectUrl = '/admin/platform-orders/' . $order->id;
        if ($safeBack !== '') {
            $redirectUrl .= '?' . \Illuminate\Support\Arr::query(['back' => $safeBack]);
        }

        return redirect($redirectUrl)
            ->with('success', '平台订单已创建：' . $order->order_no . '（待支付/待生效）');
    }

    public function index(Request $request): View
    {
        $this->ensurePlatformAdmin($request);

        $filters = [
            'status' => trim((string) $request->query('status', '')),
            'payment_status' => trim((string) $request->query('payment_status', '')),
            'merchant_id' => trim((string) $request->query('merchant_id', '')),
            'plan_id' => trim((string) $request->query('plan_id', '')),
            // 精确过滤：订阅ID（用于从订阅详情页跳转到平台订单列表时锁定范围）
            'site_subscription_id' => trim((string) $request->query('site_subscription_id', '')),
            'fail_only' => (string) $request->query('fail_only', ''),
            // 只看批量“标记支付并生效”失败：meta.batch_mark_paid_and_activate_error.message 存在
            'bmpa_failed_only' => (string) $request->query('bmpa_failed_only', ''),
            // 只看批量“标记支付并生效”成功：存在 run_id 且 error.message 为空
            'bmpa_success_only' => (string) $request->query('bmpa_success_only', ''),
            'synced_only' => (string) $request->query('synced_only', ''),
        ];

        // 治理口径：bmpa_failed_only 与 bmpa_success_only 互斥（两者同时勾选会导致空集合）。
        // 优先级：失败优先（更贴近“需要处理”的治理语义）。
        if (($filters['bmpa_failed_only'] ?? '') === '1') {
            $filters['bmpa_success_only'] = '';
        } elseif (($filters['bmpa_success_only'] ?? '') === '1') {
            $filters['bmpa_failed_only'] = '';
        }

        $filters += [
            'sync_status' => trim((string) $request->query('sync_status', '')),
            'keyword' => trim((string) $request->query('keyword', '')),
            // 同步失败原因关键词：用于快速定位同原因失败订单（可治理）
            'sync_error_keyword' => trim((string) $request->query('sync_error_keyword', '')),
            // 批量“标记支付并生效”失败原因关键词：用于定位同原因失败订单（可治理）
            'bmpa_error_keyword' => trim((string) $request->query('bmpa_error_keyword', '')),
            // 只看“可同步订阅”的订单：已支付 + 已生效 + 未同步（用于运营快速处理）
            'syncable_only' => (string) $request->query('syncable_only', ''),
            // 只看“续费但未绑定订阅”的脏数据（可治理）
            'renewal_missing_subscription' => (string) $request->query('renewal_missing_subscription', ''),
            // 只看最近 24 小时批量同步过的订单（可治理追踪）
            'batch_synced_24h' => (string) $request->query('batch_synced_24h', ''),
            // 批次号筛选：用于队列批量同步 run_id 追溯
            'batch_activation_run_id' => trim((string) $request->query('batch_activation_run_id', '')),
            // 批量 BMPA 批次号筛选（run_id 追溯）
            'batch_bmpa_run_id' => trim((string) $request->query('batch_bmpa_run_id', '')),
            // 只看最近 24 小时批量“标记支付并生效(BMPA)”过的订单（可治理追踪）
            'batch_mark_paid_and_activate_24h' => (string) $request->query('batch_mark_paid_and_activate_24h', ''),
            // 只看最近 24 小时批量“仅标记为已生效”过的订单（可治理追踪）
            'batch_mark_activated_24h' => (string) $request->query('batch_mark_activated_24h', ''),
            // 只看“对账不一致”的订单（粗版）：meta.payment_summary.total_amount 与 paid_amount 不一致
            'reconcile_mismatch' => (string) $request->query('reconcile_mismatch', ''),
            // 支付回执筛选：has（有回执）/none（无回执（广义））
            'receipt_status' => trim((string) $request->query('receipt_status', '')),
            // 退款轨迹筛选：has（有退款）/none（无退款）
            'refund_status' => trim((string) $request->query('refund_status', '')),
            // 退款数据不一致（可治理）：基于 refund_summary.total_amount 与 paid_amount 对比
            'refund_inconsistent' => (string) $request->query('refund_inconsistent', ''),
            // 线索联动：用于从开通线索跳转查看关联订单
            'lead_id' => trim((string) $request->query('lead_id', '')),
            // 创建时间范围（用于“趋势→集合”跳转与运营筛选）
            'created_from' => trim((string) $request->query('created_from', '')),
            'created_to' => trim((string) $request->query('created_to', '')),

            // 真正可 BMPA 处理集合（与仪表盘「可BMPA处理」口径对齐）：
            // - pending + unpaid
            // - 排除：续费缺订阅（order_type=renewal 且 site_subscription_id 为空）
            // - 排除：存在退款轨迹（refund_total > 0）
            'bmpa_processable_only' => (string) $request->query('bmpa_processable_only', ''),
        ];


        $orders = $this->applyFilters(PlatformOrder::query()->with(['merchant', 'plan', 'siteSubscription']), $filters)
            ->latest('id')
            ->paginate(10)
            ->withQueryString();

        // 当前列表若锁定了订阅ID，则加载该订阅用于“上下文提示/快捷续费下单”等运营入口
        $currentSubscription = null;
        $currentSubscriptionId = (int) ($filters['site_subscription_id'] ?? 0);
        if ($currentSubscriptionId > 0) {
            $currentSubscription = SiteSubscription::query()->with(['merchant', 'plan'])->find($currentSubscriptionId);
        }

        // 列表行级对账视图：回执总额 / 差额（便于运营快速定位问题订单）
        $orders->getCollection()->transform(function (PlatformOrder $o) {
            $receiptTotal = (float) $this->receiptTotalForOrder($o);
            $o->setAttribute('receipt_total', $receiptTotal);
            $o->setAttribute('reconciliation_delta_row', $receiptTotal - (float) $o->paid_amount);

            return $o;
        });

        $baseQuery = $this->applyFilters(PlatformOrder::query(), $filters);

        // 同步失败原因聚合（Top 5）：用于运营快速判断“常见失败原因”
        // 注意：这里用 JSON_EXTRACT 做 group by，MySQL 会返回带引号的 JSON 字符串，展示时做一次 trim 处理。
        $topN = (int) config('saasshop.platform_orders.sync_failed_reason_top_n', 5);
        $topN = max(1, min(20, $topN));

        $failedReasonRows = (clone $baseQuery)
            ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NOT NULL")
            ->selectRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') as reason, count(*) as cnt")
            ->groupBy('reason')
            ->orderByDesc('cnt')
            ->limit($topN)
            ->get();

        // 批量“标记支付并生效”失败原因聚合（Top 5）
        $bmpaFailedReasonRows = (clone $baseQuery)
            ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') IS NOT NULL")
            ->selectRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') as reason, count(*) as cnt")
            ->groupBy('reason')
            ->orderByDesc('cnt')
            ->limit($topN)
            ->get();

        $failedReasonStats = $failedReasonRows->map(function ($row) {
            $reason = (string) ($row->reason ?? '');
            $reason = trim($reason, "\" ");

            return [
                'reason' => $reason !== '' ? $reason : '（空）',
                'count' => (int) ($row->cnt ?? 0),
            ];
        })->values()->all();

        $bmpaFailedReasonStats = $bmpaFailedReasonRows->map(function ($row) {
            $reason = (string) ($row->reason ?? '');
            $reason = trim($reason, "\" ");

            return [
                'reason' => $reason !== '' ? $reason : '（空）',
                'count' => (int) ($row->cnt ?? 0),
            ];
        })->values()->all();

        // 运营摘要中的 meta 汇总（refund/payment）需要遍历订单 meta。
        // 为避免重复 get()，在当前筛选范围内一次性拉取所需字段。
        $metaOrders = (clone $baseQuery)->get(['id', 'paid_amount', 'meta']);
        $totalRefundedAmount = (float) $metaOrders->sum(function ($o) {
            // 优先读 meta.refund_summary.total_amount，回退汇总 meta.refund_receipts[].amount
            $total = (float) (data_get($o->meta, 'refund_summary.total_amount') ?? 0);
            if ($total > 0) {
                return $total;
            }

            $refunds = (array) (data_get($o->meta, 'refund_receipts', []) ?? []);
            $sum = 0.0;
            foreach ($refunds as $r) {
                $sum += (float) (data_get($r, 'amount') ?? 0);
            }
            return $sum;
        });

        $totalReceiptAmount = (float) $this->sumReceiptAmount($metaOrders);
        $totalPayableAmount = (float) ((clone $baseQuery)->sum('payable_amount') ?: 0);
        $totalPaidAmount = (float) ((clone $baseQuery)->sum('paid_amount') ?: 0);

        // created_at 快捷入口日期范围：由 Controller 统一注入，避免 Blade 内多次 now() 调用跨天漂移。
        $baseToday = now();
        $createdRangeToday = $baseToday->format('Y-m-d');
        // 近7天：包含今天共 7 天，因此 from = today - 6 days
        $createdRangeFrom7d = $baseToday->copy()->subDays(6)->format('Y-m-d');
        // 近30天：包含今天共 30 天，因此 from = today - 29 days
        $createdRangeFrom30d = $baseToday->copy()->subDays(29)->format('Y-m-d');

        return view('admin.platform_orders.index', [
            'currentSubscription' => $currentSubscription,
            'orders' => $orders,
            'filters' => $filters,
            'filterOptions' => [
                'statuses' => $this->statusLabels(),
                'paymentStatuses' => $this->paymentStatusLabels(),
            ],
            'merchants' => PlatformOrder::query()->with('merchant')
                ->select('merchant_id')
                ->whereNotNull('merchant_id')
                ->distinct()
                ->get()
                ->pluck('merchant')
                ->filter()
                ->unique('id')
                ->values(),
            'plans' => PlatformOrder::query()->with('plan')
                ->select('plan_id')
                ->whereNotNull('plan_id')
                ->distinct()
                ->get()
                ->pluck('plan')
                ->filter()
                ->unique('id')
                ->values(),
            'statusLabels' => $this->statusLabels(),
            'paymentStatusLabels' => $this->paymentStatusLabels(),
            // order_type label 映射已下沉到 PlatformOrder::orderTypeLabel()；这里不再透传 orderTypeLabels
            // 'orderTypeLabels' => $this->orderTypeLabels(),
            // created_at 快捷入口日期范围
            'createdRangeToday' => $createdRangeToday,
            'createdRangeFrom7d' => $createdRangeFrom7d,
            'createdRangeFrom30d' => $createdRangeFrom30d,

            'summaryStats' => [
                'total_orders' => (clone $baseQuery)->count(),
                'paid_orders' => (clone $baseQuery)->where('payment_status', 'paid')->count(),
                'activated_orders' => (clone $baseQuery)->where('status', 'activated')->count(),
                'synced_orders' => (clone $baseQuery)
                    ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation.subscription_id') IS NOT NULL")
                    ->count(),
                'failed_sync_orders' => (clone $baseQuery)
                    ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NOT NULL")
                    ->count(),
                'bmpa_failed_orders' => (clone $baseQuery)
                    ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') IS NOT NULL")
                    ->count(),
                'bmpa_success_orders' => (clone $baseQuery)
                    ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.run_id') IS NOT NULL")
                    ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') IS NULL")
                    ->count(),
                'unsynced_orders' => (clone $baseQuery)
                    ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation.subscription_id') IS NULL")
                    ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NULL")
                    ->count(),
                'total_payable_amount' => $totalPayableAmount,
                'total_paid_amount' => $totalPaidAmount,
                'syncable_orders' => (clone $baseQuery)
                    ->where('payment_status', 'paid')
                    ->where('status', 'activated')
                    ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation.subscription_id') IS NULL")
                    // 口径一致：syncable_only=1 隐含 sync_status=unsynced（排除同步失败等异常单）
                    ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NULL")
                    // 口径一致：排除（续费但未绑定订阅）的脏数据
                    ->where(function (\Illuminate\Database\Eloquent\Builder $q) {
                        $q->where('order_type', '!=', 'renewal')
                            ->orWhereNotNull('site_subscription_id');
                    })
                    ->count(),
                'renewal_missing_subscription_orders' => (clone $baseQuery)
                    ->where('order_type', 'renewal')
                    ->whereNull('site_subscription_id')
                    ->count(),
                'batch_synced_24h_orders' => (function () use ($baseQuery) {
                    $since = now()->subHours(24)->format('Y-m-d H:i:s');

                    $q = (clone $baseQuery)
                        ->whereRaw("JSON_EXTRACT(meta, '$.batch_activation.at') IS NOT NULL");

                    $driver = $q->getQuery()->getConnection()->getDriverName();
                    if ($driver === 'sqlite') {
                        $q->whereRaw("JSON_EXTRACT(meta, '$.batch_activation.at') >= ?", [$since]);
                    } else {
                        $q->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_activation.at')) >= ?", [$since]);
                    }

                    return $q->count();
                })(),
                'batch_mark_paid_and_activate_24h_orders' => (function () use ($baseQuery) {
                    $since = now()->subHours(24)->format('Y-m-d H:i:s');

                    $q = (clone $baseQuery)
                        ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.at') IS NOT NULL");

                    $driver = $q->getQuery()->getConnection()->getDriverName();
                    if ($driver === 'sqlite') {
                        $q->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.at') >= ?", [$since]);
                    } else {
                        $q->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.at')) >= ?", [$since]);
                    }

                    return $q->count();
                })(),
                'batch_mark_activated_24h_orders' => (function () use ($baseQuery) {
                    $since = now()->subHours(24)->format('Y-m-d H:i:s');

                    $q = (clone $baseQuery)
                        ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_activated.at') IS NOT NULL");

                    $driver = $q->getQuery()->getConnection()->getDriverName();
                    if ($driver === 'sqlite') {
                        $q->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_activated.at') >= ?", [$since]);
                    } else {
                        $q->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_mark_activated.at')) >= ?", [$since]);
                    }

                    return $q->count();
                })(),
                'partially_refunded_orders' => (clone $baseQuery)->where('payment_status', 'partially_refunded')->count(),
                'refunded_orders' => (clone $baseQuery)->where('payment_status', 'refunded')->count(),
                'total_refunded_amount' => $totalRefundedAmount,
                'receipt_orders' => (clone $baseQuery)
                    ->where(function (Builder $q) {
                        $q->whereRaw("JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NOT NULL")
                          ->orWhereRaw("JSON_EXTRACT(meta, '$.payment_receipts[0].amount') IS NOT NULL");
                    })
                    ->count(),
                'no_receipt_orders' => (clone $baseQuery)
                    ->whereRaw("JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NULL")
                    ->whereRaw("JSON_EXTRACT(meta, '$.payment_receipts[0].amount') IS NULL")
                    ->count(),
                'refund_orders' => (clone $baseQuery)
                    ->where(function (Builder $q) {
                        $q->whereRaw("JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL")
                          ->orWhereRaw("JSON_EXTRACT(meta, '$.refund_receipts[0].amount') IS NOT NULL");
                    })
                    ->count(),
                'no_refund_orders' => (clone $baseQuery)
                    ->whereRaw("JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NULL")
                    ->whereRaw("JSON_EXTRACT(meta, '$.refund_receipts[0].amount') IS NULL")
                    ->count(),
                'total_receipt_amount' => $totalReceiptAmount,
                // 对账差额：回执总额 - 订单已付总额（当前筛选范围）
                'reconciliation_delta' => (float) ($totalReceiptAmount - $totalPaidAmount),
                'reconciliation_delta_note' => '回执总额 - 订单已付总额',
                // 对账不一致订单数（在当前筛选范围基础上叠加 mismatch 口径）
                'reconcile_mismatch_orders' => (function () use ($filters) {
                    $mismatchFilters = $filters;
                    $mismatchFilters['reconcile_mismatch'] = '1';

                    return $this->applyFilters(PlatformOrder::query(), $mismatchFilters)->count();
                })(),
                // 退款数据不一致订单数（在当前筛选范围基础上叠加 inconsistent 口径）
                'refund_inconsistent_orders' => (function () use ($filters) {
                    $f = $filters;
                    $f['refund_inconsistent'] = '1';

                    return $this->applyFilters(PlatformOrder::query(), $f)->count();
                })(),
            ],
            'failedReasonStats' => $failedReasonStats,
            'bmpaFailedReasonStats' => $bmpaFailedReasonStats,
        ]);
    }

    public function show(Request $request, PlatformOrder $order): View
    {
        $this->ensurePlatformAdmin($request);

        $order->loadMissing(['merchant', 'plan', 'siteSubscription']);

        return view('admin.platform_orders.show', [
            'order' => $order,
            'statusLabels' => $this->statusLabels(),
            'paymentStatusLabels' => $this->paymentStatusLabels(),
            // order_type label 映射已下沉到 PlatformOrder::orderTypeLabel()；这里不再透传 orderTypeLabels
            // 'orderTypeLabels' => $this->orderTypeLabels(),
        ]);
    }

    /**
     * 导出单笔订单的“对账明细”（支付回执 + 退款记录）CSV。
     * 用于运营线下对账沟通/留档，不依赖订单列表的筛选口径。
     */
    public function exportLedger(Request $request, PlatformOrder $order): StreamedResponse
    {
        $this->ensurePlatformAdmin($request);

        // 安全阀：必须显式声明 download=1，避免浏览器预取/误触发导致频繁导出
        if ((string) $request->query('download', '') !== '1') {
            abort(400, 'download=1 required');
        }

        $order->loadMissing(['merchant', 'plan', 'siteSubscription']);

        $paymentReceipts = (array) (data_get($order->meta, 'payment_receipts', []) ?? []);
        $refundReceipts = (array) (data_get($order->meta, 'refund_receipts', []) ?? []);

        $includeOrderSnapshot = (string) $request->query('include_order_snapshot', '') === '1';

        $filename = 'platform_order_' . $order->id . '_ledger_' . now()->format('Ymd_His') . '.csv';

        return response()->streamDownload(function () use ($order, $paymentReceipts, $refundReceipts, $includeOrderSnapshot) {
            $out = fopen('php://output', 'w');

            // UTF-8 BOM，避免 Excel 打开中文乱码
            fwrite($out, "\xEF\xBB\xBF");

            // 订单摘要（基础三行）
            fputcsv($out, ['order_id', (string) $order->id]);
            fputcsv($out, ['order_no', (string) $order->order_no]);
            fputcsv($out, ['exported_at', now()->format('Y-m-d H:i:s')]);

            // 可选：导出更多订单快照字段（便于财务/对账留档，不必另截图）
            if ($includeOrderSnapshot) {
                fputcsv($out, ['merchant_id', (string) ($order->merchant_id ?? '')]);
                fputcsv($out, ['merchant_name', (string) ($order->merchant?->name ?? '')]);
                fputcsv($out, ['plan_id', (string) ($order->plan_id ?? '')]);
                fputcsv($out, ['plan_name', (string) ($order->plan_name ?? ($order->plan?->name ?? ''))]);
                fputcsv($out, ['order_type', (string) ($order->order_type ?? '')]);
                fputcsv($out, ['status', (string) ($order->status ?? '')]);
                fputcsv($out, ['payment_status', (string) ($order->payment_status ?? '')]);
                fputcsv($out, ['payable_amount', number_format((float) ($order->payable_amount ?? 0), 2, '.', '')]);
                fputcsv($out, ['paid_amount', number_format((float) ($order->paid_amount ?? 0), 2, '.', '')]);

                // 汇总字段：对账/退款快速口径（复用模型统一口径，避免控制器内重复计算导致不一致）
                $receiptTotal = (float) $order->receiptTotal();
                $refundTotal = (float) $order->refundTotal();
                $delta = $receiptTotal - (float) ($order->paid_amount ?? 0);

                $tol = (float) config('saasshop.amounts.tolerance', 0.01);

                $reconcileMismatch = (bool) $order->isReconcileMismatch();

                fputcsv($out, ['receipt_total', number_format($receiptTotal, 2, '.', '')]);
                fputcsv($out, ['refund_total', number_format($refundTotal, 2, '.', '')]);
                fputcsv($out, ['reconcile_delta', number_format($delta, 2, '.', '')]);
                fputcsv($out, ['amount_tolerance', number_format($tol, 2, '.', '')]);
                $refundInconsistent = (bool) $order->isRefundInconsistent();

                fputcsv($out, ['reconcile_mismatch', $reconcileMismatch ? '1' : '0']);
                fputcsv($out, ['refund_inconsistent', $refundInconsistent ? '1' : '0']);

                // 口径说明（给财务/运营离线核对时可读）
                fputcsv($out, ['reconcile_mismatch_rule', 'receipt_total > 0 && abs(receipt_total - paid_amount) >= amount_tolerance']);
                fputcsv($out, ['refund_inconsistent_rule', 'PlatformOrder::isRefundInconsistent()（按系统统一容差口径）']);

                fputcsv($out, ['placed_at', (string) (optional($order->placed_at)->format('Y-m-d H:i:s') ?? '')]);
                fputcsv($out, ['paid_at', (string) (optional($order->paid_at)->format('Y-m-d H:i:s') ?? '')]);
                fputcsv($out, ['activated_at', (string) (optional($order->activated_at)->format('Y-m-d H:i:s') ?? '')]);
                fputcsv($out, ['refunded_at', (string) (optional($order->refunded_at)->format('Y-m-d H:i:s') ?? '')]);
                fputcsv($out, ['site_subscription_id', (string) ($order->site_subscription_id ?? '')]);
                fputcsv($out, ['subscription_no', (string) ($order->siteSubscription?->subscription_no ?? '')]);
            }

            fputcsv($out, []);

            // 明细表头
            fputcsv($out, ['record_type', 'receipt_type', 'channel', 'amount', 'biz_time', 'created_at', 'admin_id', 'note']);

            foreach ($paymentReceipts as $r) {
                fputcsv($out, [
                    'payment',
                    (string) (data_get($r, 'type') ?? ''),
                    (string) (data_get($r, 'channel') ?? ''),
                    number_format((float) (data_get($r, 'amount') ?? 0), 2, '.', ''),
                    (string) (data_get($r, 'paid_at') ?? ''),
                    (string) (data_get($r, 'created_at') ?? ''),
                    (string) (data_get($r, 'admin_id') ?? ''),
                    (string) (data_get($r, 'note') ?? ''),
                ]);
            }

            foreach ($refundReceipts as $r) {
                fputcsv($out, [
                    'refund',
                    (string) (data_get($r, 'type') ?? ''),
                    (string) (data_get($r, 'channel') ?? ''),
                    number_format((float) (data_get($r, 'amount') ?? 0), 2, '.', ''),
                    (string) (data_get($r, 'refunded_at') ?? ''),
                    (string) (data_get($r, 'created_at') ?? ''),
                    (string) (data_get($r, 'admin_id') ?? ''),
                    (string) (data_get($r, 'note') ?? ''),
                ]);
            }

            fclose($out);
        }, $filename, [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    /**
     * 治理动作：为订单手工绑定订阅（用于“续费缺订阅”脏数据修复）。
     *
     * 口径：
     * - 仅平台管理员可操作（ensurePlatformAdmin）
     * - 仅允许续费单绑定订阅
     * - 订阅必须与订单 merchant_id / plan_id 一致，避免串单
     * - 写入 meta.audit 留痕
     */
    public function attachSubscription(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $data = $request->validate([
            'site_subscription_id' => ['required', 'integer', 'exists:site_subscriptions,id'],
            'back' => ['nullable', 'string', 'max:2000'],
        ]);

        $safeBack = \App\Support\BackUrl::sanitizeForLinks((string) ($data['back'] ?? ''));
        // 进一步稳妥：强制回跳到订单详情自身（避免外部页面传错 back，导致运营迷路）
        $fallbackBack = '/admin/platform-orders/' . $order->id;
        if ($safeBack === '') {
            $safeBack = $fallbackBack;
        }

        if ((string) ($order->order_type ?? '') !== 'renewal') {
            return ($safeBack !== '' ? redirect($safeBack) : redirect()->back())
                ->with('warning', '仅「续费」类型订单允许绑定订阅。');
        }

        if ((int) ($order->site_subscription_id ?? 0) > 0) {
            return ($safeBack !== '' ? redirect($safeBack) : redirect()->back())
                ->with('warning', '该订单已绑定订阅，无需重复操作。');
        }

        $subId = (int) $data['site_subscription_id'];
        $sub = SiteSubscription::query()->with(['merchant', 'plan'])->findOrFail($subId);

        // 强约束：订阅上下文必须与订单一致
        if ((int) ($sub->merchant_id ?? 0) !== (int) ($order->merchant_id ?? 0)) {
            return ($safeBack !== '' ? redirect($safeBack) : redirect()->back())
                ->withErrors([
                    'site_subscription_id' => '订阅所属站点与订单站点不一致，禁止绑定（避免串单）。',
                ]);
        }
        if ((int) ($sub->plan_id ?? 0) !== (int) ($order->plan_id ?? 0)) {
            return ($safeBack !== '' ? redirect($safeBack) : redirect()->back())
                ->withErrors([
                    'site_subscription_id' => '订阅套餐与订单套餐不一致，禁止绑定（避免跨套餐续费）。',
                ]);
        }

        $order->site_subscription_id = $sub->id;

        $meta = (array) ($order->meta ?? []);
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'attach_subscription',
            'scope' => 'single',
            'at' => now()->toDateTimeString(),
            'admin_id' => $admin->id,
            'subscription_id' => $sub->id,
            'subscription_no' => (string) ($sub->subscription_no ?? ''),
            'note' => '续费缺订阅治理：手工绑定订阅',
        ];
        data_set($meta, 'audit', $audit);
        $order->meta = $meta;

        $order->save();

        // 绑定成功后追加一个轻量 query，用于前端 JS 做滚动/高亮提示（渐进增强）
        $redirectUrl = $safeBack;
        if (str_contains($redirectUrl, '?')) {
            $redirectUrl .= '&attached_subscription=1';
        } else {
            $redirectUrl .= '?attached_subscription=1';
        }

        return redirect($redirectUrl)
            ->with('success', '已绑定订阅：' . (string) ($sub->subscription_no ?? $sub->id));
    }

    public function activateSubscription(Request $request, PlatformOrder $order, SubscriptionActivationService $service): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        // 治理优先：续费单必须绑定订阅（兼容历史脏数据/手工改库等场景）
        if ((string) ($order->order_type ?? '') === 'renewal' && ! (int) ($order->site_subscription_id ?? 0)) {
            return redirect()->back()->with('warning', '当前订单类型为「续费」，但未绑定订阅（site_subscription_id 为空）。为避免误同步/续期串单，请先补齐订阅关联后再处理。');
        }

        // 治理优先：当订单命中金额/状态不一致时，不建议直接同步订阅（避免把“带病订单”同步到订阅）
        if ($order->isReconcileMismatch() || $order->isRefundInconsistent()) {
            return redirect()->back()->with('warning', '当前订单命中「对账不一致/退款不一致」，为避免带病同步，请先完成金额/状态治理（补回执/核对退款/修正状态）后再同步订阅。');
        }

        try {
            $subscription = $service->activateOrder($order->id, $admin->id);

            // 重要：activateOrder() 内会更新订单 meta（subscription_activation 等）。
            // 这里必须 refresh，避免用旧 $order->meta 覆盖掉刚写入的 subscription_activation。
            $order->refresh();

            // 同步成功：清理失败记录（若存在）+ 写入审计记录
            $meta = (array) ($order->meta ?? []);
            data_forget($meta, 'subscription_activation_error');

            $audit = (array) (data_get($meta, 'audit', []) ?? []);
            $audit[] = [
                'action' => 'activate_subscription',
                'scope' => 'single',
                'at' => now()->toDateTimeString(),
                'admin_id' => $admin->id,
                'subscription_id' => $subscription->id,
                'note' => '手动点击订单详情【同步订阅】',
            ];
            data_set($meta, 'audit', $audit);

            $order->meta = $meta;
            $order->save();
        } catch (\Throwable $e) {
            // 同步失败：写入错误信息，便于运营排查（可治理）
            $meta = (array) ($order->meta ?? []);
            data_set($meta, 'subscription_activation_error', [
                'message' => $e->getMessage(),
                'at' => now()->toDateTimeString(),
                'admin_id' => $admin->id,
            ]);
            $order->meta = $meta;
            $order->save();

            return redirect()->back()->with('error', '订阅同步失败：' . $e->getMessage());
        }

        return redirect()->back()->with('success', '订阅已同步：' . $subscription->subscription_no);
    }

    public function markPaidAndActivate(Request $request, PlatformOrder $order, SubscriptionActivationService $service): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        // 治理优先：续费单必须绑定订阅（兼容历史脏数据/手工改库等场景）
        if ((string) ($order->order_type ?? '') === 'renewal' && ! (int) ($order->site_subscription_id ?? 0)) {
            return redirect()->back()->with('warning', '当前订单类型为「续费」，但未绑定订阅（site_subscription_id 为空）。为避免误同步/续期串单，请先补齐订阅关联后再处理。');
        }

        // 治理优先：若该订单已有退款轨迹（refund_summary/refund_receipts），不允许直接“标记支付并生效”，避免出现带退款的订单被强行推进并同步订阅
        if ((float) $order->refundTotal() > 0) {
            return redirect()->back()->with('warning', '当前订单已存在退款记录/退款汇总，请先核对退款轨迹与订单状态后再处理（不建议直接标记支付并生效）。');
        }

        // 治理优先：若该订单已有回执但回执总额与应标记的已付金额不一致，不允许直接“标记支付并生效”
        // 说明：该动作会尝试补回执并同步订阅，若原回执存在差额，直接推进可能掩盖问题
        $receiptTotal = (float) $order->receiptTotal();
        $expectedPaid = (float) (($order->paid_amount ?? 0) > 0 ? $order->paid_amount : $order->payable_amount);
        $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 ($receiptCents > 0 && abs($receiptCents - $expectedCents) >= $tolCents) {
            return redirect()->back()->with('warning', '当前订单已存在支付回执，但回执总额与订单金额不一致。为避免带病推进，请先补齐/修正支付回执后再执行「标记支付并生效」。');
        }

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

        // 兼容：若尚未写入“支付回执”，则自动补一条（可治理）
        $meta = (array) ($order->meta ?? []);
        $receipts = (array) (data_get($meta, 'payment_receipts', []) ?? []);
        if (count($receipts) === 0) {
            $receipts[] = [
                'type' => 'manual_mark_paid',
                'channel' => (string) ($order->payment_channel ?? ''),
                'amount' => (float) $order->paid_amount,
                'paid_at' => $order->paid_at ? $order->paid_at->format('Y-m-d H:i:s') : $now->toDateTimeString(),
                'note' => '由【标记支付并生效】自动补记（可治理）',
                'created_at' => $now->toDateTimeString(),
                'admin_id' => $admin->id,
            ];
            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') ?? ''),
            ]);

            $order->meta = $meta;
        }

        $order->save();

        // 立刻同步订阅
        try {
            $subscription = $service->activateOrder($order->id, $admin->id);

            // 重要：activateOrder() 内会更新订单 meta（subscription_activation 等）。
            // 这里必须 refresh，避免用旧 $order->meta 覆盖掉刚写入的 subscription_activation。
            $order->refresh();

            $meta = (array) ($order->meta ?? []);
            data_forget($meta, 'subscription_activation_error');

            // 审计：标记支付并生效（并已同步订阅）
            $audit = (array) (data_get($meta, 'audit', []) ?? []);
            $audit[] = [
                'action' => 'mark_paid_and_activate',
                'scope' => 'single',
                'at' => now()->toDateTimeString(),
                'admin_id' => $admin->id,
                'subscription_id' => $subscription->id,
                'snapshot' => [
                    'paid_amount' => (float) ($order->paid_amount ?? 0),
                    'payable_amount' => (float) ($order->payable_amount ?? 0),
                ],
                'note' => '手动点击订单详情【标记支付并生效】（包含订阅同步）',
            ];
            data_set($meta, 'audit', $audit);

            $order->meta = $meta;
            $order->save();
        } catch (\Throwable $e) {
            $meta = (array) ($order->meta ?? []);
            data_set($meta, 'subscription_activation_error', [
                'message' => $e->getMessage(),
                'at' => now()->toDateTimeString(),
                'admin_id' => $admin->id,
            ]);
            $order->meta = $meta;
            $order->save();

            return redirect()->back()->with('error', '订单已标记为已支付/已生效，但订阅同步失败：' . $e->getMessage());
        }

        return redirect()->back()->with('success', '订单已标记支付并生效，订阅已同步：' . $subscription->subscription_no);
    }

    public function addPaymentReceipt(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $data = $request->validate([
            'type' => ['required', 'string', 'max:30'],
            'channel' => ['nullable', 'string', 'max:30'],
            'amount' => ['required', 'numeric', 'min:0'],
            'paid_at' => ['nullable', 'date'],
            'note' => ['nullable', 'string', 'max:2000'],
        ]);

        $now = now();

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

        $receipts[] = [
            'type' => (string) $data['type'],
            'channel' => (string) ($data['channel'] ?? ''),
            'amount' => (float) $data['amount'],
            'paid_at' => $data['paid_at'] ? (string) $data['paid_at'] : null,
            'note' => (string) ($data['note'] ?? ''),
            'created_at' => $now->toDateTimeString(),
            'admin_id' => $admin->id,
        ];

        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') ?? ''),
        ]);

        // 审计：追加支付回执（用于对账留痕，不自动改状态）
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'add_payment_receipt',
            'scope' => 'single',
            'at' => $now->toDateTimeString(),
            'admin_id' => $admin->id,
            'note' => '追加支付回执（对账留痕，不自动改状态）',
            'snapshot' => [
                'type' => (string) $data['type'],
                'channel' => (string) ($data['channel'] ?? ''),
                'amount' => (float) $data['amount'],
                'paid_at' => $data['paid_at'] ? (string) $data['paid_at'] : null,
                'summary' => [
                    'count' => (int) data_get($meta, 'payment_summary.count'),
                    'total_amount' => (float) data_get($meta, 'payment_summary.total_amount'),
                ],
            ],
        ];
        data_set($meta, 'audit', $audit);

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

        $safeBack = BackUrl::sanitizeForLinks((string) $request->input('back', ''));

        $redirectUrl = '/admin/platform-orders/' . $order->id;
        if ($safeBack !== '') {
            $redirectUrl .= '?' . \Illuminate\Support\Arr::query(['back' => $safeBack]);
        }

        return redirect($redirectUrl . '#add-payment-receipt')
            ->with('success', '已追加支付回执记录（仅用于对账留痕，不自动改状态）。');
    }

    public function addRefundReceipt(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $data = $request->validate([
            'type' => ['required', 'string', 'max:30'],
            'channel' => ['nullable', 'string', 'max:30'],
            'amount' => ['required', 'numeric', 'min:0'],
            'refunded_at' => ['nullable', 'date'],
            'note' => ['nullable', 'string', 'max:2000'],
        ]);

        $now = now();

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

        $refunds[] = [
            'type' => (string) $data['type'],
            'channel' => (string) ($data['channel'] ?? ''),
            'amount' => (float) $data['amount'],
            'refunded_at' => $data['refunded_at'] ? (string) $data['refunded_at'] : null,
            'note' => (string) ($data['note'] ?? ''),
            'created_at' => $now->toDateTimeString(),
            'admin_id' => $admin->id,
        ];

        data_set($meta, 'refund_receipts', $refunds);

        // 扁平统计：避免在列表/汇总处频繁遍历退款数组（可治理）
        $totalRefunded = 0.0;
        foreach ($refunds as $r) {
            $totalRefunded += (float) (data_get($r, 'amount') ?? 0);
        }
        $latestRefund = count($refunds) > 0 ? end($refunds) : null;
        data_set($meta, 'refund_summary', [
            'count' => count($refunds),
            'total_amount' => $totalRefunded,
            'last_at' => (string) (data_get($latestRefund, 'refunded_at') ?? ''),
            'last_amount' => (float) (data_get($latestRefund, 'amount') ?? 0),
            'last_channel' => (string) (data_get($latestRefund, 'channel') ?? ''),
        ]);

        // 审计：追加退款回执（用于退款轨迹留痕）
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'add_refund_receipt',
            'scope' => 'single',
            'at' => $now->toDateTimeString(),
            'admin_id' => $admin->id,
            'note' => '追加退款回执（用于退款轨迹留痕）',
            'snapshot' => [
                'type' => (string) $data['type'],
                'channel' => (string) ($data['channel'] ?? ''),
                'amount' => (float) $data['amount'],
                'refunded_at' => $data['refunded_at'] ? (string) $data['refunded_at'] : null,
                'summary' => [
                    'count' => (int) data_get($meta, 'refund_summary.count'),
                    'total_amount' => (float) data_get($meta, 'refund_summary.total_amount'),
                ],
            ],
        ];
        data_set($meta, 'audit', $audit);

        // 可治理辅助：自动推进退款标记（仅当退款金额>0 时）
        // 注意：允许从 paid / partially_refunded 推进到 partially_refunded / refunded
        // 且不会把已 refunded 的订单降级。
        if ((float) $data['amount'] > 0 && in_array($order->payment_status, ['paid', 'partially_refunded'], true)) {
            $paidAmount = (float) ($order->paid_amount ?? 0);

            // 退款总额 + 容差 >= 已付金额 => 视为已退款；否则视为部分退款（与 refund_inconsistent 口径一致）
            $tol = (float) config('saasshop.amounts.tolerance', 0.01);
            $tolCents = (int) round($tol * 100);
            $tolCents = max(1, $tolCents);
            $paidCents = (int) round($paidAmount * 100);
            $refundCents = (int) round($totalRefunded * 100);

            if ($paidCents > 0 && ($refundCents + $tolCents) >= $paidCents) {
                $order->payment_status = 'refunded';
                $order->refunded_at = $order->refunded_at ?: now();
            } else {
                $order->payment_status = 'partially_refunded';
                $order->refunded_at = $order->refunded_at ?: now();
            }
        }

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

        $safeBack = BackUrl::sanitizeForLinks((string) $request->input('back', ''));

        $redirectUrl = '/admin/platform-orders/' . $order->id;
        if ($safeBack !== '') {
            $redirectUrl .= '?' . \Illuminate\Support\Arr::query(['back' => $safeBack]);
        }

        return redirect($redirectUrl . '#add-refund-receipt')
            ->with('success', '已追加退款记录（用于退款轨迹留痕）。');
    }

    public function markRefunded(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $paidAmount = (float) ($order->paid_amount ?? 0);
        if ($paidAmount <= 0) {
            return redirect()->back()->with('warning', '当前订单已付金额为 0，无法标记为已退款。');
        }

        if ((string) $order->payment_status === 'refunded') {
            return redirect()->back()->with('warning', '当前订单已是已退款状态，无需重复操作。');
        }

        // 安全阀：仅允许在“退款总额已达到/超过已付金额 + 容差”时标记为已退款
        $refundTotal = (float) $this->refundTotalForOrder($order);

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

        if (round($refundTotal * 100) + $tolCents < round($paidAmount * 100)) {
            return redirect()->back()->with('warning', '退款总额尚未达到已付金额（考虑容差），无法标记为已退款。请先核对/补齐退款记录。');
        }

        $now = now();
        $order->payment_status = 'refunded';
        $order->refunded_at = $order->refunded_at ?: $now;

        $meta = (array) ($order->meta ?? []);
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'mark_refunded',
            'scope' => 'single',
            'at' => $now->toDateTimeString(),
            'admin_id' => $admin->id,
            'note' => '手动标记为已退款（仅修正支付状态，不自动写退款回执）',
            'snapshot' => [
                'paid_amount' => $paidAmount,
                'refund_total' => $refundTotal,
            ],
        ];
        data_set($meta, 'audit', $audit);

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

        return redirect()->back()->with('success', '已将订单支付状态标记为已退款（未自动写入退款回执）。');
    }

    public function markPartiallyRefunded(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $paidAmount = (float) ($order->paid_amount ?? 0);
        if ($paidAmount <= 0) {
            return redirect()->back()->with('warning', '当前订单已付金额为 0，无法标记为部分退款。');
        }

        if ((string) $order->payment_status === 'partially_refunded') {
            return redirect()->back()->with('warning', '当前订单已是部分退款状态，无需重复操作。');
        }

        // 安全阀：部分退款需要“退款总额>0 且 未达到已付金额 + 容差”
        $refundTotal = (float) $this->refundTotalForOrder($order);

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

        if (round($refundTotal * 100) <= 0) {
            return redirect()->back()->with('warning', '退款总额为 0，无法标记为部分退款。');
        }
        if (round($refundTotal * 100) + $tolCents >= round($paidAmount * 100)) {
            return redirect()->back()->with('warning', '退款总额已达到/超过已付金额（考虑容差），建议标记为已退款。');
        }

        $now = now();
        $order->payment_status = 'partially_refunded';
        $order->refunded_at = $order->refunded_at ?: $now;

        $meta = (array) ($order->meta ?? []);
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'mark_partially_refunded',
            'scope' => 'single',
            'at' => $now->toDateTimeString(),
            'admin_id' => $admin->id,
            'note' => '手动标记为部分退款（仅修正支付状态，不自动写退款回执）',
            'snapshot' => [
                'paid_amount' => $paidAmount,
                'refund_total' => $refundTotal,
            ],
        ];
        data_set($meta, 'audit', $audit);

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

        return redirect()->back()->with('success', '已将订单支付状态标记为部分退款（未自动写入退款回执）。');
    }

    public function markPaidStatus(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $paidAmount = (float) ($order->paid_amount ?? 0);
        if ($paidAmount <= 0) {
            return redirect()->back()->with('warning', '当前订单已付金额为 0，无法标记为已支付。');
        }

        if ((string) $order->payment_status === 'unpaid') {
            return redirect()->back()->with('warning', '当前订单为未支付状态，不允许直接标记为已支付，请使用「标记支付并生效」或补回执/金额后再处理。');
        }

        if ((string) $order->payment_status === 'paid') {
            return redirect()->back()->with('warning', '当前订单已是已支付状态，无需重复操作。');
        }

        $now = now();
        $order->payment_status = 'paid';
        // paid 状态不强依赖 refunded_at，这里不做清空，避免丢历史痕迹

        $meta = (array) ($order->meta ?? []);
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'mark_paid_status',
            'scope' => 'single',
            'at' => $now->toDateTimeString(),
            'admin_id' => $admin->id,
            'note' => '手动标记为已支付（仅修正支付状态，不自动写回执/退款回执）',
            'snapshot' => [
                'paid_amount' => $paidAmount,
                'refund_total' => (float) $this->refundTotalForOrder($order),
            ],
        ];
        data_set($meta, 'audit', $audit);

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

        return redirect()->back()->with('success', '已将订单支付状态标记为已支付（未自动写入回执/退款回执）。');
    }

    public function markActivated(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        // 治理优先：续费单必须绑定订阅（兼容历史脏数据/手工改库等场景）
        if ((string) ($order->order_type ?? '') === 'renewal' && ! (int) ($order->site_subscription_id ?? 0)) {
            return redirect()->back()->with('warning', '当前订单类型为「续费」，但未绑定订阅（site_subscription_id 为空）。为避免续期串单，请先补齐订阅关联后再标记为已生效。');
        }

        // 治理优先：对账不一致/存在退款轨迹时，不建议直接“仅标记为已生效”，避免把带病订单推进到 activated。
        if ($order->isReconcileMismatch()) {
            return redirect()->back()->with('warning', '当前订单存在对账不一致（支付回执总额与已付金额不一致），请先补齐/修正回执轨迹后再标记为已生效。');
        }
        if ((float) $order->refundTotal() > 0) {
            return redirect()->back()->with('warning', '当前订单已存在退款记录/退款汇总，请先核对退款轨迹与订单状态后再标记为已生效。');
        }

        // 仅标记“已生效”：用于处理已支付但未生效的订单（不改 payment_status）
        if ($order->payment_status !== 'paid') {
            return redirect()->back()->with('warning', '当前订单尚未支付，无法仅标记为已生效。');
        }

        if ($order->status === 'activated') {
            return redirect()->back()->with('warning', '当前订单已是已生效状态，无需重复操作。');
        }

        $now = now();
        $order->status = 'activated';
        $order->activated_at = $order->activated_at ?: $now;
        $order->save();

        // 轻量审计：记录这次“仅标记生效”的动作，便于追溯
        $meta = (array) ($order->meta ?? []);
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'mark_activated',
            'scope' => 'single',
            'at' => $now->toDateTimeString(),
            'admin_id' => $admin->id,
        ];
        data_set($meta, 'audit', $audit);
        $order->meta = $meta;
        $order->save();

        return redirect()->back()->with('success', '订单已标记为已生效（未修改支付状态）。');
    }

    public function export(Request $request): StreamedResponse
    {
        $this->ensurePlatformAdmin($request);

        // 安全阀：必须显式声明 download=1，避免浏览器预取/误触发导致频繁导出
        if ((string) $request->query('download', '') !== '1') {
            abort(400, 'download=1 required');
        }

        $filters = [
            'status' => trim((string) $request->query('status', '')),
            'payment_status' => trim((string) $request->query('payment_status', '')),
            'merchant_id' => trim((string) $request->query('merchant_id', '')),
            'plan_id' => trim((string) $request->query('plan_id', '')),
            // 精确过滤：订阅ID（用于从订阅详情页跳转到平台订单列表时锁定范围）
            'site_subscription_id' => trim((string) $request->query('site_subscription_id', '')),
            'fail_only' => (string) $request->query('fail_only', ''),
            // 只看批量“标记支付并生效”失败：meta.batch_mark_paid_and_activate_error.message 存在
            'bmpa_failed_only' => (string) $request->query('bmpa_failed_only', ''),
            // 只看批量“标记支付并生效”成功：存在 run_id 且 error.message 为空
            'bmpa_success_only' => (string) $request->query('bmpa_success_only', ''),
            'synced_only' => (string) $request->query('synced_only', ''),
        ];

        // 治理口径：bmpa_failed_only 与 bmpa_success_only 互斥（两者同时勾选会导致空集合）。
        // 优先级：失败优先（更贴近“需要处理”的治理语义）。
        if (($filters['bmpa_failed_only'] ?? '') === '1') {
            $filters['bmpa_success_only'] = '';
        } elseif (($filters['bmpa_success_only'] ?? '') === '1') {
            $filters['bmpa_failed_only'] = '';
        }

        $filters += [
            'sync_status' => trim((string) $request->query('sync_status', '')),
            'keyword' => trim((string) $request->query('keyword', '')),
            // 同步失败原因关键词：用于快速定位同原因失败订单（可治理）
            'sync_error_keyword' => trim((string) $request->query('sync_error_keyword', '')),
            // 批量“标记支付并生效”失败原因关键词：用于定位同原因失败订单（可治理）
            'bmpa_error_keyword' => trim((string) $request->query('bmpa_error_keyword', '')),
            // 只看“可同步订阅”的订单：已支付 + 已生效 + 未同步（用于运营快速处理）
            'syncable_only' => (string) $request->query('syncable_only', ''),
            'renewal_missing_subscription' => (string) $request->query('renewal_missing_subscription', ''),
            // 只看最近 24 小时批量同步过的订单（可治理追踪）
            'batch_synced_24h' => (string) $request->query('batch_synced_24h', ''),
            // 批次号筛选：用于队列批量同步 run_id 追溯
            'batch_activation_run_id' => trim((string) $request->query('batch_activation_run_id', '')),
            // 批量 BMPA 批次号筛选（run_id 追溯）
            'batch_bmpa_run_id' => trim((string) $request->query('batch_bmpa_run_id', '')),
            // 只看最近 24 小时批量“标记支付并生效(BMPA)”过的订单（可治理追踪）
            'batch_mark_paid_and_activate_24h' => (string) $request->query('batch_mark_paid_and_activate_24h', ''),
            // 只看最近 24 小时批量“仅标记为已生效”过的订单（可治理追踪）
            'batch_mark_activated_24h' => (string) $request->query('batch_mark_activated_24h', ''),
            // 只看“对账不一致”的订单（粗版）：meta.payment_summary.total_amount 与 paid_amount 不一致
            'reconcile_mismatch' => (string) $request->query('reconcile_mismatch', ''),
            // 支付回执筛选：has（有回执）/none（无回执（广义））
            'receipt_status' => trim((string) $request->query('receipt_status', '')),
            // 退款轨迹筛选：has（有退款）/none（无退款）
            'refund_status' => trim((string) $request->query('refund_status', '')),
            // 退款数据不一致（可治理）：基于 refund_summary.total_amount 与 paid_amount 对比
            'refund_inconsistent' => (string) $request->query('refund_inconsistent', ''),
            // 线索联动：用于从开通线索跳转查看关联订单
            'lead_id' => trim((string) $request->query('lead_id', '')),
            // 创建时间范围（用于“趋势→集合”跳转与运营筛选）
            'created_from' => trim((string) $request->query('created_from', '')),
            'created_to' => trim((string) $request->query('created_to', '')),

            // 真正可 BMPA 处理集合（与仪表盘「可BMPA处理」口径对齐）：
            // - pending + unpaid
            // - 排除：续费缺订阅（order_type=renewal 且 site_subscription_id 为空）
            // - 排除：存在退款轨迹（refund_total > 0）
            'bmpa_processable_only' => (string) $request->query('bmpa_processable_only', ''),
        ];


        $includeMeta = (string) $request->query('include_meta', '') === '1';

        $query = $this->applyFilters(
            PlatformOrder::query()->with(['merchant', 'plan', 'siteSubscription']),
            $filters
        )->orderBy('id');

        $filename = 'platform_orders_' . now()->format('Ymd_His') . '.csv';

        return response()->streamDownload(function () use ($query, $includeMeta) {
            $out = fopen('php://output', 'w');

            // UTF-8 BOM，避免 Excel 打开中文乱码
            fwrite($out, "\xEF\xBB\xBF");

            $headers = [
                'ID',
                '订单号',
                '线索ID',
                '站点',
                '套餐',
                '订单类型',
                '订单状态',
                '支付状态',
                '应付金额',
                '已付金额',
                '下单时间',
                '支付时间',
                '生效时间',
                '同步状态',
                '订阅ID',
                '订阅号',
                '订阅到期',
                '同步时间',
                '同步失败原因',
                '同步失败时间',
                'BMPA失败原因',
                'BMPA失败时间',
                '最近批量标记支付并生效时间',
                '最近批量标记支付并生效管理员',
                '最近批量生效时间',
                '最近批量生效管理员',
                '支付回执数',
                '最近回执时间',
                '最近回执金额',
                '最近回执渠道',
                '退款记录数',
                '最近退款时间',
                '最近退款金额',
                '最近退款渠道',
                '退款总额',
                '回执总额',
                '对账差额',
                '对账不一致',
                '退款不一致',
            ];

            if ($includeMeta) {
                $headers[] = '原始meta(JSON)';
            }

            fputcsv($out, $headers);

            $query->chunkById(500, function ($orders) use ($out, $includeMeta) {
                foreach ($orders as $order) {
                    $syncedId = (int) data_get($order->meta, 'subscription_activation.subscription_id', 0);
                    $syncErr = (string) (data_get($order->meta, 'subscription_activation_error.message') ?? '');

                    if ($syncedId > 0) {
                        $syncStatus = '已同步';
                    } elseif ($syncErr !== '') {
                        $syncStatus = '同步失败';
                    } else {
                        $syncStatus = '未同步';
                    }

                    $receipts = (array) (data_get($order->meta, 'payment_receipts', []) ?? []);
                    $receiptSummaryCount = data_get($order->meta, 'payment_summary.count');
                    $receiptCount = $receiptSummaryCount !== null ? (int) $receiptSummaryCount : count($receipts);
                    $latestReceipt = count($receipts) > 0 ? end($receipts) : null;

                    $refunds = (array) (data_get($order->meta, 'refund_receipts', []) ?? []);
                    $refundSummaryCount = data_get($order->meta, 'refund_summary.count');
                    $refundCount = $refundSummaryCount !== null ? (int) $refundSummaryCount : count($refunds);
                    $latestRefund = count($refunds) > 0 ? end($refunds) : null;

                    $row = [
                        $order->id,
                        $order->order_no,
                        (int) (data_get($order->meta, 'platform_lead_id') ?? 0),
                        $order->merchant?->name ?? '',
                        $order->plan_name ?: ($order->plan?->name ?? ''),
                        $order->order_type,
                        $order->status,
                        $order->payment_status,
                        (float) $order->payable_amount,
                        (float) $order->paid_amount,
                        optional($order->placed_at)->format('Y-m-d H:i:s') ?: '',
                        optional($order->paid_at)->format('Y-m-d H:i:s') ?: '',
                        optional($order->activated_at)->format('Y-m-d H:i:s') ?: '',
                        $syncStatus,
                        (int) ($order->site_subscription_id ?? 0),
                        $order->siteSubscription?->subscription_no ?: '',
                        optional($order->siteSubscription?->ends_at)->format('Y-m-d H:i:s') ?: '',
                        (string) (data_get($order->meta, 'subscription_activation.synced_at') ?? ''),
                        $syncErr,
                        (string) (data_get($order->meta, 'subscription_activation_error.at') ?? ''),
                        (string) (data_get($order->meta, 'batch_mark_paid_and_activate_error.message') ?? ''),
                        (string) (data_get($order->meta, 'batch_mark_paid_and_activate_error.at') ?? ''),
                        (string) (data_get($order->meta, 'batch_mark_paid_and_activate.at') ?? ''),
                        (string) (data_get($order->meta, 'batch_mark_paid_and_activate.admin_id') ?? ''),
                        (string) (data_get($order->meta, 'batch_mark_activated.at') ?? ''),
                        (string) (data_get($order->meta, 'batch_mark_activated.admin_id') ?? ''),
                        $receiptCount,
                        (string) (data_get($latestReceipt, 'paid_at') ?? ''),
                        (float) (data_get($latestReceipt, 'amount') ?? 0),
                        (string) (data_get($latestReceipt, 'channel') ?? ''),
                        $refundCount,
                        (string) (data_get($latestRefund, 'refunded_at') ?? ''),
                        (float) (data_get($latestRefund, 'amount') ?? 0),
                        (string) (data_get($latestRefund, 'channel') ?? ''),
                        (float) $order->refundTotal(),
                        (float) $order->receiptTotal(),
                        (float) $order->receiptTotal() - (float) ($order->paid_amount ?? 0),
                        ((bool) $order->isReconcileMismatch()) ? '1' : '0',
                        ((bool) $order->isRefundInconsistent()) ? '1' : '0',
                    ];

                    if ($includeMeta) {
                        $row[] = json_encode($order->meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                    }

                    fputcsv($out, $row);
                }
            });

            fclose($out);
        }, $filename, [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function batchActivateSubscriptions(Request $request, SubscriptionActivationService $service): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        // 支持两种 scope：
        // - scope=filtered：只处理当前筛选范围内的订单（更安全，默认）
        // - scope=all：处理全部订单（谨慎）
        $scope = (string) $request->input('scope', 'filtered');

        $filters = [
            'status' => trim((string) $request->input('status', '')),
            'payment_status' => trim((string) $request->input('payment_status', '')),
            'merchant_id' => trim((string) $request->input('merchant_id', '')),
            'plan_id' => trim((string) $request->input('plan_id', '')),
            'site_subscription_id' => trim((string) $request->input('site_subscription_id', '')),
            'fail_only' => (string) $request->input('fail_only', ''),
            'bmpa_failed_only' => (string) $request->input('bmpa_failed_only', ''),
            'synced_only' => (string) $request->input('synced_only', ''),
            'sync_status' => trim((string) $request->input('sync_status', '')),
            'keyword' => trim((string) $request->input('keyword', '')),
            'lead_id' => trim((string) $request->input('lead_id', '')),
            'sync_error_keyword' => trim((string) $request->input('sync_error_keyword', '')),
            'bmpa_error_keyword' => trim((string) $request->input('bmpa_error_keyword', '')),
            'syncable_only' => (string) $request->input('syncable_only', ''),
            'renewal_missing_subscription' => (string) $request->input('renewal_missing_subscription', ''),
            'batch_synced_24h' => (string) $request->input('batch_synced_24h', ''),
            'batch_activation_run_id' => trim((string) $request->input('batch_activation_run_id', '')),
            // 与列表页筛选保持一致（可治理）：用于在批量操作后仍能回到同一口径
            'batch_mark_paid_and_activate_24h' => (string) $request->input('batch_mark_paid_and_activate_24h', ''),
            'batch_mark_activated_24h' => (string) $request->input('batch_mark_activated_24h', ''),
            'reconcile_mismatch' => (string) $request->input('reconcile_mismatch', ''),
            // receipt_status：批量工具输入口径与列表筛选保持一致；none 表示“无回执（广义）”
            'receipt_status' => trim((string) $request->input('receipt_status', '')),
            'refund_status' => trim((string) $request->input('refund_status', '')),
            'refund_inconsistent' => (string) $request->input('refund_inconsistent', ''),
        ];

        // 防误操作（治理优先）：续费单未绑定订阅属于高风险治理集合，不允许直接批量同步。
        if ($scope === 'filtered' && ((string) ($filters['renewal_missing_subscription'] ?? '') === '1')) {
            return redirect()->back()->with('warning', '当前筛选为「续费单未绑定订阅」治理集合。为避免带病推进，请先完成订阅绑定/修复关联后再批量同步订阅。');
        }

        // 防误操作：批量同步默认要求先勾选“只看可同步”，避免无意识扩大处理范围
        if ($scope === 'filtered' && ($filters['syncable_only'] ?? '') !== '1') {
            return redirect()->back()->with('warning', '为避免误操作，请先在筛选条件中勾选「只看可同步」，再执行批量同步订阅。');
        }

        // 防误操作（口径一致）：当已勾选 syncable_only=1 时，不允许叠加互斥的“同步状态/失败原因/已同步”筛选
        // 说明：syncable_only=1 的语义已统一为 unsynced（未同步且非失败）。若仍保留 sync_status=failed/synced、synced_only=1 或失败原因关键词，
        // 会导致集合为空或语义混乱；这里直接 warning 阻断，避免运营误解。
        if ($scope === 'filtered' && ($filters['syncable_only'] ?? '') === '1') {
            $syncStatus = (string) ($filters['sync_status'] ?? '');
            if ($syncStatus !== '' && $syncStatus !== 'unsynced') {
                return redirect()->back()->with('warning', '当前已勾选「只看可同步」，但同步状态筛选不是「未同步」。请先切回 sync_status=unsynced（或清空同步状态筛选）后再执行批量同步。');
            }
            if ((string) ($filters['synced_only'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前已勾选「只看可同步」，但又勾选了「只看已同步」：两者语义互斥。请先取消只看已同步后再执行批量同步。');
            }
            if ((string) ($filters['fail_only'] ?? '') === '1' || trim((string) ($filters['sync_error_keyword'] ?? '')) !== '') {
                return redirect()->back()->with('warning', '当前筛选包含「同步失败/失败原因」。请先治理失败原因或切回未同步集合，再执行批量同步。');
            }
        }

        // 防误操作（治理优先）：当筛选集合同时命中“对账不一致/退款不一致”时，不允许直接批量同步，避免把“带病订单”同步到订阅
        if ($scope === 'filtered'
            && ($filters['syncable_only'] ?? '') === '1'
            && ((string) ($filters['reconcile_mismatch'] ?? '') === '1' || (string) ($filters['refund_inconsistent'] ?? '') === '1')) {
            return redirect()->back()->with('warning', '当前筛选集合包含「对账不一致/退款不一致」订单，为避免带病同步，请先完成金额/状态治理（补回执/核对退款/修正状态）后再批量同步订阅。');
        }

        // 防误操作（退款治理优先）：当用户显式筛选「有退款」时，禁止直接批量同步
        if ($scope === 'filtered'
            && ($filters['syncable_only'] ?? '') === '1'
            && ((string) ($filters['refund_status'] ?? '') === 'has')) {
            return redirect()->back()->with('warning', '当前筛选为「有退款」订单集合。为避免带退款订单直接同步订阅，请先完成退款治理（核对退款回执/修正状态）后再批量同步订阅。');
        }

        // 防误操作（回执治理优先）：当用户显式筛选「无回执（广义）」时，禁止直接批量同步
        // 原因：已支付/已生效但无回执证据的订单属于收费闭环缺口，应先补齐回执留痕（可治理、可对账）再同步订阅。
        if ($scope === 'filtered'
            && ($filters['syncable_only'] ?? '') === '1'
            && ((string) ($filters['receipt_status'] ?? '') === 'none')) {
            return redirect()->back()->with('warning', '当前筛选为「无回执（广义）」订单集合。为保证收费闭环可治理，请先补齐支付回执留痕后再批量同步订阅。');
        }

        // 防误操作（口径一致）：当用户显式传入了 status/payment_status 时，要求口径至少锁定「已支付+已生效」
        // 说明：订阅详情页的批量同步入口会带 site_subscription_id + syncable_only=1，但未必显式带 status/payment_status。
        // 这里采用“仅在显式传参时校验”的策略，避免误伤订阅详情页的一键批量同步。
        $hasExplicitStatusFilters = ((string) ($filters['payment_status'] ?? '') !== '') || ((string) ($filters['status'] ?? '') !== '');
        if ($scope === 'filtered' && $hasExplicitStatusFilters) {
            if (($filters['payment_status'] ?? '') !== 'paid' || ($filters['status'] ?? '') !== 'activated') {
                return redirect()->back()->with('warning', '为避免误操作，请先在筛选条件中锁定「支付状态=已支付」且「订单状态=已生效」，再执行批量同步订阅。');
            }
        }

        // 防误操作：scope=all 需要二次确认
        if ($scope === 'all' && (string) $request->input('confirm', '') !== 'YES') {
            return redirect()->back()->with('warning', '为避免误操作，执行全量批量同步前请在确认框输入 YES。');
        }

        $query = PlatformOrder::query();

        if ($scope === 'filtered') {
            $query = $this->applyFilters($query, $filters);
        }

        // 只处理“可同步”的订单（双保险，避免误操作）
        // 口径需与 applyFilters(syncable_only=1) 一致：
        // - 已支付 + 已生效
        // - 未同步（subscription_activation.subscription_id 为空）
        // - 非失败（subscription_activation_error.message 为空）
        // - 排除：续费但未绑定订阅（脏数据，避免串单风险）
        $query = $query
            ->where('payment_status', 'paid')
            ->where('status', 'activated')
            ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation.subscription_id') IS NULL")
            ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NULL")
            ->where(function (Builder $q) {
                $q->where('order_type', '!=', 'renewal')
                    ->orWhereNotNull('site_subscription_id');
            });

        $limit = (int) $request->input('limit', 50);
        $limit = max(1, min(500, $limit));

        $matchedTotal = (clone $query)->count();

        // 默认按最新订单优先处理：避免 seed/demo 数据干扰测试，同时也更符合“先处理新问题”的运营直觉
        $orders = $query->orderByDesc('id')->limit($limit)->get(['id']);
        $processed = $orders->count();

        $success = 0;
        $failed = 0;

        // 筛选摘要：用于审计记录（避免每条订单都手写拼接，且便于追溯本次批量处理口径）
        $filterSummaryParts = [];
        foreach ($filters as $k => $v) {
            if ((string) $v !== '') {
                $filterSummaryParts[] = $k . '=' . (string) $v;
            }
        }
        $filterSummary = implode('&', $filterSummaryParts);

        // 队列化（M3 可运维化第一步）：先把匹配到的订单 ID 列表投递为一个 Job，避免请求超时。
        // 注意：当前阶段仍由 Job 内逐条写 meta 与错误原因；后续可再升级为分片 Job + 结果聚合。
        $orderIds = $orders->pluck('id')->map(fn ($id) => (int) $id)->values()->all();

        // run_id：用于把一次批量执行关联起来，便于后续追溯/筛选/可观测。
        // 说明：run_id 在投递阶段就生成，这样运营在页面成功提示里即可复制 run_id 并进入批次复盘页。
        $runId = 'BAS' . now()->format('YmdHis') . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT);

        // 幂等/防抖（最小实现）：避免运营短时间内重复点击导致重复投递同一批次。
        // 说明：这里做“短 TTL 的一次性锁”，不引入新表；后续可演进为批次表 + 幂等 key。
        // key 口径：scope + filters + ids + limit（同一集合的重复点击会被拦截）。
        $lockKey = \App\Support\BatchDispatchLock::makeKey(
            'admin:bas:dispatch',
            $scope,
            (string) $filterSummary,
            $orderIds,
            (int) $limit,
        );

        if (! \App\Support\BatchDispatchLock::acquire(
            'admin:bas:dispatch',
            $scope,
            (string) $filterSummary,
            $orderIds,
            (int) $limit,
            60,
            (string) $runId,
        )) {
            $existing = (string) (\App\Support\BatchDispatchLock::getExistingValue($lockKey) ?? '');
            $existing = trim($existing);

            $flash = \App\Support\BatchDispatchWarning::build('BAS ', $existing, 'bas');

            return \App\Support\AdminFlash::apply(redirect()->back(), $flash);
        }

        \App\Jobs\BatchActivateSubscriptionsJob::dispatch(
            $orderIds,
            (int) $admin->id,
            $scope,
            (string) $filterSummary,
            (int) $limit,
            (int) $matchedTotal,
            (int) $processed,
            (string) $runId,
        );

        $flash = \App\Support\AdminFlash::success(
            '批量同步订阅任务已提交到队列：命中 ' . $matchedTotal . ' 条，本次处理 ' . $processed . ' 条（limit=' . $limit . '，run_id=' . $runId . '）。',
            '/admin/platform-batches/show?type=bas&run_id=' . urlencode((string) $runId),
            '进入批次复盘',
        );

        return \App\Support\AdminFlash::apply(redirect()->back(), $flash);
    }

    public function batchMarkPaidAndActivate(Request $request, SubscriptionActivationService $service): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        // 支持两种 scope：filtered / all
        $scope = (string) $request->input('scope', 'filtered');

        $filters = [
            'status' => trim((string) $request->input('status', '')),
            'payment_status' => trim((string) $request->input('payment_status', '')),
            'merchant_id' => trim((string) $request->input('merchant_id', '')),
            'plan_id' => trim((string) $request->input('plan_id', '')),
            'site_subscription_id' => trim((string) $request->input('site_subscription_id', '')),
            'fail_only' => (string) $request->input('fail_only', ''),
            'bmpa_failed_only' => (string) $request->input('bmpa_failed_only', ''),
            'synced_only' => (string) $request->input('synced_only', ''),
            'sync_status' => trim((string) $request->input('sync_status', '')),
            'keyword' => trim((string) $request->input('keyword', '')),
            'lead_id' => trim((string) $request->input('lead_id', '')),
            'sync_error_keyword' => trim((string) $request->input('sync_error_keyword', '')),
            'bmpa_error_keyword' => trim((string) $request->input('bmpa_error_keyword', '')),
            'syncable_only' => (string) $request->input('syncable_only', ''),
            'renewal_missing_subscription' => (string) $request->input('renewal_missing_subscription', ''),
            'batch_synced_24h' => (string) $request->input('batch_synced_24h', ''),
            'batch_mark_paid_and_activate_24h' => (string) $request->input('batch_mark_paid_and_activate_24h', ''),
            'batch_mark_activated_24h' => (string) $request->input('batch_mark_activated_24h', ''),
            'reconcile_mismatch' => (string) $request->input('reconcile_mismatch', ''),
            // receipt_status：批量工具输入口径与列表筛选保持一致；none 表示“无回执（广义）”
            'receipt_status' => trim((string) $request->input('receipt_status', '')),
            'refund_status' => trim((string) $request->input('refund_status', '')),
            'refund_inconsistent' => (string) $request->input('refund_inconsistent', ''),
        ];

        // 防误操作（治理优先）：续费单未绑定订阅属于高风险治理集合，不允许执行 BMPA（会产生大量失败标记，掩盖问题）。
        if ($scope === 'filtered' && ((string) ($filters['renewal_missing_subscription'] ?? '') === '1')) {
            return redirect()->back()->with('warning', '当前筛选为「续费单未绑定订阅」治理集合。为避免带病推进，请先完成订阅绑定/修复关联后再执行 BMPA。');
        }

        // 防误操作：批量“标记支付并生效”默认要求当前筛选口径为「待处理(pending) + 未支付(unpaid)」
        if ($scope === 'filtered') {
            if (($filters['status'] ?? '') !== 'pending' || ($filters['payment_status'] ?? '') !== 'unpaid') {
                return redirect()->back()->with('warning', '为避免误操作，请先筛选「订单状态=待处理」且「支付状态=未支付」，再执行批量标记支付并生效。');
            }

            // 互斥筛选阻断：BMPA 面向“待处理+未支付”推进链路；若叠加同步治理筛选，语义会混乱，且可能误伤治理集合。
            $hasSyncGovernanceFilters = ((string) ($filters['syncable_only'] ?? '') === '1')
                || ((string) ($filters['synced_only'] ?? '') === '1')
                || (trim((string) ($filters['sync_status'] ?? '')) !== '')
                || ((string) ($filters['fail_only'] ?? '') === '1')
                || (trim((string) ($filters['sync_error_keyword'] ?? '')) !== '');

            if ($hasSyncGovernanceFilters) {
                return redirect()->back()->with('warning', '当前筛选包含「订阅同步治理」相关条件（同步状态/同步失败/失败原因/只看可同步等）。本动作仅用于推进待处理未支付订单，请先清空同步治理筛选后再执行。');
            }

            // 治理优先：若当前筛选包含「有退款」集合，则不允许直接批量推进
            if ((string) ($filters['refund_status'] ?? '') === 'has') {
                return redirect()->back()->with('warning', '当前筛选为「有退款」订单集合。为避免带退款订单被批量推进，请先完成退款治理（核对退款回执/修正状态）后再执行批量标记支付并生效。');
            }

            // 治理优先：若当前筛选已包含“对账不一致/退款不一致”等治理集合，则先治理再执行批量推进
            if ((string) ($filters['reconcile_mismatch'] ?? '') === '1' || (string) ($filters['refund_inconsistent'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前筛选包含「对账不一致/退款不一致」治理集合，请先完成回执/退款治理后，再执行批量标记支付并生效。');
            }
        }

        // 防误操作：scope=all 需要二次确认
        if ($scope === 'all' && (string) $request->input('confirm', '') !== 'YES') {
            return redirect()->back()->with('warning', '为避免误操作，执行全量批量标记支付并生效前请在确认框输入 YES。');
        }

        $query = PlatformOrder::query();
        if ($scope === 'filtered') {
            $query = $this->applyFilters($query, $filters);
        }

        // 双保险：只处理 pending + unpaid
        $query = $query->where('status', 'pending')->where('payment_status', 'unpaid');

        $limit = (int) $request->input('limit', 50);
        $limit = max(1, min(500, $limit));

        $matchedTotal = (clone $query)->count();
        $orders = $query->orderByDesc('id')->limit($limit)->get(['id']);
        $processed = $orders->count();

        // 批次号：用于把一次批量执行关联起来，便于后续追溯/筛选/可观测。
        $runId = 'BMPA' . now()->format('YmdHis') . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT);

        // 筛选摘要：用于审计记录（便于追溯本次批量处理口径）
        $filterSummaryParts = [];
        foreach ($filters as $k => $v) {
            if ((string) $v !== '') {
                $filterSummaryParts[] = $k . '=' . (string) $v;
            }
        }
        $filterSummary = implode('&', $filterSummaryParts);

        // 队列化（M3 可运维化第二步）：把批量 BMPA 投递为一个 Job，避免请求超时，并对齐 BAS 的批次可观测模式。
        $orderIds = $orders->pluck('id')->map(fn ($id) => (int) $id)->values()->all();

        // 幂等/防抖（最小实现）：避免运营短时间内重复点击导致重复投递同一批次。
        // 说明：这里做“短 TTL 的一次性锁”，不引入新表；后续可演进为批次表 + 幂等 key。
        // key 口径：scope + filters + ids + limit（同一集合的重复点击会被拦截）。
        $lockKey = \App\Support\BatchDispatchLock::makeKey(
            'admin:bmpa:dispatch',
            $scope,
            (string) $filterSummary,
            $orderIds,
            (int) $limit,
        );

        if (! \App\Support\BatchDispatchLock::acquire(
            'admin:bmpa:dispatch',
            $scope,
            (string) $filterSummary,
            $orderIds,
            (int) $limit,
            60,
            (string) $runId,
        )) {
            $existing = (string) (\App\Support\BatchDispatchLock::getExistingValue($lockKey) ?? '');
            $existing = trim($existing);

            $flash = \App\Support\BatchDispatchWarning::build('BMPA ', $existing, 'bmpa');

            return \App\Support\AdminFlash::apply(redirect()->back(), $flash);
        }

        \App\Jobs\BatchMarkPaidAndActivateJob::dispatch(
            $orderIds,
            (int) $admin->id,
            $scope,
            (string) $filterSummary,
            (int) $limit,
            (int) $matchedTotal,
            (int) $processed,
            (string) $runId,
        );

        $flash = \App\Support\AdminFlash::success(
            '批量 BMPA 任务已提交到队列：命中 ' . $matchedTotal . ' 条，本次处理 ' . $processed . ' 条（limit=' . $limit . '，run_id=' . $runId . '）。',
            '/admin/platform-batches/show?type=bmpa&run_id=' . urlencode((string) $runId),
            '进入批次复盘',
        );

        return \App\Support\AdminFlash::apply(redirect()->back(), $flash);
    }

    public function batchMarkActivated(Request $request): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        // 支持两种 scope：
        // - scope=filtered：只处理当前筛选范围内的订单（更安全，默认）
        // - scope=all：处理全部订单（谨慎）
        $scope = (string) $request->input('scope', 'filtered');

        $filters = [
            'status' => trim((string) $request->input('status', '')),
            'payment_status' => trim((string) $request->input('payment_status', '')),
            'merchant_id' => trim((string) $request->input('merchant_id', '')),
            'plan_id' => trim((string) $request->input('plan_id', '')),
            'site_subscription_id' => trim((string) $request->input('site_subscription_id', '')),
            'fail_only' => (string) $request->input('fail_only', ''),
            'bmpa_failed_only' => (string) $request->input('bmpa_failed_only', ''),
            'synced_only' => (string) $request->input('synced_only', ''),
            'sync_status' => trim((string) $request->input('sync_status', '')),
            'keyword' => trim((string) $request->input('keyword', '')),
            'lead_id' => trim((string) $request->input('lead_id', '')),
            'sync_error_keyword' => trim((string) $request->input('sync_error_keyword', '')),
            'bmpa_error_keyword' => trim((string) $request->input('bmpa_error_keyword', '')),
            'syncable_only' => (string) $request->input('syncable_only', ''),
            'renewal_missing_subscription' => (string) $request->input('renewal_missing_subscription', ''),
            'batch_synced_24h' => (string) $request->input('batch_synced_24h', ''),
            'batch_activation_run_id' => trim((string) $request->input('batch_activation_run_id', '')),
            // 与列表页筛选保持一致（可治理）：用于在批量操作后仍能回到同一口径
            'batch_mark_paid_and_activate_24h' => (string) $request->input('batch_mark_paid_and_activate_24h', ''),
            'batch_mark_activated_24h' => (string) $request->input('batch_mark_activated_24h', ''),
            'reconcile_mismatch' => (string) $request->input('reconcile_mismatch', ''),
            // receipt_status：批量工具输入口径与列表筛选保持一致；none 表示“无回执（广义）”
            'receipt_status' => trim((string) $request->input('receipt_status', '')),
            'refund_status' => trim((string) $request->input('refund_status', '')),
            'refund_inconsistent' => (string) $request->input('refund_inconsistent', ''),
        ];

        // 防误操作（治理优先）：续费单未绑定订阅属于高风险治理集合，不允许直接批量仅标记为已生效。
        if ($scope === 'filtered' && ((string) ($filters['renewal_missing_subscription'] ?? '') === '1')) {
            return redirect()->back()->with('warning', '当前筛选为「续费单未绑定订阅」治理集合。为避免带病推进，请先完成订阅绑定/修复关联后再批量仅标记为已生效。');
        }

        // 防误操作：批量“仅标记为已生效”默认要求当前筛选口径为「已支付 + 待处理(pending) + 未同步(unsynced)」
        if ($scope === 'filtered') {
            if (($filters['payment_status'] ?? '') !== 'paid' || ($filters['status'] ?? '') !== 'pending') {
                return redirect()->back()->with('warning', '为避免误操作，请先筛选「支付状态=已支付」且「订单状态=待处理」，再执行批量仅标记为已生效。');
            }

            if (($filters['sync_status'] ?? '') !== 'unsynced') {
                return redirect()->back()->with('warning', '为避免把同步失败/已同步等异常单混入，请先锁定「同步状态=未同步(sync_status=unsynced)」（建议用快捷筛选「待生效」）再执行批量仅标记为已生效。');
            }

            // 互斥筛选阻断：避免“待生效”批量动作在其它治理集合上误触（或误以为会命中失败单/已同步单/可同步单）。
            if ((string) ($filters['fail_only'] ?? '') === '1' || trim((string) ($filters['sync_error_keyword'] ?? '')) !== '') {
                return redirect()->back()->with('warning', '当前筛选包含「同步失败/失败原因」治理集合：与“待生效(unsynced)”互斥。请先切回待生效集合后再执行批量仅标记为已生效。');
            }
            if ((string) ($filters['synced_only'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前已勾选「只看已同步」：该集合与“待生效(unsynced)”互斥。请先取消该筛选后再执行批量仅标记为已生效。');
            }
            if ((string) ($filters['syncable_only'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前已勾选「只看可同步」：该集合语义为“已生效(activated)+未同步”，与本动作处理的“待处理(pending)”互斥。请先取消只看可同步后再执行。');
            }

            // 治理优先：当用户显式筛选「无回执（广义）」时，不允许直接批量生效
            if ((string) ($filters['receipt_status'] ?? '') === 'none') {
                return redirect()->back()->with('warning', '当前筛选为「无回执（广义）」订单集合。为保证收费闭环可治理，请先补齐支付回执留痕后再执行批量仅标记为已生效。');
            }

            // 治理优先：当用户显式筛选「有退款」时，不允许直接批量生效
            if ((string) ($filters['refund_status'] ?? '') === 'has') {
                return redirect()->back()->with('warning', '当前筛选为「有退款」订单集合。为避免带退款订单直接批量生效，请先完成退款治理（核对退款回执/修正状态）后再执行批量仅标记为已生效。');
            }

            // 治理优先：当筛选集合命中“对账不一致/退款不一致”时，不允许直接批量生效
            if ((string) ($filters['reconcile_mismatch'] ?? '') === '1' || (string) ($filters['refund_inconsistent'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前筛选集合包含「对账不一致/退款不一致」订单，为避免带病推进，请先完成金额/状态治理（补回执/核对退款/修正状态）后再执行批量仅标记为已生效。');
            }
        }

        // 防误操作：scope=all 需要二次确认
        if ($scope === 'all' && (string) $request->input('confirm', '') !== 'YES') {
            return redirect()->back()->with('warning', '为避免误操作，执行全量批量生效前请在确认框输入 YES。');
        }

        $query = PlatformOrder::query();

        if ($scope === 'filtered') {
            $query = $this->applyFilters($query, $filters);
        }

        // 只处理“已支付 + 待处理”的订单（双保险）
        $query = $query
            ->where('payment_status', 'paid')
            ->where('status', 'pending');

        $limit = (int) $request->input('limit', 50);
        $limit = max(1, min(500, $limit));

        $matchedTotal = (clone $query)->count();

        $orders = $query->orderByDesc('id')->limit($limit)->get(['id']);
        $processed = $orders->count();

        $success = 0;
        $failed = 0;
        $nowStr = now()->toDateTimeString();

        // 筛选摘要：用于审计记录（便于追溯本次批量处理口径）
        $filterSummaryParts = [];
        foreach ($filters as $k => $v) {
            if ((string) $v !== '') {
                $filterSummaryParts[] = $k . '=' . (string) $v;
            }
        }
        $filterSummary = implode('&', $filterSummaryParts);

        foreach ($orders as $row) {
            $order = PlatformOrder::query()->find($row->id);
            if (! $order) {
                continue;
            }

            // 再次防御：仅推进 pending
            if ($order->payment_status !== 'paid' || $order->status !== 'pending') {
                continue;
            }

            // 治理优先：续费单必须绑定订阅（兼容历史脏数据/手工改库等场景）
            if ((string) ($order->order_type ?? '') === 'renewal' && ! (int) ($order->site_subscription_id ?? 0)) {
                $failed++;

                $reason = '续费单未绑定订阅（site_subscription_id 为空），不允许批量仅标记为已生效。';
                $failedReasonCounts[$reason] = ($failedReasonCounts[$reason] ?? 0) + 1;

                $meta = (array) ($order->meta ?? []);
                data_set($meta, 'batch_mark_activated_error', [
                    'message' => $reason,
                    'at' => $nowStr,
                    'admin_id' => $admin->id,
                    'scope' => $scope,
                    'filters' => $filterSummary,
                ]);
                $order->meta = $meta;
                $order->save();

                continue;
            }

            $order->status = 'activated';
            $order->activated_at = $order->activated_at ?: now();

            $meta = (array) ($order->meta ?? []);
            $audit = (array) (data_get($meta, 'audit', []) ?? []);
            $audit[] = [
                'action' => 'batch_mark_activated',
                'scope' => $scope,
                'at' => $nowStr,
                'admin_id' => $admin->id,
                'filters' => $filterSummary,
                'note' => '批量仅标记为已生效（limit=' . $limit . ', matched=' . $matchedTotal . ', processed=' . $processed . ')',
            ];
            data_set($meta, 'audit', $audit);

            // 便于筛选/统计：记录最近一次批量生效信息（扁平字段）
            data_set($meta, 'batch_mark_activated', [
                'at' => $nowStr,
                'admin_id' => $admin->id,
                'scope' => $scope,
            ]);

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

            $success++;
        }

        $msg = '批量仅标记为已生效完成：成功 ' . $success . ' 条，失败 ' . $failed . ' 条（命中 ' . $matchedTotal . ' 条，本次处理 ' . $processed . ' 条，limit=' . $limit . '）';

        if ($failed > 0 && count($failedReasonCounts) > 0) {
            arsort($failedReasonCounts);
            $top = array_slice($failedReasonCounts, 0, 3, true);
            $topText = collect($top)->map(function ($cnt, $reason) {
                $reason = mb_substr((string) $reason, 0, 60);
                return $reason . '（' . $cnt . '）';
            })->implode('；');

            $msg .= '；失败原因Top：' . $topText;
        }

        $flash = \App\Support\AdminFlash::success(
            $msg,
            // 批量“仅标记为已生效”无 run_id（同步执行），这里提供一个“查看本次批量结果”的快捷入口：最近24小时批量生效集合。
            '/admin/platform-orders?batch_mark_activated_24h=1#filters',
            '查看本次批量结果',
        );

        return \App\Support\AdminFlash::apply(redirect()->back(), $flash);
    }

    public function clearSyncError(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $meta = (array) ($order->meta ?? []);
        if (! data_get($meta, 'subscription_activation_error')) {
            return redirect()->back()->with('warning', '当前订单暂无同步失败标记，无需清理。');
        }

        data_forget($meta, 'subscription_activation_error');

        // 轻量审计：记录清理动作（不做独立表，先落 meta，便于排查）
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'clear_sync_error',
            'scope' => 'single',
            'at' => now()->toDateTimeString(),
            'admin_id' => $admin->id,
            'note' => '手动点击订单详情【清除同步失败标记】',
        ];
        data_set($meta, 'audit', $audit);

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

        return redirect()->back()->with('success', '已清除该订单的同步失败标记。');
    }

    public function clearBmpaError(Request $request, PlatformOrder $order): RedirectResponse
    {
        $admin = $this->ensurePlatformAdmin($request);

        $meta = (array) ($order->meta ?? []);
        if (! data_get($meta, 'batch_mark_paid_and_activate_error')) {
            return redirect()->back()->with('warning', '当前订单暂无 BMPA 失败标记，无需清理。');
        }

        data_forget($meta, 'batch_mark_paid_and_activate_error');

        // 轻量审计：记录清理动作（不做独立表，先落 meta，便于排查）
        $audit = (array) (data_get($meta, 'audit', []) ?? []);
        $audit[] = [
            'action' => 'clear_bmpa_error',
            'scope' => 'single',
            'at' => now()->toDateTimeString(),
            'admin_id' => $admin->id,
            'note' => '手动点击订单详情【清除 BMPA 失败标记】',
        ];
        data_set($meta, 'audit', $audit);

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

        return redirect()->back()->with('success', '已清除该订单的 BMPA 失败标记。');
    }

    public function clearSyncErrors(Request $request): RedirectResponse
    {
        $this->ensurePlatformAdmin($request);

        // 支持两种模式：
        // - scope=all（默认）：清理所有订单的失败标记（需要 confirm=YES）
        // - scope=filtered：仅清理当前筛选结果命中的订单（更安全）
        $scope = (string) $request->input('scope', 'all');

        // 防误操作：scope=all 需要二次确认
        if ($scope === 'all' && (string) $request->input('confirm', '') !== 'YES') {
            return redirect()->back()->with('warning', '为避免误操作，清除全部失败标记前请在确认框输入 YES。');
        }

        $filters = [
            'status' => trim((string) $request->input('status', '')),
            'payment_status' => trim((string) $request->input('payment_status', '')),
            'merchant_id' => trim((string) $request->input('merchant_id', '')),
            'plan_id' => trim((string) $request->input('plan_id', '')),
            'site_subscription_id' => trim((string) $request->input('site_subscription_id', '')),
            'fail_only' => (string) $request->input('fail_only', ''),
            'bmpa_failed_only' => (string) $request->input('bmpa_failed_only', ''),
            'synced_only' => (string) $request->input('synced_only', ''),
            'sync_status' => trim((string) $request->input('sync_status', '')),
            'keyword' => trim((string) $request->input('keyword', '')),
            'lead_id' => trim((string) $request->input('lead_id', '')),
            'sync_error_keyword' => trim((string) $request->input('sync_error_keyword', '')),
            'bmpa_error_keyword' => trim((string) $request->input('bmpa_error_keyword', '')),
            'syncable_only' => (string) $request->input('syncable_only', ''),
            'renewal_missing_subscription' => (string) $request->input('renewal_missing_subscription', ''),
            'batch_synced_24h' => (string) $request->input('batch_synced_24h', ''),
            'batch_activation_run_id' => trim((string) $request->input('batch_activation_run_id', '')),
            // 与列表页筛选保持一致（可治理）：用于在批量操作后仍能回到同一口径
            'batch_mark_paid_and_activate_24h' => (string) $request->input('batch_mark_paid_and_activate_24h', ''),
            'batch_mark_activated_24h' => (string) $request->input('batch_mark_activated_24h', ''),
            'reconcile_mismatch' => (string) $request->input('reconcile_mismatch', ''),
            // receipt_status：批量工具输入口径与列表筛选保持一致；none 表示“无回执（广义）”
            'receipt_status' => trim((string) $request->input('receipt_status', '')),
            'refund_status' => trim((string) $request->input('refund_status', '')),
            'refund_inconsistent' => (string) $request->input('refund_inconsistent', ''),
        ];

        // 防误操作（后端兜底）：清理“同步失败”标记时，不允许叠加与失败集合互斥的筛选。
        // 说明：列表页 UI 已做按钮禁用，但仍需后端兜底以防绕过 UI 直接 POST。
        if ($scope === 'filtered') {
            $syncStatus = (string) ($filters['sync_status'] ?? '');
            if ($syncStatus !== '' && $syncStatus !== 'failed') {
                return redirect()->back()->with('warning', '当前为「清理同步失败标记」动作：若需要筛选同步状态，请使用 sync_status=failed（或清空该筛选）后再执行。');
            }
            if ((string) ($filters['syncable_only'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前已勾选「只看可同步」：该集合与「同步失败」互斥，请先取消只看可同步或切到失败集合后再清理。');
            }
            if ((string) ($filters['synced_only'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前已勾选「只看已同步」：该集合与「同步失败」治理集合无关，请先取消只看已同步或切到失败集合后再清理。');
            }
        }

        $query = PlatformOrder::query()
            ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NOT NULL");

        if ($scope === 'filtered') {
            $query = $this->applyFilters($query, $filters);
        }

        $orders = $query->get(['id', 'meta']);
        $matched = $orders->count();

        $cleared = 0;
        foreach ($orders as $order) {
            $meta = (array) ($order->meta ?? []);
            if (! data_get($meta, 'subscription_activation_error')) {
                continue;
            }

            data_forget($meta, 'subscription_activation_error');

            // 轻量审计：记录清理动作（不做独立表，先落 meta，便于排查）
            $audit = (array) (data_get($meta, 'audit', []) ?? []);
            $audit[] = [
                'action' => 'clear_sync_error',
                'scope' => $scope,
                'at' => now()->toDateTimeString(),
                'admin_id' => $this->platformAdminId($request),
            ];
            data_set($meta, 'audit', $audit);

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

        $msg = $scope === 'filtered'
            ? '已清除当前筛选范围内的同步失败标记：'
            : '已清除全部订单的同步失败标记：';

        return redirect()->back()->with('success', $msg . $cleared . ' 条（命中 ' . $matched . ' 条）');
    }

    public function clearBmpaErrors(Request $request): RedirectResponse
    {
        $this->ensurePlatformAdmin($request);

        // 支持两种模式：
        // - scope=all（默认）：清理所有订单的 BMPA 失败标记（需要 confirm=YES）
        // - scope=filtered：仅清理当前筛选结果命中的订单（更安全）
        $scope = (string) $request->input('scope', 'all');

        // 防误操作：scope=all 需要二次确认
        if ($scope === 'all' && (string) $request->input('confirm', '') !== 'YES') {
            return redirect()->back()->with('warning', '为避免误操作，清除全部 BMPA 失败标记前请在确认框输入 YES。');
        }

        $filters = [
            'status' => trim((string) $request->input('status', '')),
            'payment_status' => trim((string) $request->input('payment_status', '')),
            'merchant_id' => trim((string) $request->input('merchant_id', '')),
            'plan_id' => trim((string) $request->input('plan_id', '')),
            'site_subscription_id' => trim((string) $request->input('site_subscription_id', '')),
            'fail_only' => (string) $request->input('fail_only', ''),
            'bmpa_failed_only' => (string) $request->input('bmpa_failed_only', ''),
            'synced_only' => (string) $request->input('synced_only', ''),
            'sync_status' => trim((string) $request->input('sync_status', '')),
            'keyword' => trim((string) $request->input('keyword', '')),
            'lead_id' => trim((string) $request->input('lead_id', '')),
            'sync_error_keyword' => trim((string) $request->input('sync_error_keyword', '')),
            'bmpa_error_keyword' => trim((string) $request->input('bmpa_error_keyword', '')),
            'syncable_only' => (string) $request->input('syncable_only', ''),
            'renewal_missing_subscription' => (string) $request->input('renewal_missing_subscription', ''),
            'batch_synced_24h' => (string) $request->input('batch_synced_24h', ''),
            'batch_mark_paid_and_activate_24h' => (string) $request->input('batch_mark_paid_and_activate_24h', ''),
            'batch_mark_activated_24h' => (string) $request->input('batch_mark_activated_24h', ''),
            'reconcile_mismatch' => (string) $request->input('reconcile_mismatch', ''),
            // receipt_status：批量工具输入口径与列表筛选保持一致；none 表示“无回执（广义）”
            'receipt_status' => trim((string) $request->input('receipt_status', '')),
            'refund_status' => trim((string) $request->input('refund_status', '')),
            'refund_inconsistent' => (string) $request->input('refund_inconsistent', ''),
        ];

        // 防误操作（后端兜底）：清理“BMPA 失败”标记时，建议先锁定 BMPA 失败集合，避免误清理。
        // 说明：列表页 UI 已做按钮禁用，但仍需后端兜底以防绕过 UI 直接 POST。
        if ($scope === 'filtered') {
            if ((string) ($filters['syncable_only'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前已勾选「只看可同步」：该集合与「BMPA失败」治理集合无关，请先取消只看可同步或切到 BMPA 失败集合后再清理。');
            }

            if ((string) ($filters['fail_only'] ?? '') === '1' || trim((string) ($filters['sync_error_keyword'] ?? '')) !== '') {
                return redirect()->back()->with('warning', '当前筛选包含「同步失败/失败原因」：该集合用于订阅同步治理，请切到 BMPA 失败集合后再清理 BMPA 失败标记。');
            }

            if (trim((string) ($filters['sync_status'] ?? '')) !== '' || (string) ($filters['synced_only'] ?? '') === '1') {
                return redirect()->back()->with('warning', '当前筛选包含「同步状态/已同步」：该集合用于订阅同步治理，请先清空同步相关筛选后再清理 BMPA 失败标记。');
            }

            $hasBmpaScope = ((string) ($filters['bmpa_failed_only'] ?? '') === '1')
                || (trim((string) ($filters['bmpa_error_keyword'] ?? '')) !== '')
                || ((string) ($filters['batch_mark_paid_and_activate_24h'] ?? '') === '1');

            if (! $hasBmpaScope) {
                return redirect()->back()->with('warning', '为避免误操作，请先筛选「BMPA失败」集合（bmpa_failed_only=1 或失败原因关键词，或勾选 batch_mark_paid_and_activate_24h）后再执行清理。');
            }
        }

        $query = PlatformOrder::query()
            ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') IS NOT NULL");

        if ($scope === 'filtered') {
            $query = $this->applyFilters($query, $filters);
        }

        $orders = $query->get(['id', 'meta']);
        $matched = $orders->count();

        $cleared = 0;
        foreach ($orders as $order) {
            $meta = (array) ($order->meta ?? []);
            if (! data_get($meta, 'batch_mark_paid_and_activate_error')) {
                continue;
            }

            data_forget($meta, 'batch_mark_paid_and_activate_error');

            // 轻量审计：记录清理动作（不做独立表，先落 meta，便于排查）
            $audit = (array) (data_get($meta, 'audit', []) ?? []);
            $audit[] = [
                'action' => 'clear_bmpa_error',
                'scope' => $scope,
                'at' => now()->toDateTimeString(),
                'admin_id' => $this->platformAdminId($request),
            ];
            data_set($meta, 'audit', $audit);

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

        $msg = $scope === 'filtered'
            ? '已清除当前筛选范围内的 BMPA 失败标记：'
            : '已清除全部订单的 BMPA 失败标记：';

        return redirect()->back()->with('success', $msg . $cleared . ' 条（命中 ' . $matched . ' 条）');
    }

    protected function applyFilters(Builder $query, array $filters): Builder
    {
        return $query
            ->when($filters['status'] !== '', fn (Builder $builder) => $builder->where('status', $filters['status']))
            ->when($filters['payment_status'] !== '', fn (Builder $builder) => $builder->where('payment_status', $filters['payment_status']))
            ->when(($filters['merchant_id'] ?? '') !== '', fn (Builder $builder) => $builder->where('merchant_id', (int) $filters['merchant_id']))
            ->when(($filters['plan_id'] ?? '') !== '', fn (Builder $builder) => $builder->where('plan_id', (int) $filters['plan_id']))
            ->when(($filters['site_subscription_id'] ?? '') !== '', fn (Builder $builder) => $builder->where('site_subscription_id', (int) $filters['site_subscription_id']))
            ->when(($filters['fail_only'] ?? '') !== '', function (Builder $builder) {
                // 只看同步失败：meta.subscription_activation_error.message 存在即视为失败
                $builder->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NOT NULL");
            })
            ->when(($filters['bmpa_failed_only'] ?? '') !== '', function (Builder $builder) {
                // 只看批量“标记支付并生效”失败：meta.batch_mark_paid_and_activate_error.message 存在即视为失败
                $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') IS NOT NULL");
            })
            ->when(($filters['bmpa_success_only'] ?? '') === '1', function (Builder $builder) {
                // 只看批量“标记支付并生效”成功：存在 run_id 且 error.message 为空。
                // 用途：从批次复盘页快速进入“本批次成功集合”（便于 spot-check 与抽样复核）。
                $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.run_id') IS NOT NULL")
                    ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') IS NULL");
            })
            ->when(($filters['synced_only'] ?? '') !== '', function (Builder $builder) {
                // 只看已同步：meta.subscription_activation.subscription_id 存在即视为已同步
                $builder->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation.subscription_id') IS NOT NULL");
            })
            ->when(($filters['sync_status'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 同步状态筛选：unsynced / synced / failed
                if (($filters['sync_status'] ?? '') === 'synced') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation.subscription_id') IS NOT NULL")
                        ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NULL");
                } elseif (($filters['sync_status'] ?? '') === 'failed') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NOT NULL");
                } elseif (($filters['sync_status'] ?? '') === 'unsynced') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation.subscription_id') IS NULL")
                        ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NULL");
                }
            })
            ->when(($filters['keyword'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 关键词搜索：订单号 / 站点名称 / 订阅号
                $kw = trim((string) ($filters['keyword'] ?? ''));
                if ($kw === '') {
                    return;
                }

                $builder->where(function (Builder $q) use ($kw) {
                    $q->where('order_no', 'like', '%' . $kw . '%')
                        ->orWhere('order_type', 'like', '%' . $kw . '%')
                        ->orWhere('plan_name', 'like', '%' . $kw . '%')
                        ->orWhereHas('merchant', function (Builder $mq) use ($kw) {
                            $mq->where('name', 'like', '%' . $kw . '%')
                                ->orWhere('slug', 'like', '%' . $kw . '%');
                        })
                        ->orWhereHas('siteSubscription', function (Builder $sq) use ($kw) {
                            $sq->where('subscription_no', 'like', '%' . $kw . '%');
                        });

                    if (ctype_digit($kw)) {
                        $q->orWhere('id', (int) $kw);
                    }
                });
            })
            ->when(($filters['sync_error_keyword'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 同步失败原因关键词：subscription_activation_error.message like
                $kw = trim((string) ($filters['sync_error_keyword'] ?? ''));
                if ($kw === '') {
                    return;
                }

                $driver = $builder->getQuery()->getConnection()->getDriverName();
                if ($driver === 'sqlite') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') LIKE ?", ['%' . $kw . '%']);
                } else {
                    $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.subscription_activation_error.message')) LIKE ?", ['%' . $kw . '%']);
                }
            })
            ->when(($filters['bmpa_error_keyword'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 批量“标记支付并生效”失败原因关键词：batch_mark_paid_and_activate_error.message like
                $kw = trim((string) ($filters['bmpa_error_keyword'] ?? ''));
                if ($kw === '') {
                    return;
                }

                $driver = $builder->getQuery()->getConnection()->getDriverName();
                if ($driver === 'sqlite') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') LIKE ?", ['%' . $kw . '%']);
                } else {
                    $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message')) LIKE ?", ['%' . $kw . '%']);
                }
            })
            ->when(($filters['syncable_only'] ?? '') === '1', function (Builder $builder) {
                // 只看可同步：已支付 + 已生效 + 未同步 + 非失败
                // - 未同步：subscription_activation.subscription_id 为空
                // - 非失败：subscription_activation_error.message 为空
                // 额外治理口径：排除（续费但未绑定订阅）的脏数据，避免误入可同步集合导致串单风险
                $builder->where('payment_status', 'paid')
                    ->where('status', 'activated')
                    ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation.subscription_id') IS NULL")
                    ->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NULL")
                    ->where(function (Builder $q) {
                        $q->where('order_type', '!=', 'renewal')
                            ->orWhereNotNull('site_subscription_id');
                    });
            })
            ->when(($filters['renewal_missing_subscription'] ?? '') !== '', function (Builder $builder) {
                // 只看“续费但未绑定订阅”的脏数据（可治理）
                $builder->where('order_type', 'renewal')
                    ->whereNull('site_subscription_id');
            })
            ->when(($filters['batch_synced_24h'] ?? '') !== '', function (Builder $builder) {
                // 只看最近 24 小时批量同步过的订单（基于 meta.batch_activation.at）
                $since = now()->subHours(24)->format('Y-m-d H:i:s');

                $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_activation.at') IS NOT NULL");

                // sqlite 测试库没有 JSON_UNQUOTE()，需要做兼容
                $driver = $builder->getQuery()->getConnection()->getDriverName();
                if ($driver === 'sqlite') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_activation.at') >= ?", [$since]);
                } else {
                    $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_activation.at')) >= ?", [$since]);
                }
            })
            ->when(($filters['batch_activation_run_id'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 按批次号筛选：用于队列批量同步 run_id 追溯
                $runId = trim((string) ($filters['batch_activation_run_id'] ?? ''));
                if ($runId === '') {
                    return;
                }

                $driver = $builder->getQuery()->getConnection()->getDriverName();
                if ($driver === 'sqlite') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_activation.run_id') = ?", [$runId]);
                } else {
                    $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_activation.run_id')) = ?", [$runId]);
                }
            })
            ->when(($filters['batch_bmpa_run_id'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 按批次号筛选：用于批量 BMPA run_id 追溯
                $runId = trim((string) ($filters['batch_bmpa_run_id'] ?? ''));
                if ($runId === '') {
                    return;
                }

                $driver = $builder->getQuery()->getConnection()->getDriverName();
                if ($driver === 'sqlite') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.run_id') = ?", [$runId]);
                } else {
                    $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.run_id')) = ?", [$runId]);
                }
            })
            ->when(($filters['batch_mark_paid_and_activate_24h'] ?? '') !== '', function (Builder $builder) {
                // 只看最近 24 小时批量“标记支付并生效(BMPA)”过的订单（基于 meta.batch_mark_paid_and_activate.at）
                $since = now()->subHours(24)->format('Y-m-d H:i:s');

                $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.at') IS NOT NULL");

                $driver = $builder->getQuery()->getConnection()->getDriverName();
                if ($driver === 'sqlite') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.at') >= ?", [$since]);
                } else {
                    $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate.at')) >= ?", [$since]);
                }
            })
            ->when(($filters['batch_mark_activated_24h'] ?? '') !== '', function (Builder $builder) {
                // 只看最近 24 小时批量“仅标记为已生效”过的订单（基于 meta.batch_mark_activated.at）
                $since = now()->subHours(24)->format('Y-m-d H:i:s');

                $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_activated.at') IS NOT NULL");

                $driver = $builder->getQuery()->getConnection()->getDriverName();
                if ($driver === 'sqlite') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_activated.at') >= ?", [$since]);
                } else {
                    $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_mark_activated.at')) >= ?", [$since]);
                }
            })
            ->when(($filters['reconcile_mismatch'] ?? '') !== '', function (Builder $builder) {
                // 只看“对账不一致”的订单：支付回执总额 与订单 paid_amount 不一致
                // 口径：优先使用 meta.payment_summary.total_amount；若为空，则回退汇总 meta.payment_receipts[].amount
                // 注意：该筛选需要读取 JSON 字段，MySQL/SQLite 写法略有差异。
                $driver = $builder->getQuery()->getConnection()->getDriverName();

                if ($driver === 'sqlite') {
                    // sqlite 下 JSON_EXTRACT 直接返回标量（数值或字符串），这里用“按分”取整避免浮点误差导致 0.01 边界不稳定
                    // total_cents = (payment_summary.total_amount 存在 ? summary*100 : sum(payment_receipts[].amount)*100)
                    $tol = (float) config('saasshop.amounts.tolerance', 0.01);
                    $tolCents = (int) round($tol * 100);
                    $tolCents = max(1, $tolCents);

                    $builder->whereRaw("ABS(ROUND((CASE WHEN JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NOT NULL THEN CAST(JSON_EXTRACT(meta, '$.payment_summary.total_amount') AS REAL) ELSE (SELECT IFNULL(SUM(CAST(JSON_EXTRACT(value, '$.amount') AS REAL)), 0) FROM json_each(COALESCE(JSON_EXTRACT(meta, '$.payment_receipts'), '[]'))) END) * 100) - ROUND(paid_amount * 100)) >= {$tolCents}");
                } else {
                    // MySQL 下 JSON_EXTRACT 返回 JSON，需要 JSON_UNQUOTE 再 cast；同样按分取整避免浮点误差
                    // total_cents = (payment_summary.total_amount 存在 ? summary*100 : SUM(payment_receipts[].amount)*100)
                    $tol = (float) config('saasshop.amounts.tolerance', 0.01);
                    $tolCents = (int) round($tol * 100);
                    $tolCents = max(1, $tolCents);

                    $builder->whereRaw("ABS(ROUND((CASE WHEN JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NOT NULL THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(meta, '$.payment_summary.total_amount')) AS DECIMAL(12,2)) ELSE (SELECT IFNULL(SUM(j.amount), 0) FROM JSON_TABLE(meta, '$.payment_receipts[*]' COLUMNS(amount DECIMAL(12,2) PATH '$.amount')) j) END) * 100) - ROUND(paid_amount * 100)) >= {$tolCents}");
                }
            })
            ->when(($filters['receipt_status'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 支付回执筛选：
                // - has：有回执（payment_summary.total_amount 存在 或 payment_receipts[0].amount 存在）
                // - none：无回执（广义，payment_summary / payment_receipts 两者都不存在）
                $status = (string) ($filters['receipt_status'] ?? '');

                if ($status === 'has') {
                    $builder->where(function (Builder $q) {
                        $q->whereRaw("JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NOT NULL")
                          ->orWhereRaw("JSON_EXTRACT(meta, '$.payment_receipts[0].amount') IS NOT NULL");
                    });
                } elseif ($status === 'none') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NULL")
                        ->whereRaw("JSON_EXTRACT(meta, '$.payment_receipts[0].amount') IS NULL");
                }
            })
            ->when(($filters['refund_status'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 退款轨迹筛选：
                // - has：有退款（退款总额 > 0）
                // - none：无退款（退款总额 <= 0，或无任何退款字段）
                // 退款总额口径：优先 refund_summary.total_amount；缺省回退汇总 refund_receipts[].amount
                // 说明：这里不再用“字段是否存在”判断 has/none，避免出现 total_amount=0 但被误判为“有退款”。
                $status = (string) ($filters['refund_status'] ?? '');

                $driver = $builder->getQuery()->getConnection()->getDriverName();

                if ($driver === 'sqlite') {
                    $refundTotalExpr = "(CASE WHEN JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL THEN CAST(JSON_EXTRACT(meta, '$.refund_summary.total_amount') AS REAL) ELSE (SELECT IFNULL(SUM(CAST(JSON_EXTRACT(value, '$.amount') AS REAL)), 0) FROM json_each(COALESCE(JSON_EXTRACT(meta, '$.refund_receipts'), '[]'))) END)";
                } else {
                    $refundTotalExpr = "(CASE WHEN JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(meta, '$.refund_summary.total_amount')) AS DECIMAL(12,2)) ELSE (SELECT IFNULL(SUM(j.amount), 0) FROM JSON_TABLE(meta, '$.refund_receipts[*]' COLUMNS(amount DECIMAL(12,2) PATH '$.amount')) j) END)";
                }

                if ($status === 'has') {
                    $builder->whereRaw("ROUND(($refundTotalExpr) * 100) > 0");
                } elseif ($status === 'none') {
                    $builder->whereRaw("ROUND(($refundTotalExpr) * 100) <= 0");
                }
            })
            ->when(($filters['refund_inconsistent'] ?? '') !== '', function (Builder $builder) {
                // 退款数据不一致（可治理）：
                // - 状态=refunded 但 退款总额 + 容差 < 已付金额
                // - 状态!=refunded 且 已付金额>0 且 退款总额 >= 已付金额 + 容差
                // 退款总额口径：优先 refund_summary.total_amount；缺省回退汇总 refund_receipts[].amount
                // 容差口径与模型 PlatformOrder::isRefundInconsistent() 对齐（config('saasshop.amounts.tolerance')）
                $tol = (float) config('saasshop.amounts.tolerance', 0.01);
                $tolCents = (int) round($tol * 100);
                $tolCents = max(1, $tolCents);

                $driver = $builder->getQuery()->getConnection()->getDriverName();

                if ($driver === 'sqlite') {
                    $refundTotalExpr = "(CASE WHEN JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL THEN CAST(JSON_EXTRACT(meta, '$.refund_summary.total_amount') AS REAL) ELSE (SELECT IFNULL(SUM(CAST(JSON_EXTRACT(value, '$.amount') AS REAL)), 0) FROM json_each(COALESCE(JSON_EXTRACT(meta, '$.refund_receipts'), '[]'))) END)";

                    $builder->where(function (Builder $q) use ($refundTotalExpr, $tolCents) {
                        // refunded 但退款不够：refund_total + tol < paid
                        $q->where(function (Builder $q2) use ($refundTotalExpr, $tolCents) {
                            $q2->where('payment_status', 'refunded')
                                ->whereRaw("paid_amount > 0")
                                ->whereRaw("(ROUND($refundTotalExpr * 100) + {$tolCents}) < ROUND(paid_amount * 100)");
                        })
                        // 非 refunded 但退款已达到/超过已付 + tol：refund_total >= paid + tol
                        ->orWhere(function (Builder $q2) use ($refundTotalExpr, $tolCents) {
                            $q2->where('payment_status', '!=', 'refunded')
                                ->whereRaw("paid_amount > 0")
                                ->whereRaw("ROUND($refundTotalExpr * 100) >= (ROUND(paid_amount * 100) + {$tolCents})");
                        });
                    });
                } else {
                    $refundTotalExpr = "(CASE WHEN JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(meta, '$.refund_summary.total_amount')) AS DECIMAL(12,2)) ELSE (SELECT IFNULL(SUM(j.amount), 0) FROM JSON_TABLE(meta, '$.refund_receipts[*]' COLUMNS(amount DECIMAL(12,2) PATH '$.amount')) j) END)";

                    $builder->where(function (Builder $q) use ($refundTotalExpr, $tolCents) {
                        $q->where(function (Builder $q2) use ($refundTotalExpr, $tolCents) {
                            $q2->where('payment_status', 'refunded')
                                ->whereRaw("paid_amount > 0")
                                ->whereRaw("(ROUND($refundTotalExpr * 100) + {$tolCents}) < ROUND(paid_amount * 100)");
                        })->orWhere(function (Builder $q2) use ($refundTotalExpr, $tolCents) {
                            $q2->where('payment_status', '!=', 'refunded')
                                ->whereRaw("paid_amount > 0")
                                ->whereRaw("ROUND($refundTotalExpr * 100) >= (ROUND(paid_amount * 100) + {$tolCents})");
                        });
                    });
                }
            })
            ->when(($filters['lead_id'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 线索联动：仅看指定线索关联的订单（meta.platform_lead_id = lead_id）
                $leadId = (int) ($filters['lead_id'] ?? 0);
                if ($leadId <= 0) {
                    return;
                }

                $driver = $builder->getQuery()->getConnection()->getDriverName();
                if ($driver === 'sqlite') {
                    $builder->whereRaw("JSON_EXTRACT(meta, '$.platform_lead_id') = ?", [$leadId]);
                } else {
                    $builder->whereRaw("CAST(JSON_UNQUOTE(JSON_EXTRACT(meta, '$.platform_lead_id')) AS UNSIGNED) = ?", [$leadId]);
                }
            })
            ->when(($filters['created_from'] ?? '') !== '' || ($filters['created_to'] ?? '') !== '', function (Builder $builder) use ($filters) {
                // 创建时间范围筛选：用于“趋势→集合”跳转与运营快速定位
                // 口径：基于 created_at。
                $from = trim((string) ($filters['created_from'] ?? ''));
                $to = trim((string) ($filters['created_to'] ?? ''));

                // 容错：仅接受 YYYY-MM-DD 格式；不合法则忽略，避免异常输入污染查询
                if ($from !== '' && ! preg_match('/^\d{4}-\d{2}-\d{2}$/', $from)) {
                    $from = '';
                }
                if ($to !== '' && ! preg_match('/^\d{4}-\d{2}-\d{2}$/', $to)) {
                    $to = '';
                }

                if ($from !== '' && $to !== '') {
                    // [from 00:00:00, to 23:59:59]
                    $builder->whereBetween('created_at', [
                        $from . ' 00:00:00',
                        $to . ' 23:59:59',
                    ]);
                } elseif ($from !== '') {
                    $builder->where('created_at', '>=', $from . ' 00:00:00');
                } elseif ($to !== '') {
                    $builder->where('created_at', '<=', $to . ' 23:59:59');
                }
            })
            ->when(($filters['bmpa_processable_only'] ?? '') === '1', function (Builder $builder) {
                // 真正可 BMPA 处理集合（与仪表盘统计口径对齐）：
                // - pending + unpaid
                // - 排除：续费缺订阅（renewal 且 site_subscription_id 为空）
                // - 排除：存在退款轨迹（refund_total > 0）
                $builder->where('status', 'pending')
                    ->where('payment_status', 'unpaid')
                    ->where(function (Builder $q) {
                        $q->where('order_type', '!=', 'renewal')
                            ->orWhereNotNull('site_subscription_id');
                    });

                $driver = $builder->getQuery()->getConnection()->getDriverName();

                if ($driver === 'sqlite') {
                    $refundTotalExpr = "(CASE WHEN JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL THEN CAST(JSON_EXTRACT(meta, '$.refund_summary.total_amount') AS REAL) ELSE (SELECT IFNULL(SUM(CAST(JSON_EXTRACT(value, '$.amount') AS REAL)), 0) FROM json_each(COALESCE(JSON_EXTRACT(meta, '$.refund_receipts'), '[]'))) END)";
                    $builder->whereRaw("ROUND(($refundTotalExpr) * 100) <= 0");
                } else {
                    $refundTotalExpr = "(CASE WHEN JSON_EXTRACT(meta, '$.refund_summary.total_amount') IS NOT NULL THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(meta, '$.refund_summary.total_amount')) AS DECIMAL(12,2)) ELSE (SELECT IFNULL(SUM(j.amount), 0) FROM JSON_TABLE(meta, '$.refund_receipts[*]' COLUMNS(amount DECIMAL(12,2) PATH '$.amount')) j) END)";
                    $builder->whereRaw("ROUND(($refundTotalExpr) * 100) <= 0");
                }
            });
    }


    private function receiptTotalForOrder(PlatformOrder $order): float
    {
        // 口径统一：集中到模型方法，避免多处复制导致漂移
        return (float) $order->receiptTotal();
    }

    private function refundTotalForOrder(PlatformOrder $order): float
    {
        // 口径统一：集中到模型方法，避免多处复制导致漂移
        return (float) $order->refundTotal();
    }

    protected function sumReceiptAmount($orders): float
    {
        $total = 0.0;
        foreach ($orders as $o) {
            $t = (float) (data_get($o->meta, 'payment_summary.total_amount') ?? 0);
            if ($t > 0) {
                $total += $t;
                continue;
            }

            $receipts = (array) (data_get($o->meta, 'payment_receipts', []) ?? []);
            foreach ($receipts as $r) {
                $total += (float) (data_get($r, 'amount') ?? 0);
            }
        }
        return $total;
    }

    protected function statusLabels(): array
    {
        return [
            'pending' => '待处理',
            'paid' => '已支付',
            'activated' => '已生效',
            'cancelled' => '已取消',
            'refunded' => '已退款',
        ];
    }

    protected function paymentStatusLabels(): array
    {
        return [
            'unpaid' => '未支付',
            'paid' => '已支付',
            'partially_refunded' => '部分退款',
            'refunded' => '已退款',
            'failed' => '支付失败',
        ];
    }

    protected function orderTypeLabels(): array
    {
        return [
            'new_purchase' => '新购',
            'renewal' => '续费',
            'upgrade' => '升级',
            'downgrade' => '降级',
        ];
    }

    protected function billingCycleLabels(): array
    {
        return [
            'monthly' => '月付',
            'quarterly' => '季付',
            'yearly' => '年付',
            'one_time' => '一次性',
        ];
    }

    protected function periodMonthsFromBillingCycle(string $billingCycle): int
    {
        return match ($billingCycle) {
            'monthly' => 1,
            'quarterly' => 3,
            'yearly' => 12,
            'one_time' => 1,
            default => 1,
        };
    }
}
