<?php

declare(strict_types=1);

namespace App\Platform\Tmall\EntityParse;

use App\Constants\OrderStatus;
use App\Constants\OrderType;
use App\Constants\PaymentMethod;
use App\Constants\RefundStatus;
use App\Constants\RefundType;
use App\Platform\Tmall\Constants\RefundStatus as TmallRefundStatus;
use App\Model\Company;
use App\Model\Order;
use App\Model\OrderItem;
use App\Model\RefundItem;
use App\Model\Store;
use App\Platform\AbstractRefundParse;
use Carbon\Carbon;
use Hyperf\Collection\LazyCollection;
use Hyperf\DbConnection\Db;
use InvalidArgumentException;

/**
 * 天猫退款解析器
 *
 * Tmall 没有主退款和子退款的概念，每个售后单对应一个订单子项。
 * 每个售后单同时写入 refunds（主记录）和 refund_items（明细）两张表。
 */
class Refund extends AbstractRefundParse
{
    /**
     * 公司作用域匹配
     *
     * @param array $metadata
     * @return Company
     * @throws InvalidArgumentException
     */
    public function companyScopeMatch(array $metadata): Company
    {
        if (!isset($metadata['company_id'])) {
            throw new InvalidArgumentException('company_id is required in metadata');
        }

        $company = Company::find($metadata['company_id']);

        if (!$company) {
            throw new InvalidArgumentException("Company with ID {$metadata['company_id']} not found");
        }

        return $company;
    }

    /**
     * 店铺作用域匹配
     *
     * @param array $metadata
     * @return Store
     * @throws InvalidArgumentException
     */
    public function storeScopeMatch(array $metadata): Store
    {
        if (!isset($metadata['store_id'])) {
            throw new InvalidArgumentException('store_id is required in metadata');
        }

        $store = Store::find($metadata['store_id']);

        if (!$store) {
            throw new InvalidArgumentException("Store with ID {$metadata['store_id']} not found");
        }

        return $store;
    }

    /**
     * 实体数据映射 - 映射到 refunds 表
     *
     * Tmall 不存在父退款订单，因此此方法返回空值
     *
     * @param array $raw_data 原始数据数组
     * @return LazyCollection 每个元素为可供 Refund Model::fill() 使用的数组
     */
    public function entityMap(array $raw_data): LazyCollection
    {
        return LazyCollection::make();
    }

    /**
     * 获取实体的唯一键字段（用于 upsert 的 uniqueBy 参数）
     *
     * @return array
     */
    public function getUniqueBy(): array
    {
        return ['store_id', 'platform_refund_id'];
    }

    /**
     * 获取可更新的字段列表（用于 upsert 的 update 参数）
     *
     * @return array
     */
    public function getUpdateFields(): array
    {
        return [
            'order_id',
            'refund_status_id',
            'refund_type_id',
            'reason',
            'refund_amount',
            'freight_refund',
            'refund_total',
            'buyer_user_id',
            'updated_date',
            'completed_date',
            'raw',
            'ext',
            'hash',
            'updated_at',
        ];
    }

    /**
     * Tmall 没有父级退款单
     * @return bool 
     */
    public function hasParentRefund(): bool
    {
        return false;
    }

