<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Platform\Tmall\EntityParse;

use App\Platform\Tmall\EntityParse\Refund;
use PHPUnit\Framework\TestCase;
use ReflectionClass;

/**
 * Tmall 退款子项 attribute 字段解析测试
 *
 * 使用两笔真实退款数据作为测试样本：
 * - 样本A: 含 fundFlowInfo（资金流明细），商品单价 ¥399，退款 ¥177
 * - 样本B: 不含 fundFlowInfo，商品单价 ¥1199，退款 ¥669
 *
 * @internal
 * @coversNothing
 */
class RefundAttributeParseTest extends TestCase
{
    private Refund $parser;

    /**
     * 样本A: 含 fundFlowInfo 的完整退款 attribute
     * 商品: 维C 250mg 橘子味 (SKU 5085821404607)
     * 退款金额: ¥177.00, 支付宝 ¥154.89 + 优惠 ¥22.11
     */
    private string $sampleA = ';fundFlowInfo:CASH^ALIPAY_FUND^支付宝^15489|OTHER_ASSETS^OTHER_ASSETS_GROUP^优惠^2211;bizCode:tmall.hk.refund;disputeRequest:1;leavesCat:50026924;apply_reason_text:不喜欢/不想要;itemBuyAmount:1;interceptItemListResult:[{\"subBizOrderId\"#3B5081613120736477027,\"interceptDate\"#3B\"Feb 24, 2026 1#3B48#3B02 PM\",\"logisticInterceptEnum\"#3B\"INTERCEPT_SUCCESS\"}];seller_batch:true;clj_zero_second_refund:1;cPartRefund:0;DBT:InterceptUnconsignRefund;_F_tlmType:B_taobao;sku:5085821404607|口味#3B橘子味#3A颜色分类#3B维C 250mg;sgr:0;bgmtc:2026-02-24 13#3B47#3B41;jibuBizCode:tmall.jibu;interceptType:clj;agreeSource:clj;shop_name:NordicNaturalsPro海外旗舰店;nrp:1;sync:0;pmtR:TAPP_USERCOUPON_SP^2211|;rp3:1;disputeTradeStatus:4;tmgSimpleZeroRefund:1;sars:skip;isVirtual:0;EXmrf:17700;warehouseRefund:1;price_protection:2026-02-24 13#3B47#3B45~2026-03-16 23#3B59#3B59;enfunddetail:1;agrExctT:1771912080140;pay_lock:system;lastOrder:0;tod:86400000;newRefund:rp2;opRole:daemon;newUltron:3;refundPostFee:0;products:timeoutrefund^;apply_init_refund_fee:17700;abilitySuccessFlag:1;apply_text_id:175001;userCredit:4;sdkCode:ali.china.tmall.tmallhk;b2c:1;_F_uplus:1;interceptStatus:2;agrExctd:1;sellerFlag:0;rootCat:50026800;tos:3;has_threshold_coupon:1;chnlObn:5081613120736477027;ol_tf:17700;percent_refund:1;part_refund:0;r_r_l:504;payMode:alipay;appName:rtee;fps:4;hasSellerMemo:0;workflowName:refund;threshold_instruction:{\"TAPP_USERCOUPON_SP\"#3B{\"fpAssetBizExt\"#3B\"fpOpId#3B10416813443042770\"}};abilityTemplateCode:WAREHOUSE_INTERCEPT_ABILITY^1^2|;seller_audit:0;sellerOpAbWarehouseIntercept:2;mainOrderPostFee:0;itemPrice:39900;toSellerFee:0;';

