package com.fs.app.course;

import com.fs.FsUserAppApplication;
import com.fs.common.constant.FsConstants;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.core.domain.R;
import com.fs.course.mapper.FsCourseRedPacketLogMapper;
import com.fs.course.param.FsCourseSendRewardUParam;
import com.fs.course.service.IFsUserCourseVideoService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;

import static org.junit.Assert.*;

/**
 * 红包余额为0测试类
 * 测试场景：公司红包余额为0时，不能发送红包
 *
 * @author test
 * @date 2025-12-30
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FsUserAppApplication.class)
public class RedPacketBalanceZeroTest {

    @Autowired
    private IFsUserCourseVideoService courseVideoService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private FsCourseRedPacketLogMapper redPacketLogMapper;

    // ==================== 测试数据配置 ====================
    private static final Long TEST_COMPANY_ID = 300L;        // 测试公司ID（请根据实际调整）
    private static final Long TEST_COURSE_ID = 104L;       // 测试课程ID
    private static final Long TEST_VIDEO_ID = 3877L;         // 测试视频ID
    private static final Long TEST_USER_ID = 459809L;        // 测试用户ID
    private static final Long TEST_COMPANY_USER_ID = 9587L; // 测试公司用户ID
    private static final Long TEST_PERIOD_ID = 705L;         // 测试期数ID

    private String companyMoneyKey;
    private String originalBalance; // 保存原始余额，用于恢复

    @Before
    public void setUp() {
        System.out.println("\n" + repeat("=", 60));
        System.out.println("    开始测试：红包余额为0不能发放红包");
        System.out.println(repeat("=", 60));

        // 初始化Redis Key
        companyMoneyKey = FsConstants.COMPANY_MONEY_KEY + TEST_COMPANY_ID;

        // 保存原始余额
        originalBalance = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("📌 测试准备：保存原始余额 = " + originalBalance);
    }

    /**
     * 核心测试用例：余额为0时不能发送红包
     * 验证点：
     * 1. 设置余额为0后，发送红包应返回错误
     * 2. 错误信息应包含"余额不足"
     * 3. Redis余额应保持为0，未被扣减
     * 4. 数据库不应生成成功的红包记录
     */
    @Test
    public void testSendRedPacketWhenBalanceIsZero() {
        System.out.println("\n🧪 测试用例：余额为0时不能发送红包");
        System.out.println(repeat("-", 60));

        // ==================== 步骤1：设置Redis余额为0 ====================
        System.out.println("\n📍 步骤1：设置Redis余额为0");
        redisCache.setCacheObject(companyMoneyKey, "0");

        String currentBalance = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("   Redis Key: " + companyMoneyKey);
        System.out.println("   当前余额: " + currentBalance);
        assertEquals("初始余额应为0", "0", currentBalance);
        System.out.println("   ✅ 余额设置成功");

        // ==================== 步骤2：构造请求参数 ====================
        System.out.println("\n📍 步骤2：构造发送红包请求参数");
        FsCourseSendRewardUParam param = new FsCourseSendRewardUParam();
        param.setCourseId(TEST_COURSE_ID);
        param.setVideoId(TEST_VIDEO_ID);
        param.setUserId(TEST_USER_ID);
        param.setCompanyId(TEST_COMPANY_ID);
        param.setCompanyUserId(TEST_COMPANY_USER_ID);
        param.setPeriodId(TEST_PERIOD_ID);
        param.setQwUserId("test_qw_user");
        param.setAppId("test_app_id");
        param.setQwExternalId(1L);

        System.out.println("   参数信息：");
        System.out.println("   - 公司ID: " + param.getCompanyId());
        System.out.println("   - 课程ID: " + param.getCourseId());
        System.out.println("   - 视频ID: " + param.getVideoId());
        System.out.println("   - 用户ID: " + param.getUserId());
        System.out.println("   ✅ 参数构造完成");

        // ==================== 步骤3：调用发送红包接口 ====================
        System.out.println("\n📍 步骤3：调用发送红包接口");
        R result = null;
        try {
            result = courseVideoService.sendReward(param);
        } catch (Exception e) {
            System.out.println("   ⚠️  接口调用异常: " + e.getMessage());
        }

        // ==================== 步骤4：验证返回结果 ====================
        System.out.println("\n📍 步骤4：验证返回结果");

        assertNotNull("返回结果不应为空", result);
        System.out.println("   返回码: " + result.get("code"));
        System.out.println("   返回消息: " + result.get("msg"));

        // 验证返回错误
        Integer code = (Integer) result.get("code");
        assertTrue("应返回错误状态码 (500或400)",
            code == 500 || code == 400);

        // 验证错误消息包含"余额不足"
        String msg = String.valueOf(result.get("msg"));
        assertTrue("错误信息应包含'余额不足'",
            msg != null && msg.contains("余额不足"));

        System.out.println("   ✅ 返回错误提示正确");

        // ==================== 步骤5：验证Redis余额未被扣减 ====================
        System.out.println("\n📍 步骤5：验证Redis余额未被扣减");

        String finalBalance = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("   最终余额: " + finalBalance);

        assertEquals("余额应保持为0，未被扣减", "0", finalBalance);
        System.out.println("   ✅ 余额保持为0，未被扣减");

        // ==================== 步骤6：验证数据库无成功记录 ====================
        System.out.println("\n📍 步骤6：验证数据库日志");
        System.out.println("   提示：请手动查询数据库验证");
        System.out.println("   SQL: SELECT * FROM fs_course_red_packet_log");
        System.out.println("        WHERE company_id = " + TEST_COMPANY_ID);
        System.out.println("        ORDER BY create_time DESC LIMIT 5;");

        // ==================== 测试结论 ====================
        System.out.println("\n" + repeat("=", 60));
        System.out.println("    ✅ 测试通过：余额为0时成功阻止红包发放");
        System.out.println(repeat("=", 60));
    }

    /**
     * 测试用例2：余额不足时不能发送红包
     */
    @Test
    public void testSendRedPacketWhenBalanceNotEnough() {
        System.out.println("\n🧪 测试用例：余额不足时不能发送红包");
        System.out.println(repeat("-", 60));

        // 设置余额为3元（假设红包金额为5元）
        System.out.println("\n📍 步骤1：设置Redis余额为3元");
        redisCache.setCacheObject(companyMoneyKey, "3");

        String currentBalance = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("   当前余额: " + currentBalance);

        // 构造请求
        System.out.println("\n📍 步骤2：调用发送红包接口");
        FsCourseSendRewardUParam param = new FsCourseSendRewardUParam();
        param.setCourseId(TEST_COURSE_ID);
        param.setVideoId(TEST_VIDEO_ID);
        param.setUserId(TEST_USER_ID);
        param.setCompanyId(TEST_COMPANY_ID);
        param.setCompanyUserId(TEST_COMPANY_USER_ID);
        param.setPeriodId(TEST_PERIOD_ID);
        param.setQwUserId("test_qw_user");
        param.setAppId("test_app_id");
        param.setQwExternalId(1L);

        R result = courseVideoService.sendReward(param);

        // 验证结果
        System.out.println("\n📍 步骤3：验证返回结果");
        System.out.println("   返回码: " + result.get("code"));
        System.out.println("   返回消息: " + result.get("msg"));

        Integer code = (Integer) result.get("code");
        assertTrue("应返回错误状态码", code == 500 || code == 400);

        // 验证余额未变化
        String finalBalance = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("   最终余额: " + finalBalance);

        System.out.println("\n   ✅ 余额不足时成功阻止红包发放");
    }

    /**
     * 测试用例3：余额充足时能正常发送红包（可选，需要真实支付配置）
     */
    @Test
    public void testSendRedPacketWhenBalanceEnough() {
        System.out.println("\n🧪 测试用例：余额充足时能正常发送红包");
        System.out.println(repeat("-", 60));
        System.out.println("⚠️  注意：此测试需要真实的支付配置和红包接口");
        System.out.println("   建议在测试环境执行，避免产生真实交易");

        // 设置充足余额
        System.out.println("\n📍 步骤1：设置Redis余额为100元");
        redisCache.setCacheObject(companyMoneyKey, "100");

        String currentBalance = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("   当前余额: " + currentBalance);

        // 构造请求
        FsCourseSendRewardUParam param = new FsCourseSendRewardUParam();
        param.setCourseId(TEST_COURSE_ID);
        param.setVideoId(TEST_VIDEO_ID);
        param.setUserId(TEST_USER_ID);
        param.setCompanyId(TEST_COMPANY_ID);
        param.setCompanyUserId(TEST_COMPANY_USER_ID);
        param.setPeriodId(TEST_PERIOD_ID);
        param.setQwUserId("test_qw_user");
        param.setAppId("test_app_id");
        param.setQwExternalId(1L);

        System.out.println("\n📍 步骤2：调用发送红包接口（跳过真实发送）");
        System.out.println("   提示：如需完整测试，请配置测试环境的支付接口");

        // 注：此处可根据实际情况决定是否执行
        // R result = courseVideoService.sendReward(param);
    }

    /**
     * 测试用例4：并发场景测试（可选）
     */
    @Test
    public void testConcurrentSendRedPacketWithZeroBalance() {
        System.out.println("\n🧪 测试用例：并发场景下余额为0的保护");
        System.out.println(repeat("-", 60));

        // 设置余额为0
        redisCache.setCacheObject(companyMoneyKey, "0");
        System.out.println("   初始余额: 0");

        // 模拟3个并发请求
        int threadCount = 3;
        System.out.println("\n📍 模拟" + threadCount + "个并发请求");

        for (int i = 1; i <= threadCount; i++) {
            System.out.println("\n   请求" + i + ":");

            FsCourseSendRewardUParam param = new FsCourseSendRewardUParam();
            param.setCourseId(TEST_COURSE_ID);
            param.setVideoId(TEST_VIDEO_ID);
            param.setUserId(TEST_USER_ID + i); // 不同用户
            param.setCompanyId(TEST_COMPANY_ID);
            param.setCompanyUserId(TEST_COMPANY_USER_ID + i);
            param.setPeriodId(TEST_PERIOD_ID);
            param.setQwUserId("test_qw_user_" + i);
            param.setAppId("test_app_id");
            param.setQwExternalId(1L);

            R result = courseVideoService.sendReward(param);
            System.out.println("   - 返回码: " + result.get("code"));
            System.out.println("   - 消息: " + result.get("msg"));
        }

        // 验证余额仍为0
        String finalBalance = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("\n   最终余额: " + finalBalance);
        assertEquals("并发请求后余额应仍为0", "0", finalBalance);

        System.out.println("   ✅ 并发场景下余额保护正常");
    }

    @After
    public void tearDown() {
        // 恢复原始余额
        if (originalBalance != null) {
            System.out.println("\n🔄 测试清理：恢复原始余额 = " + originalBalance);
            redisCache.setCacheObject(companyMoneyKey, originalBalance);
        } else {
            System.out.println("\n🔄 测试清理：删除测试数据");
            redisCache.deleteObject(companyMoneyKey);
        }

        System.out.println("✅ 测试清理完成\n");
    }

    /**
     * 工具方法：查看当前Redis余额
     */
    private void printCurrentBalance() {
        String balance = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("💰 当前Redis余额: " + balance);
    }

    /**
     * 工具方法：验证余额
     */
    private void assertBalance(String expected, String message) {
        String actual = redisCache.getCacheObject(companyMoneyKey);
        System.out.println("   预期余额: " + expected);
        System.out.println("   实际余额: " + actual);
        assertEquals(message, expected, actual);
    }

    /**
     * 工具方法：替代String.repeat()方法 (JDK 8兼容)
     */
    private String repeat(String str, int count) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < count; i++) {
            sb.append(str);
        }
        return sb.toString();
    }
}