    /**
     * 格式化退款子项数据 - 映射到 refund_items 表
     *
     * Tmall 每个售后单同时作为一条 refund_item 记录
     * 需要在 refunds upsert 完成后调用，以获取 refund_id 映射
     *
     * @param array $raw_data 原始数据数组
     * @param array $platform_refund_id_to_local_refund_id_map [platform_refund_id => local db refund id]
     * @return array
     */
    public function formatRefundItemsFromRaw(array $raw_data, ?array $platform_refund_id_to_local_refund_id_map): array
    {
        $records = isset($raw_data[0]) ? $raw_data : [$raw_data];
        $items = [];

        foreach ($records as $record) {
            $platform_refund_id = strval($record['refund_id']);
            $tz = $this->getStore()->getTimezoneString();
            $raw = \json_encode($record);

            // 闪电退款: 覆盖退款类型 + 预填订单日期（闪电退款无原始订单，fillOrderDates 查不到）
            $refund_type_id = $this->getRefundTypeId($record);
            $instant_refund_order_date = null;
            if (isset($record['attribute']) && $record['attribute'] !== '') {
                $attr = $this->parseRefundItemAttribute($record['attribute']);
                if (!empty($attr) && $attr['clj_zero_second_refund']) {
                    $refund_type_id = RefundType::INSTANT_REFUND_WITHOUT_RETURN->value;
                    // bgmtc 是「退款后台创建时间」，并非真实订单创建时间。
                    // 闪电退款场景下 Tmall 不推送原始订单，bgmtc 是我们能获取到的最接近订单创建时间的时间戳（误差在秒~分钟级别），
                    // 用作 order_created_date / order_paid_date 的近似值。
                    $date_str = $attr['bgmtc'] ?? $record['created'];
                    $instant_refund_order_date = Carbon::parse($date_str, $tz)->format('Y-m-d H:i:sP');
                }
            }

            $items[] = [
                'company_id' => $this->getCompany()->id,
                'platform_id' => $this->getPlatform()->id,
                'store_id' => $this->getStore()->id,
                'refund_id' => 0,
                'platform_parent_refund_id' => '',
                'platform_refund_id' => $platform_refund_id,
                'refund_status_id' => $this->getRefundStatusId($record['status']),
                'refund_type_id' => $refund_type_id,
                'reason' => $record['reason'] ?? null,
                'currency' => 'CNY',
                'buyer_user_id' => $record['buyer_open_uid'] ?? null,
                'platform_order_id' => strval($record['tid']),
                'platform_sub_order_id' => strval($record['oid']),
                'platform_product_id' => isset($record['num_iid']) ? strval($record['num_iid']) : null,
                'quantity' => $record['num'] ?? 0,
                'refund_amount' => (float) ($record['refund_fee'] ?? 0),
                'order_created_date' => $instant_refund_order_date, // 闪电退款用 bgmtc 预填，普通退款由 fillOrderDates 回填
                'order_paid_date' => $instant_refund_order_date,
                'created_date' => Carbon::parse($record['created'], $tz)->format('Y-m-d H:i:sP'),
                'updated_date' => isset($record['modified']) ? Carbon::parse($record['modified'], $tz)->format('Y-m-d H:i:sP') : null,
                'completed_date' => isset($record['end_time']) ? Carbon::parse($record['end_time'], $tz)->format('Y-m-d H:i:sP') : null,
                'raw' => $raw,
                'ext' => null,
                'created_at' => Carbon::now()->format('Y-m-d H:i:sP'),
                'updated_at' => Carbon::now()->format('Y-m-d H:i:sP'),
            ];
        }

        return $this->fillOrderDates($items);
    }

    /**
     * 根据退款子项的 store_id + platform_order_id 查询订单，
     * 将 order_created_date 和 order_paid_date 填入 $items 数组
     *
     * 订单搜索范围：[退款 created_date - 3个月, 退款 created_date]
     *
     * @param array $items 退款子项数据数组
     * @return array 填充了订单日期的退款子项数组
     */
    protected function fillOrderDates(array $items): array
    {
        $lookups = [];
        foreach ($items as $item) {
            $key = $item['store_id'] . ':' . $item['platform_order_id'];
            if (!isset($lookups[$key])) {
                $lookups[$key] = [
                    'store_id' => $item['store_id'],
                    'platform_order_id' => $item['platform_order_id'],
                    'created_date' => $item['created_date'],
                ];
            }
        }

        if (empty($lookups)) {
            return $items;
        }

        $orderDatesMap = $this->queryOrderDates($lookups);

        dump("Matched " . count($orderDatesMap) . " orders for " . count($lookups) . " refund item lookups");

        foreach ($items as &$item) {
            $key = $item['store_id'] . ':' . $item['platform_order_id'];
            if (isset($orderDatesMap[$key])) {
                $item['order_created_date'] = $orderDatesMap[$key]['order_created_date'];
                $item['order_paid_date'] = $orderDatesMap[$key]['order_paid_date'];
            }
        }
        unset($item);

        return $items;
    }