    /**
     * 样本B: 不含 fundFlowInfo 的退款 attribute
     * 商品: 120粒装 (SKU 5112591851366)
     * 退款金额: ¥669.00
     */
    private string $sampleB = ';bizCode:tmall.hk.refund;disputeRequest:1;leavesCat:50026872;apply_reason_text:不喜欢/不想要;itemBuyAmount:1;interceptItemListResult:[{\"subBizOrderId\"#3B5080532763236889532,\"interceptDate\"#3B\"Feb 24, 2026 12#3B04#3B41 AM\",\"logisticInterceptEnum\"#3B\"INTERCEPT_SUCCESS\"}];seller_batch:true;clj_zero_second_refund:1;cPartRefund:0;DBT:InterceptUnconsignRefund;_F_tlmType:B_taobao;sku:5112591851366|颜色分类#3B120粒;sgr:0;bgmtc:2026-02-24 00#3B03#3B54;jibuBizCode:tmall.jibu;interceptType:clj;agreeSource:clj;shop_name:NordicNaturalsPro海外旗舰店;nrp:1;sync:0;pmtR:TAPP_USERCOUPON_SP^6589|;rp3:1;disputeTradeStatus:4;tmgSimpleZeroRefund:1;sars:skip;isVirtual:0;EXmrf:66900;warehouseRefund:1;price_protection:2026-02-24 00#3B04#3B01~2026-03-16 23#3B59#3B59;enfunddetail:1;agrExctT:1771862678950;pay_lock:system;lastOrder:0;tod:86400000;newRefund:rp2;opRole:daemon;newUltron:3;refundPostFee:0;products:timeoutrefund^;apply_init_refund_fee:66900;abilitySuccessFlag:1;apply_text_id:175001;userCredit:2;sdkCode:ali.china.tmall.tmallhk;b2c:1;interceptStatus:2;agrExctd:1;sellerFlag:0;rootCat:50026800;tos:3;has_threshold_coupon:1;chnlObn:5080532763236889532;ol_tf:66900;percent_refund:1;part_refund:0;r_r_l:504;payMode:alipay;appName:rtee;fps:4;hasSellerMemo:0;workflowName:refund;threshold_instruction:{\"TAPP_USERCOUPON_SP\"#3B{\"fpAssetBizExt\"#3B\"fpOpId#3B10414521188493295\"}};abilityTemplateCode:WAREHOUSE_INTERCEPT_ABILITY^1^2|;seller_audit:0;sellerOpAbWarehouseIntercept:2;mainOrderPostFee:0;itemPrice:119900;toSellerFee:0;';

    protected function setUp(): void
    {
        parent::setUp();

        // parseRefundItemAttribute 是纯解析方法，不依赖实例状态
        // 通过 Reflection 绕过 EntityParse 的 protected 构造函数
        $reflection = new ReflectionClass(Refund::class);
        $this->parser = $reflection->newInstanceWithoutConstructor();
    }

    // ========================================================
    // 编码解码测试
    // ========================================================

    /**
     * 验证 #3B → :(冒号) 解码正确
     * bgmtc 原始值 "2026-02-24 13#3B47#3B41" 应解码为 "2026-02-24 13:47:41"
     */
    public function testDecodeHashEncoding(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertSame('2026-02-24 13:47:41', $result['bgmtc']);
    }

    /**
     * 验证 #3A → ;(分号) 解码正确
     * SKU 属性分隔符 #3A 应解码为 ;
     */
    public function testDecodeHashEncodingSemicolon(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        // SKU 的 properties 使用 ; 分隔后再用 : 切分
        // 原始: 口味#3B橘子味#3A颜色分类#3B维C 250mg
        // 解码: 口味:橘子味;颜色分类:维C 250mg
        $this->assertSame('橘子味', $result['sku']['properties']['口味']);
        $this->assertSame('维C 250mg', $result['sku']['properties']['颜色分类']);
    }

    // ========================================================
    // 基本信息
    // ========================================================

    public function testParseBasicInfo(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertSame('tmall.hk.refund', $result['biz_code']);
        $this->assertSame('ali.china.tmall.tmallhk', $result['sdk_code']);
        $this->assertSame('NordicNaturalsPro海外旗舰店', $result['shop_name']);
        $this->assertSame('refund', $result['workflow_name']);
        $this->assertSame('daemon', $result['op_role']);
    }

    // ========================================================
    // 商品信息 & SKU 解析
    // ========================================================