    /**
     * 根据 store_id + platform_order_id 批量查询订单的创建时间和付款时间
     *
     * 订单搜索范围：[退款 created_date - 3个月, 退款 created_date]
     *
     * @param array $lookups [key => ['store_id', 'platform_order_id', 'created_date']]
     * @return array [store_id:platform_order_id => ['order_created_date' => ..., 'order_paid_date' => ...]]
     */
    protected function queryOrderDates(array $lookups): array
    {
        $orders = Order::query()
            ->where(function ($query) use ($lookups) {
                foreach ($lookups as $lookup) {
                    $createdDate = Carbon::parse($lookup['created_date']);
                    $query->orWhere(function ($q) use ($lookup, $createdDate) {
                        $q->where('store_id', $lookup['store_id'])
                          ->where('platform_order_id', $lookup['platform_order_id'])
                          ->whereBetween('created_date', [
                              $createdDate->copy()->subMonths(3),
                              $createdDate,
                          ]);
                    });
                }
            })
            ->get(['store_id', 'platform_order_id', 'created_date', 'paid_date']);

        $map = [];
        foreach ($orders as $order) {
            $key = $order->store_id . ':' . $order->platform_order_id;
            $map[$key] = [
                'order_created_date' => $order->created_date,
                'order_paid_date' => $order->paid_date,
            ];
        }

        return $map;
    }

    /**
     * 将 Tmall 退款状态映射为系统退款状态 ID
     *
     * @param string $platformStatus
     * @return int
     */
    public function getRefundStatusId(string $platform_status): int
    {
        $map = $this->refundStatusMap();
        return $map[$platform_status] ?? RefundStatus::APPLIED->value;
    }

    /**
     * Tmall 退款状态 → 系统退款状态映射
     *
     * @return array<string, int>
     */
    private function refundStatusMap(): array
    {
        return [
            TmallRefundStatus::WAIT_SELLER_AGREE->value       => RefundStatus::APPLIED->value,
            TmallRefundStatus::WAIT_BUYER_RETURN_GOODS->value  => RefundStatus::SELLER_ACCEPTED->value,
            TmallRefundStatus::WAIT_SELLER_CONFIRM_GOODS->value => RefundStatus::SELLER_ACCEPTED->value,
            TmallRefundStatus::SELLER_REFUSE_BUYER->value      => RefundStatus::REFUSED->value,
            TmallRefundStatus::SUCCESS->value                  => RefundStatus::SUCCESS->value,
            TmallRefundStatus::CLOSED->value                   => RefundStatus::CLOSED->value,
        ];
    }

    /**
     * 根据 Tmall 售后单数据判断退款类型
     *
     * @param array $record 单条售后单原始数据
     * @return int
     */
    public function getRefundTypeId(array $record): int
    {
        $has_good_return = $record['has_good_return'] ?? false;
        $good_status = $record['good_status'] ?? '';

        // 买家已退货或等待退货 → 退货退款
        if ($has_good_return) {
            return RefundType::RETURN_AND_REFUND->value;
        }

        // 买家未收到货 → 未发货前退款
        if ($good_status === 'BUYER_NOT_RECEIVED') {
            return RefundType::RETURN_BEFORE_SHIPPING->value;
        }

        // 买家已收货但无须退货 → 退款无须退货
        if ($good_status === 'BUYER_RECEIVED') {
            return RefundType::REFUND_WITHOUT_RETURN->value;
        }

        return RefundType::RETURN_BEFORE_SHIPPING->value;
    }


    /**
     * Tmall「0秒闪电退款」订单重建
     *
     * 买家下单后立刻反悔退款，Tmall 不推送原始订单数据。
     * 从退款记录的 attribute 字段重建 Order + OrderItem，并回填 RefundItem 日期。
     *
     * 判定规则: attribute.clj_zero_second_refund === true
     *
     * @param array $raw_data 原始退款数据数组
     */
    public function fixInstantRefundOrders(array $raw_data): void
    {
        $records = isset($raw_data[0]) ? $raw_data : [$raw_data];
        $tz = $this->getStore()->getTimezoneString();
        $store_id = $this->getStore()->id;

        // Step 1: 解析 attribute，筛选闪电退款记录
        $instant_records = [];
        foreach ($records as $record) {
            $attribute = $record['attribute'] ?? '';
            if ($attribute === '') {
                continue;
            }

            $attr = $this->parseRefundItemAttribute($attribute);
            if (empty($attr) || !$attr['clj_zero_second_refund']) {
                continue;
            }

            $record['_attr'] = $attr;
            $instant_records[] = $record;
        }

        if (empty($instant_records)) {
            return;
        }

        // Step 2: 按 tid（platform_order_id）分组
        $grouped = [];
        foreach ($instant_records as $record) {
            $tid = strval($record['tid']);
            $grouped[$tid][] = $record;
        }

        // Step 3: 幂等性检查 - 排除 orders 表中已存在的订单
        $tids = array_keys($grouped);
        $existing_tids = Order::where('store_id', $store_id)
            ->whereIn('platform_order_id', $tids)
            ->pluck('platform_order_id')
            ->toArray();

        $grouped = array_diff_key($grouped, array_flip($existing_tids));

        if (empty($grouped)) {
            dump('[fixInstantRefundOrders] All orders already exist, skipping');
            return;
        }

        dump('[fixInstantRefundOrders] Reconstructing ' . count($grouped) . ' orders from instant refund data');

        // Step 4: 按 tid 聚合，重建 Order 记录
        $orders_data = [];
        $order_dates = []; // tid => created_date

        foreach ($grouped as $tid => $tid_records) {
            $total_amount_fen = 0;
            $total_paid_yuan = 0;
            $total_discount_fen = 0;
            $max_post_fee_fen = 0;
            $refund_ids = [];
            $buyer_user_id = null;
            $created_date = null;

            foreach ($tid_records as $record) {
                $attr = $record['_attr'];

                // attribute 金额单位: 分
                $total_amount_fen += $attr['item_price'] * $attr['item_buy_amount'];

                // refund_fee 单位: 元（全额退 = 已付金额）
                $total_paid_yuan += (float) ($record['refund_fee'] ?? 0);

                foreach ($attr['promotions'] as $promo) {
                    $total_discount_fen += $promo['amount'];
                }

                if ($attr['main_order_post_fee'] > $max_post_fee_fen) {
                    $max_post_fee_fen = $attr['main_order_post_fee'];
                }

                $refund_ids[] = strval($record['refund_id']);
                $buyer_user_id = $record['buyer_open_uid'] ?? $buyer_user_id;

                // bgmtc 是「退款后台创建时间」，非真实订单创建时间，
                // 但闪电退款场景下是最接近订单创建时间的近似值（误差秒~分钟级）。
                if ($created_date === null) {
                    $date_str = $attr['bgmtc'] ?? $record['created'];
                    $created_date = Carbon::parse($date_str, $tz)->format('Y-m-d H:i:sP');
                }
            }

            $order_dates[$tid] = $created_date;

            $raw_json = \json_encode([
                '_reconstructed' => true,
                'tid' => $tid,
                'refund_ids' => $refund_ids,
            ]);

            $ext_json = \json_encode([
                'reconstructed_from_refund' => true,
                'refund_ids' => $refund_ids,
            ]);

            $orders_data[] = [
                'company_id' => $this->getCompany()->id,
                'platform_id' => $this->getPlatform()->id,
                'store_id' => $store_id,
                'order_status_id' => OrderStatus::CANCEL_BEFORE_SHIPPING->value,
                'order_type_id' => OrderType::REGRET_ORDER->value,
                'platform_order_id' => $tid,
                'buyer_user_id' => $buyer_user_id,
                'payment_method_id' => PaymentMethod::ALIPAY_CN->value,
                'presale' => false,
                'total_amount' => $total_amount_fen / 100,
                'total_paid' => $total_paid_yuan,
                'total_discount' => $total_discount_fen / 100,
                'total_received' => 0,
                'freight_fee' => $max_post_fee_fen / 100,
                'tax_fee' => 0,
                'discount_fee' => 0,
                'commission_fee' => 0,
                'coupon_amount' => 0,
                'voucher_amount' => 0,
                'created_date' => $created_date,
                'updated_date' => null,
                'paid_date' => $created_date,
                'shipping_date' => null,
                'zipcode' => '',
                'city' => '',
                'province' => '',
                'country' => 'CN',
                'raw' => $raw_json,
                'ext' => $ext_json,
                'hash' => \md5($raw_json),
                'created_at' => Carbon::now()->format('Y-m-d H:i:sP'),
                'updated_at' => Carbon::now()->format('Y-m-d H:i:sP'),
            ];
        }

        Order::upsert(
            $orders_data,
            ['store_id', 'platform_order_id', 'created_date'],
            [
                'order_status_id', 'order_type_id', 'buyer_user_id',
                'payment_method_id', 'total_amount', 'total_paid',
                'total_discount', 'total_received', 'freight_fee',
                'paid_date', 'raw', 'ext', 'hash', 'updated_at',
            ]
        );

        // Step 5: 获取 Order ID 映射 [tid => db_order_id]
        $tids = array_keys($grouped);
        $order_id_map = Order::where('store_id', $store_id)
            ->whereIn('platform_order_id', $tids)
            ->pluck('id', 'platform_order_id')
            ->toArray();

        // Step 6: 重建 OrderItem 记录
        $items_data = [];
        $product_keys = [];

        foreach ($grouped as $tid => $tid_records) {
            $order_id = $order_id_map[$tid] ?? 0;
            $created_date = $order_dates[$tid];

            foreach ($tid_records as $record) {
                $attr = $record['_attr'];
                $index = count($items_data);

                $discount_fen = 0;
                foreach ($attr['promotions'] as $promo) {
                    $discount_fen += $promo['amount'];
                }

                $items_data[] = [
                    'company_id' => $this->getCompany()->id,
                    'platform_id' => $this->getPlatform()->id,
                    'store_id' => $store_id,
                    'order_id' => $order_id,
                    'platform_order_id' => $tid,
                    'sub_order_id' => strval($record['oid']),
                    'sub_order_type_id' => null,
                    'product_id' => 0,
                    'platform_product_id' => isset($record['num_iid']) ? strval($record['num_iid']) : null,
                    'product_sku' => null,
                    'product_barcode' => null,
                    'unit_price' => $attr['item_price'] / 100,
                    'quantity' => $attr['item_buy_amount'] > 0 ? $attr['item_buy_amount'] : ($record['num'] ?? 1),
                    'discount' => $discount_fen / 100,
                    'total' => (float) ($record['refund_fee'] ?? 0),
                    'ext' => null,
                    'created_date' => $created_date,
                ];

                // 构建产品查询键: item_id:model_id
                $item_id = isset($record['num_iid']) ? strval($record['num_iid']) : null;
                $model_id = $attr['sku']['sku_id'] ?? null;
                if ($item_id !== null) {
                    $product_keys[$index] = $item_id . ':' . ($model_id ?? 'null');
                }
            }
        }

        // 批量查询产品 ID 并回填
        if (!empty($product_keys)) {
            $products_map = $this->batchQueryProductsForReconstruction($product_keys);
            foreach ($product_keys as $index => $key) {
                if (isset($products_map[$key])) {
                    $items_data[$index]['product_id'] = $products_map[$key];
                }
            }
        }

        if (!empty($items_data)) {
            OrderItem::upsert(
                $items_data,
                ['store_id', 'platform_order_id', 'sub_order_id', 'created_date'],
                [
                    'order_id', 'product_id', 'platform_product_id',
                    'unit_price', 'quantity', 'discount', 'total',
                ]
            );
        }

        // Step 7: 回填 RefundItem 的 order_created_date / order_paid_date
        foreach ($grouped as $tid => $tid_records) {
            $created_date = $order_dates[$tid];
            RefundItem::where('store_id', $store_id)
                ->where('platform_order_id', $tid)
                ->whereNull('order_created_date')
                ->update([
                    'order_created_date' => $created_date,
                    'order_paid_date' => $created_date,
                ]);
        }

        dump('[fixInstantRefundOrders] Completed: ' . count($orders_data) . ' orders, ' . count($items_data) . ' items reconstructed');
    }