    /**
     * 样本A SKU: 5085821404607|口味:橘子味;颜色分类:维C 250mg
     */
    public function testParseSkuWithMultipleProperties(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertSame('5085821404607', $result['sku']['sku_id']);
        $this->assertCount(2, $result['sku']['properties']);
        $this->assertSame('橘子味', $result['sku']['properties']['口味']);
        $this->assertSame('维C 250mg', $result['sku']['properties']['颜色分类']);
    }

    /**
     * 样本B SKU: 5112591851366|颜色分类:120粒 (仅一个属性)
     */
    public function testParseSkuWithSingleProperty(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleB);

        $this->assertSame('5112591851366', $result['sku']['sku_id']);
        $this->assertCount(1, $result['sku']['properties']);
        $this->assertSame('120粒', $result['sku']['properties']['颜色分类']);
    }

    public function testParseProductInfo(): void
    {
        $resultA = $this->parser->parseRefundItemAttribute($this->sampleA);
        $resultB = $this->parser->parseRefundItemAttribute($this->sampleB);

        // 购买数量
        $this->assertSame(1, $resultA['item_buy_amount']);

        // 商品单价 (分)
        $this->assertSame(39900, $resultA['item_price']);    // ¥399.00
        $this->assertSame(119900, $resultB['item_price']);   // ¥1,199.00

        // 类目
        $this->assertSame('50026924', $resultA['leaves_cat']);
        $this->assertSame('50026800', $resultA['root_cat']);
        $this->assertFalse($resultA['is_virtual']);
    }

    // ========================================================
    // 退款金额
    // ========================================================

    public function testParseRefundAmountsSampleA(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertSame(17700, $result['apply_init_refund_fee']); // 申请退款 ¥177.00
        $this->assertSame(17700, $result['ex_max_refund_fee']);     // 最大可退 ¥177.00
        $this->assertSame(17700, $result['online_refund_fee']);     // 在线退款 ¥177.00
        $this->assertSame(0, $result['refund_post_fee']);
        $this->assertSame(0, $result['main_order_post_fee']);
        $this->assertSame(0, $result['to_seller_fee']);
    }

    public function testParseRefundAmountsSampleB(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleB);

        $this->assertSame(66900, $result['apply_init_refund_fee']); // 申请退款 ¥669.00
        $this->assertSame(66900, $result['ex_max_refund_fee']);     // 最大可退 ¥669.00
        $this->assertSame(66900, $result['online_refund_fee']);     // 在线退款 ¥669.00
    }

    // ========================================================
    // 退款原因与类型
    // ========================================================

    public function testParseRefundReasonAndType(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertSame('不喜欢/不想要', $result['apply_reason_text']);
        $this->assertSame('175001', $result['apply_text_id']);
        $this->assertSame(1, $result['dispute_request']);
        $this->assertSame(4, $result['dispute_trade_status']);
        $this->assertSame('InterceptUnconsignRefund', $result['dbt']); // 拦截未发货退款
    }

    // ========================================================
    // 退款特征标识 (闪电退款判定的关键字段)
    // ========================================================

    /**
     * clj_zero_second_refund = 1 表示0秒闪电退款
     * 这是 fixInstantRefundOrders() 的判定依据
     */
    public function testParseFlashRefundFlags(): void
    {
        $resultA = $this->parser->parseRefundItemAttribute($this->sampleA);
        $resultB = $this->parser->parseRefundItemAttribute($this->sampleB);

        // 两笔样本都是闪电退款
        $this->assertTrue($resultA['clj_zero_second_refund']);
        $this->assertTrue($resultB['clj_zero_second_refund']);

        $this->assertTrue($resultA['tmg_simple_zero_refund']);
        $this->assertTrue($resultA['warehouse_refund']);
        $this->assertFalse($resultA['part_refund']);
        $this->assertTrue($resultA['percent_refund']);
        $this->assertSame('timeoutrefund^', $resultA['products']);
    }

    // ========================================================
    // 资金流 (fundFlowInfo)
    // ========================================================

    /**
     * 样本A 有 fundFlowInfo: 支付宝 ¥154.89 + 优惠 ¥22.11
     */
    public function testParseFundFlowPresent(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertCount(2, $result['fund_flow']);

        // 支付宝现金
        $this->assertSame('CASH', $result['fund_flow'][0]['type']);
        $this->assertSame('ALIPAY_FUND', $result['fund_flow'][0]['group']);
        $this->assertSame('支付宝', $result['fund_flow'][0]['name']);
        $this->assertSame(15489, $result['fund_flow'][0]['amount']); // ¥154.89

        // 优惠资产
        $this->assertSame('OTHER_ASSETS', $result['fund_flow'][1]['type']);
        $this->assertSame('OTHER_ASSETS_GROUP', $result['fund_flow'][1]['group']);
        $this->assertSame('优惠', $result['fund_flow'][1]['name']);
        $this->assertSame(2211, $result['fund_flow'][1]['amount']); // ¥22.11

        $this->assertSame('alipay', $result['pay_mode']);
        $this->assertSame('system', $result['pay_lock']);
    }

    /**
     * 样本B 没有 fundFlowInfo，fund_flow 应为空数组
     */
    public function testParseFundFlowAbsent(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleB);

        $this->assertSame([], $result['fund_flow']);
        $this->assertSame('alipay', $result['pay_mode']);
    }

    // ========================================================
    // 优惠信息 (pmtR / threshold)
    // ========================================================

    public function testParsePromotions(): void
    {
        $resultA = $this->parser->parseRefundItemAttribute($this->sampleA);
        $resultB = $this->parser->parseRefundItemAttribute($this->sampleB);

        // 样本A: 优惠券 ¥22.11
        $this->assertCount(1, $resultA['promotions']);
        $this->assertSame('TAPP_USERCOUPON_SP', $resultA['promotions'][0]['type']);
        $this->assertSame(2211, $resultA['promotions'][0]['amount']);

        // 样本B: 优惠券 ¥65.89
        $this->assertCount(1, $resultB['promotions']);
        $this->assertSame('TAPP_USERCOUPON_SP', $resultB['promotions'][0]['type']);
        $this->assertSame(6589, $resultB['promotions'][0]['amount']);

        $this->assertTrue($resultA['has_threshold_coupon']);
    }

    public function testParseThresholdInstruction(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertIsArray($result['threshold_instruction']);
        $this->assertArrayHasKey('TAPP_USERCOUPON_SP', $result['threshold_instruction']);
    }

    // ========================================================
    // 价保
    // ========================================================

    public function testParsePriceProtection(): void
    {
        $resultA = $this->parser->parseRefundItemAttribute($this->sampleA);
        $resultB = $this->parser->parseRefundItemAttribute($this->sampleB);

        // 样本A: 2026-02-24 13:47:45 ~ 2026-03-16 23:59:59
        $this->assertSame('2026-02-24 13:47:45', $resultA['price_protection']['start']);
        $this->assertSame('2026-03-16 23:59:59', $resultA['price_protection']['end']);

        // 样本B: 2026-02-24 00:04:01 ~ 2026-03-16 23:59:59
        $this->assertSame('2026-02-24 00:04:01', $resultB['price_protection']['start']);
        $this->assertSame('2026-03-16 23:59:59', $resultB['price_protection']['end']);
    }

    // ========================================================
    // 物流拦截
    // ========================================================

    public function testParseInterceptInfo(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertSame('clj', $result['intercept_type']); // 菜鸟拦截
        $this->assertSame(2, $result['intercept_status']);
        $this->assertTrue($result['ability_success_flag']);
        $this->assertSame(2, $result['warehouse_intercept_op']);
    }

    /**
     * interceptItemListResult 是编码后的 JSON
     * 解码后应为包含拦截明细的数组
     */
    public function testParseInterceptItemsJson(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertIsArray($result['intercept_items']);
        $this->assertCount(1, $result['intercept_items']);
        $this->assertSame(5081613120736477027, $result['intercept_items'][0]['subBizOrderId']);
        $this->assertSame('INTERCEPT_SUCCESS', $result['intercept_items'][0]['logisticInterceptEnum']);
        $this->assertSame('Feb 24, 2026 1:48:02 PM', $result['intercept_items'][0]['interceptDate']);
    }

    public function testParseInterceptItemsSampleB(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleB);

        $this->assertIsArray($result['intercept_items']);
        $this->assertCount(1, $result['intercept_items']);
        $this->assertSame('INTERCEPT_SUCCESS', $result['intercept_items'][0]['logisticInterceptEnum']);
        $this->assertSame('Feb 24, 2026 12:04:41 AM', $result['intercept_items'][0]['interceptDate']);
    }

    // ========================================================
    // 卖家 / 买家
    // ========================================================

    public function testParseSellerBuyerInfo(): void
    {
        $resultA = $this->parser->parseRefundItemAttribute($this->sampleA);
        $resultB = $this->parser->parseRefundItemAttribute($this->sampleB);

        $this->assertTrue($resultA['seller_batch']);
        $this->assertSame(0, $resultA['seller_audit']);
        $this->assertFalse($resultA['has_seller_memo']);
        $this->assertSame('clj', $resultA['agree_source']); // 菜鸟自动同意

        // 买家信用等级不同
        $this->assertSame(4, $resultA['user_credit']);
        $this->assertSame(2, $resultB['user_credit']);
    }

    // ========================================================
    // 其他字段
    // ========================================================

    public function testParseOtherFields(): void
    {
        $result = $this->parser->parseRefundItemAttribute($this->sampleA);

        $this->assertSame('5081613120736477027', $result['channel_sub_order_id']);
        $this->assertFalse($result['last_order']);
        $this->assertTrue($result['b2c']);
    }

    // ========================================================
    // 两笔样本的差异性对比
    // ========================================================

    /**
     * 确认两笔样本的关键差异点
     */
    public function testSampleDifferences(): void
    {
        $resultA = $this->parser->parseRefundItemAttribute($this->sampleA);
        $resultB = $this->parser->parseRefundItemAttribute($this->sampleB);

        // 金额差异
        $this->assertSame(17700, $resultA['apply_init_refund_fee']);  // ¥177
        $this->assertSame(66900, $resultB['apply_init_refund_fee']);  // ¥669

        // 商品单价差异
        $this->assertSame(39900, $resultA['item_price']);   // ¥399
        $this->assertSame(119900, $resultB['item_price']);  // ¥1,199

        // 资金流: A有，B无
        $this->assertCount(2, $resultA['fund_flow']);
        $this->assertCount(0, $resultB['fund_flow']);

        // 优惠券金额差异
        $this->assertSame(2211, $resultA['promotions'][0]['amount']);  // ¥22.11
        $this->assertSame(6589, $resultB['promotions'][0]['amount']);  // ¥65.89

        // 类目不同
        $this->assertSame('50026924', $resultA['leaves_cat']);
        $this->assertSame('50026872', $resultB['leaves_cat']);

        // 共同特征: 都是闪电退款 + 仓库拦截成功
        $this->assertTrue($resultA['clj_zero_second_refund']);
        $this->assertTrue($resultB['clj_zero_second_refund']);
    }

    // ========================================================
    // 边界情况
    // ========================================================

    public function testParseEmptyString(): void
    {
        $result = $this->parser->parseRefundItemAttribute('');

        $this->assertSame([], $result);
    }

    public function testParseSingleField(): void
    {
        $result = $this->parser->parseRefundItemAttribute(';bizCode:tmall.hk.refund;');

        $this->assertSame('tmall.hk.refund', $result['biz_code']);
        // 缺失字段使用默认值
        $this->assertNull($result['shop_name']);
        $this->assertSame(0, $result['item_price']);
        $this->assertFalse($result['clj_zero_second_refund']);
        $this->assertSame([], $result['fund_flow']);
        $this->assertSame([], $result['promotions']);
        $this->assertNull($result['price_protection']);
        $this->assertNull($result['sku']);
    }

    public function testParseNoLeadingSemicolon(): void
    {
        // attribute 可能不以 ; 开头
        $result = $this->parser->parseRefundItemAttribute('bizCode:tmall.refund;shopName:test');

        $this->assertSame('tmall.refund', $result['biz_code']);
    }
}