    /**
     * 批量查询产品 ID（用于闪电退款订单重建）
     *
     * 复用 Tmall\EntityParse\Order 中 batchQueryProducts 的 PostgreSQL VALUES + JOIN 模式
     *
     * @param array $product_keys 索引 => 查询键 (item_id:model_id) 的映射
     * @return array 查询键 => 产品 ID 的映射
     */
    private function batchQueryProductsForReconstruction(array $product_keys): array
    {
        $store_id = $this->getStore()->id;
        $unique_keys = array_unique(array_values($product_keys));

        if (empty($unique_keys)) {
            return [];
        }

        $values = [];
        $bindings = [];

        foreach ($unique_keys as $key) {
            [$item_id, $model_id] = explode(':', $key, 2);
            $values[] = '(?::text, ?::text)';
            $bindings[] = $item_id;
            $bindings[] = $model_id === 'null' ? null : $model_id;
        }

        $valuesClause = implode(', ', $values);
        $bindings[] = $store_id;

        $sql = "
            SELECT p.id, p.platform_item_id, p.platform_model_id
            FROM (VALUES {$valuesClause}) AS v(item_id, model_id)
            INNER JOIN products p
            ON p.store_id = ?
               AND p.platform_item_id = v.item_id
               AND (p.platform_model_id = v.model_id OR (v.model_id IS NULL AND p.platform_model_id IS NULL))
        ";

        $products = Db::select($sql, $bindings);

        $map = [];
        foreach ($products as $product) {
            $key = $product->platform_item_id . ':' . ($product->platform_model_id ?? 'null');
            $map[$key] = $product->id;
        }

        return $map;
    }


    /**
     * 解析退款子项的 attribute 字段为结构化数据
     *
     * Tmall attribute 原始格式: ;key:value;key:value;...
     * 编码规则: #3B → :(冒号)  #3A → ;(分号)
     *
     * @param string $attribute 原始 attribute 字符串
     * @return array 结构化属性数据
     */
    public function parseRefundItemAttribute(string $attribute): array
    {
        $pairs = $this->parseAttributePairs($attribute);

        if (empty($pairs)) {
            return [];
        }

        return [
            // ── 基本信息 ──
            'biz_code'      => $pairs['bizCode'] ?? null,       // 业务编码 (tmall.hk.refund=天猫国际退款)
            'sdk_code'      => $pairs['sdkCode'] ?? null,       // SDK来源
            'shop_name'     => $pairs['shop_name'] ?? null,     // 店铺名称
            'workflow_name' => $pairs['workflowName'] ?? null,  // 工作流
            'op_role'       => $pairs['opRole'] ?? null,        // 操作角色 (daemon=系统自动)

            // ── 商品信息 ──
            'sku'             => $this->parseSkuAttribute($pairs['sku'] ?? ''), // SKU ID + 规格属性
            'item_buy_amount' => (int) ($pairs['itemBuyAmount'] ?? 0),          // 购买数量
            'item_price'      => (int) ($pairs['itemPrice'] ?? 0),              // 商品单价 (分)
            'leaves_cat'      => $pairs['leavesCat'] ?? null,                   // 叶子类目ID
            'root_cat'        => $pairs['rootCat'] ?? null,                     // 根类目ID
            'is_virtual'      => (bool) ($pairs['isVirtual'] ?? 0),             // 是否虚拟商品

            // ── 退款金额 (单位: 分) ──
            'apply_init_refund_fee' => (int) ($pairs['apply_init_refund_fee'] ?? 0), // 申请退款金额
            'ex_max_refund_fee'     => (int) ($pairs['EXmrf'] ?? 0),                 // 最大可退金额
            'online_refund_fee'     => (int) ($pairs['ol_tf'] ?? 0),                 // 在线退款金额
            'refund_post_fee'       => (int) ($pairs['refundPostFee'] ?? 0),         // 退款运费
            'main_order_post_fee'   => (int) ($pairs['mainOrderPostFee'] ?? 0),      // 主单运费
            'to_seller_fee'         => (int) ($pairs['toSellerFee'] ?? 0),           // 卖家应付金额

            // ── 退款原因与类型 ──
            'apply_reason_text'    => $pairs['apply_reason_text'] ?? null,           // 退款原因文本
            'apply_text_id'        => $pairs['apply_text_id'] ?? null,               // 退款原因ID
            'dispute_request'      => (int) ($pairs['disputeRequest'] ?? 0),         // 是否有纠纷
            'dispute_trade_status' => (int) ($pairs['disputeTradeStatus'] ?? 0),     // 纠纷交易状态
            'dbt'                  => $pairs['DBT'] ?? null,                         // 退款业务类型 (InterceptUnconsignRefund=拦截未发货退款)

            // ── 退款特征标识 ──
            'clj_zero_second_refund' => (bool) ($pairs['clj_zero_second_refund'] ?? 0), // 0秒闪电退款 (为 true 时需重建订单, 见 fixInstantRefundOrders)
            'tmg_simple_zero_refund' => (bool) ($pairs['tmgSimpleZeroRefund'] ?? 0),    // 简易0秒退款
            'warehouse_refund'       => (bool) ($pairs['warehouseRefund'] ?? 0),         // 仓库退款
            'part_refund'            => (bool) ($pairs['part_refund'] ?? 0),             // 部分退款
            'percent_refund'         => (bool) ($pairs['percent_refund'] ?? 0),          // 比例退款
            'products'               => $pairs['products'] ?? null,                      // 退款产品标识 (timeoutrefund=超时退款)

            // ── 资金流 ──
            'fund_flow' => $this->parseFundFlowAttribute($pairs['fundFlowInfo'] ?? ''), // 资金渠道明细 [{type, group, name, amount}]
            'pay_mode'  => $pairs['payMode'] ?? null,                                   // 支付方式
            'pay_lock'  => $pairs['pay_lock'] ?? null,                                  // 支付锁定方

            // ── 优惠信息 ──
            'promotions'            => $this->parsePromotionsAttribute($pairs['pmtR'] ?? ''),               // 优惠明细 [{type, amount}]
            'has_threshold_coupon'  => (bool) ($pairs['has_threshold_coupon'] ?? 0),                         // 是否有门槛券
            'threshold_instruction' => $this->decodeAttributeJson($pairs['threshold_instruction'] ?? null),  // 门槛券指令

            // ── 价保 ──
            'price_protection' => $this->parsePriceProtectionAttribute($pairs['price_protection'] ?? ''), // 价保时间范围 {start, end}

            // ── 物流拦截 ──
            'intercept_type'         => $pairs['interceptType'] ?? null,                                                // 拦截类型 (clj=菜鸟)
            'intercept_status'       => (int) ($pairs['interceptStatus'] ?? 0),                                         // 拦截状态
            'intercept_items'        => $this->decodeAttributeJson($pairs['interceptItemListResult'] ?? null),           // 拦截结果明细
            'ability_template_code'  => $pairs['abilityTemplateCode'] ?? null,                                          // 能力模板编码
            'ability_success_flag'   => (bool) ($pairs['abilitySuccessFlag'] ?? 0),                                     // 能力执行是否成功
            'warehouse_intercept_op' => (int) ($pairs['sellerOpAbWarehouseIntercept'] ?? 0),                             // 卖家仓库拦截操作

            // ── 卖家/买家 ──
            'seller_batch'   => (bool) ($pairs['seller_batch'] ?? 0),   // 是否批量处理
            'seller_audit'   => (int) ($pairs['seller_audit'] ?? 0),    // 卖家审核状态
            'has_seller_memo' => (bool) ($pairs['hasSellerMemo'] ?? 0), // 是否有卖家备注
            'user_credit'    => (int) ($pairs['userCredit'] ?? 0),      // 买家信用等级
            'agree_source'   => $pairs['agreeSource'] ?? null,          // 同意来源 (clj=菜鸟自动同意)

            // ── 其他 ──
            'channel_sub_order_id' => $pairs['chnlObn'] ?? null,           // 渠道子订单编号
            'last_order'           => (bool) ($pairs['lastOrder'] ?? 0),   // 是否最后一笔子单
            'b2c'                  => (bool) ($pairs['b2c'] ?? 0),         // 是否B2C
            'bgmtc'                => $pairs['bgmtc'] ?? null,             // 退款后台创建时间（非订单创建时间，闪电退款场景用作订单时间近似值）
        ];
    }

    /**
     * 将 attribute 原始字符串拆分为 key => value 键值对
     *
     * 格式: ;key:value;key:value;...
     * 解码: #3B → :(冒号)  #3A → ;(分号)
     *
     * @param string $attribute
     * @return array<string, string>
     */
    private function parseAttributePairs(string $attribute): array
    {
        $pairs = [];
        $segments = explode(';', $attribute);

        foreach ($segments as $segment) {
            $segment = trim($segment);
            if ($segment === '') {
                continue;
            }

            $colonPos = strpos($segment, ':');
            if ($colonPos === false) {
                continue;
            }

            $key = substr($segment, 0, $colonPos);
            $value = substr($segment, $colonPos + 1);

            // #3B → :(冒号)  #3A → ;(分号)
            $value = str_replace(['#3B', '#3A'], [':', ';'], $value);

            $pairs[$key] = $value;
        }

        return $pairs;
    }

    /**
     * 解析 SKU 属性
     *
     * 格式: SKU_ID|属性名:属性值;属性名:属性值
     * 示例: 5085821404607|口味:橘子味;颜色分类:维C 250mg
     *
     * @param string $value 已解码的 SKU 字符串
     * @return array|null
     */
    private function parseSkuAttribute(string $value): ?array
    {
        if ($value === '') {
            return null;
        }

        $parts = explode('|', $value, 2);

        $result = [
            'sku_id'     => $parts[0],
            'properties' => [],
        ];

        if (isset($parts[1])) {
            foreach (explode(';', $parts[1]) as $prop) {
                $kv = explode(':', $prop, 2);
                if (count($kv) === 2) {
                    $result['properties'][$kv[0]] = $kv[1];
                }
            }
        }

        return $result;
    }

    /**
     * 解析资金流信息
     *
     * 格式: TYPE^GROUP^NAME^AMOUNT|TYPE^GROUP^NAME^AMOUNT
     * 示例: CASH^ALIPAY_FUND^支付宝^15489|OTHER_ASSETS^OTHER_ASSETS_GROUP^优惠^2211
     *
     * @param string $value
     * @return array [{type, group, name, amount}]
     */
    private function parseFundFlowAttribute(string $value): array
    {
        if ($value === '') {
            return [];
        }

        $flows = [];

        foreach (array_filter(explode('|', $value)) as $item) {
            $parts = explode('^', $item);
            if (count($parts) >= 4) {
                $flows[] = [
                    'type'   => $parts[0],         // 资金类型 (CASH=现金 / OTHER_ASSETS=其他资产)
                    'group'  => $parts[1],         // 资金组 (ALIPAY_FUND=支付宝 / OTHER_ASSETS_GROUP=优惠资产)
                    'name'   => $parts[2],         // 显示名称
                    'amount' => (int) $parts[3],   // 金额 (分)
                ];
            }
        }

        return $flows;
    }

    /**
     * 解析优惠明细
     *
     * 格式: TYPE^AMOUNT|TYPE^AMOUNT|
     * 示例: TAPP_USERCOUPON_SP^2211|
     *
     * @param string $value
     * @return array [{type, amount}]
     */
    private function parsePromotionsAttribute(string $value): array
    {
        if ($value === '') {
            return [];
        }

        $promotions = [];

        foreach (array_filter(explode('|', $value)) as $item) {
            $parts = explode('^', $item);
            if (count($parts) >= 2) {
                $promotions[] = [
                    'type'   => $parts[0],         // 优惠类型 (TAPP_USERCOUPON_SP=平台优惠券)
                    'amount' => (int) $parts[1],   // 优惠金额 (分)
                ];
            }
        }

        return $promotions;
    }

    /**
     * 解析价保时间范围
     *
     * 格式: START_TIME~END_TIME
     * 示例: 2026-02-24 13:47:45~2026-03-16 23:59:59
     *
     * @param string $value
     * @return array|null {start, end}
     */
    private function parsePriceProtectionAttribute(string $value): ?array
    {
        if ($value === '') {
            return null;
        }

        $parts = explode('~', $value, 2);

        return [
            'start' => $parts[0] ?? null,
            'end'   => $parts[1] ?? null,
        ];
    }

    /**
     * 解码 attribute 中内嵌的 JSON 字符串
     *
     * @param string|null $value
     * @return mixed
     */
    private function decodeAttributeJson(?string $value): mixed
    {
        if ($value === null || $value === '') {
            return null;
        }

        // attribute 中的 JSON 使用 \" 转义双引号，需先还原
        return json_decode(stripslashes($value), true);
    }
}
