package modules.user;

import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import common.jfinal.AppConfig;
import common.model.BalanceLog;
import common.model.Nftt;
import common.model.Order;
import common.model.User;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class UserTeamShareTask implements Runnable {
    Order order;
    UserService userService;

    public UserTeamShareTask(Order order, UserService userService) {
        this.userService = userService;
        this.order = order;
    }

    // 统一分润逻辑，并进行容错和类型转换 (BigDecimal)
    private void processShare(String mobileNumber, BigDecimal percentage, String description) throws Exception {
        // 确保 order.getTotalPrice() 返回的是 BigDecimal，或者转换为 BigDecimal
        BigDecimal totalPrice = order.getBigDecimal("total_price");
        if (totalPrice == null) {
            throw new RuntimeException("订单总价为空，无法分润");
        }

        // 计算分润金额
        // 使用 BigDecimal 进行精确计算，避免浮点数精度问题
        BigDecimal shareAmount = totalPrice.multiply(percentage).divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);

        // 查找用户 (userService 是注入的)
        User user = userService.findUserByMobileNumber(mobileNumber);
        if (user == null) {
            AppConfig.LOGGER.warn("分润用户 {} 不存在，无法分润", mobileNumber);
            // 根据业务需求抛出异常或跳过
            return;
        }

        // 更新用户余额
        BigDecimal currentBalance = user.getBigDecimal("balance");
        // 如果 balance 字段为 null，初始化为 0
        if (currentBalance == null) {
            currentBalance = BigDecimal.ZERO;
        }
        user.set("balance", currentBalance.add(shareAmount));
        if (!user.update()) {
            throw new RuntimeException("更新用户 " + user.getStr("nickname") + " 余额失败");
        }

        // 记录分润日志
        BalanceLog log = new BalanceLog();
        log.set("create_time", System.currentTimeMillis());
        log.set("is_deleted", 0);
        log.set("description", description + " " + percentage.intValue() + "%"); // 描述中带上百分比
        log.set("amount", shareAmount);
        log.set("user_id", user.getLong("id"));
        if (!log.save()) {
            throw new RuntimeException("保存分润日志失败 for user " + user.getStr("nickname"));
        }
        AppConfig.LOGGER.info("{} {}: {}", description, user.getStr("nickname"), shareAmount);
    }

    /**
     * 第一类、单独账号分润
     *      0. 大领导 2%
     *      1. 团队奖励 5%
     *      2. 市场预留 3%
     *      4. 宣传外联 4%
     *      4. 技术公司运维 1%
     *      5. 平台费用 1%
     */
    public void class1() throws Exception {
        AppConfig.LOGGER.info("正在处理订单 {} 的第一类分润", order.getLong("id"));

        // 确保这些手机号是常量，或者从配置中读取
        processShare("27788881000", BigDecimal.valueOf(2), "大领导分润");
        processShare("27788881001", BigDecimal.valueOf(5), "团队奖励");
        processShare("27788881002", BigDecimal.valueOf(3), "市场预留");
        processShare("27788881003", BigDecimal.valueOf(4), "宣传外联");
        // 注意这里是 1%，用 BigDecimal.valueOf(1)
        processShare("27788881004", BigDecimal.valueOf(1), "技术公司运维");
        processShare("27788881005", BigDecimal.valueOf(1), "平台费用");

        AppConfig.LOGGER.info("订单 {} 第一类分润处理完成", order.getLong("id"));
    }

    /**
     * 第二类、直推分润 10%
     */
    public void class2() {
        User user = User.dao.findById(order.getUserId());
        User referrerUser = User.dao.findById(user.getReferrerId());
        
        // 直推人不存在，直接跳过
        if (referrerUser == null) {
            return;
        }

        // 确保 order.getTotalPrice() 返回的是 BigDecimal，或者转换为 BigDecimal
        BigDecimal totalPrice = order.getBigDecimal("total_price");
        BigDecimal percentage = BigDecimal.valueOf(10);
        if (totalPrice == null) {
            throw new RuntimeException("订单总价为空，无法分润");
        }
        // 直推10%分润
        BigDecimal shareAmount = totalPrice.multiply(percentage).divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);
        // 更新用户余额
        BigDecimal currentBalance = referrerUser.getBigDecimal("balance");
        // 如果 balance 字段为 null，初始化为 0
        if (currentBalance == null) {
            currentBalance = BigDecimal.ZERO;
        }
        referrerUser.set("balance", currentBalance.add(shareAmount));
        if (!referrerUser.update()) {
            throw new RuntimeException("更新用户 " + referrerUser.getMobileNumber() + " 余额失败");
        }

        // 记录分润日志
        String description = "直推分润";
        BalanceLog log = new BalanceLog();
        log.set("create_time", System.currentTimeMillis());
        log.set("is_deleted", 0);
        log.set("description", description + " " + percentage.intValue() + "%"); // 描述中带上百分比
        log.set("amount", shareAmount);
        log.set("user_id", referrerUser.getLong("id"));
        if (!log.save()) {
            throw new RuntimeException("保存分润日志失败 for user " + referrerUser.getMobileNumber());
        }
        AppConfig.LOGGER.info("{} {}: {}", description, referrerUser.getMobileNumber(), shareAmount);
    }

    /**
     * 第三类、NFT持有者分润
     *      1. 10%的部分，分母是20000
     *      2. 4%的部分，分母是当前持有者数量
     */
    public void class3() {
        String queryUserIdSql = "SELECT user_id, COUNT(id) AS valid_order_count " +
                        "FROM t_order " +
                        "WHERE order_status = 60 AND delivery_status = 1 " +
                        "GROUP BY user_id HAVING COUNT(id) > 0";
        List<Record> userOrderCounts = Db.find(queryUserIdSql);
        // 提取所有唯一的 user_id，用于下一步批量查询 User 对象
        Set<Long> eligibleUserIds = userOrderCounts.stream()
                .map(record -> record.getLong("user_id"))
                .collect(Collectors.toSet());
        // 如果没有符合条件的用户，直接返回
        if (eligibleUserIds.isEmpty()) {
            AppConfig.LOGGER.info("没有符合分润条件的用户。");
            return;
        }

        // 第二步：根据这些 user_id，批量查询出对应的 User 对象
        // JFinal 的 IN 操作比较方便，但是当列表过大时，分批查询 User 仍然是最佳实践
        // 假设 eligibleUserIds 不会过大，可以直接查询
        // String inSql = eligibleUserIds.stream()
        //         .map(String::valueOf)
        //         .collect(Collectors.joining(","));

        AppConfig.LOGGER.info("开始查询用户，符合条件的用户ID数量: {}", eligibleUserIds.size());
        // ******** 关键改进：分批查询 User 对象 ********
        final int BATCH_QUERY_SIZE = 1000; // 每批次查询 1000 个用户
        List<User> userList = new ArrayList<>(eligibleUserIds.size()); // 预估大小，减少扩容开销

        // 将 Set 转换为 List，方便进行 subList 分批操作
        List<Long> eligibleUserIdsList = new ArrayList<>(eligibleUserIds);

        for (int i = 0; i < eligibleUserIdsList.size(); i += BATCH_QUERY_SIZE) {
            // 获取当前批次的 user_id 子列表
            List<Long> subList = eligibleUserIdsList.subList(i, Math.min(i + BATCH_QUERY_SIZE, eligibleUserIdsList.size()));

            String subInSql = subList.stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));

            // 执行 IN 查询，添加到总的 userList 中
            userList.addAll(User.dao.find("SELECT id, balance FROM t_user WHERE id IN (" + subInSql + ")"));
            AppConfig.LOGGER.debug("已查询用户批次 {} 到 {}，总已查询用户数: {}", i, Math.min(i + BATCH_QUERY_SIZE, eligibleUserIdsList.size()) -1 , userList.size());
        }
        // ******** 分批查询 User 对象结束 ********
        AppConfig.LOGGER.info("用户查询完毕，共查询到 {} 个符合用户。", userList.size());

        // 为了方便处理，将 list 转换为 map，userOrderCounts 也转换为 map
        Map<Long, User> userMap = userList.stream()
                .collect(Collectors.toMap(User::getId, user -> user));

        Map<Long, Integer> userValidOrderCountMap = userOrderCounts.stream()
                .collect(Collectors.toMap(
                        record -> record.getLong("user_id"),
                        record -> record.getLong("valid_order_count").intValue()
                ));

        // 现在，你可以遍历 userOrderCounts，结合 userMap 进行分润处理
        List<User> usersToUpdate = new ArrayList<>();
        List<BalanceLog> depositLogsToSave = new ArrayList<>();

        // 假设分润规则是：每个用户获得的总利润 = (该用户的有效订单数量 / 所有用户的总有效订单数量) * 总利润
        // 或者：简单地每个用户的固定份额
        // 这里我们用总订单数量来计算每个用户的分润比例
        long totalValidOrders = userValidOrderCountMap.values().stream().mapToLong(Integer::longValue).sum();
        // 避免除以零
        if (totalValidOrders == 0) { 
            AppConfig.LOGGER.warn("所有符合条件用户的总订单数为0，无法按订单比例分润。");
            return;
        }

        // 总价值
        BigDecimal totalPrice = BigDecimal.valueOf(order.getTotalPrice());
        Nftt nftt = Nftt.dao.findById(10000);
        
        if (nftt == null) {
            AppConfig.LOGGER.warn("ID为10000的NFT找不到，无法分润！");
            return;
        }
        
        for (Record record : userOrderCounts) {
            // 参与分润的用户
            Long userId = record.getLong("user_id");
            // 当前用户的有效订单数量
            int validOrderCount = record.getLong("valid_order_count").intValue();

            User user = userMap.get(userId);
            if (user == null) {
                AppConfig.LOGGER.warn("未找到用户ID为 {} 的User对象，跳过分润。", userId);
                continue;
            }

            // 分润1，其中4%作为NFT持有者分享(0.04 * validOrderCount / totalValidOrders)
            BigDecimal shareArtio1 = BigDecimal.valueOf(0.04);
            BigDecimal userShareRatio1 = shareArtio1.multiply(BigDecimal.valueOf(validOrderCount))
                    .divide(BigDecimal.valueOf(totalValidOrders), 8, RoundingMode.HALF_UP);// 确保高精度
            BigDecimal shareAmount1 = totalPrice.multiply(userShareRatio1).setScale(2, RoundingMode.HALF_UP); // 四舍五入到两位小数
            // 分润2，NFT持有者共享总数的10%，还没售卖出去的部分归平台所有 (0.1 * validOrderCount / Nftt.maxQuantity)
            BigDecimal shareArtio2 = BigDecimal.valueOf(0.1);
            BigDecimal userShareRatio2 = shareArtio2.multiply(BigDecimal.valueOf(validOrderCount))
                    .divide(BigDecimal.valueOf(nftt.getMaxQuantity()), 8, RoundingMode.HALF_UP);// 确保高精度，注意这里的底数是MaxQuantity
            BigDecimal shareAmount2 = totalPrice.multiply(userShareRatio2).setScale(2, RoundingMode.HALF_UP); // 四舍五入到两位小数

            // 更新用户余额
            BigDecimal currentBalance = user.getBigDecimal("balance");
            if (currentBalance == null) {
                currentBalance = BigDecimal.ZERO;
            }
            user.set("balance", currentBalance.add(shareAmount1).add(shareAmount2).floatValue());
            usersToUpdate.add(user);

            // 创建分润日志
            BalanceLog log1 = new BalanceLog();
            log1.set("create_time", System.currentTimeMillis());
            log1.set("is_deleted", 0);
            log1.set("description", "4%作为NFT持有者分享，" + validOrderCount + " / " + totalValidOrders); // 加入订单数量信息
            log1.set("amount", shareAmount1);
            log1.set("user_id", userId);
            depositLogsToSave.add(log1);
            
            BalanceLog log2 = new BalanceLog();
            log2.set("create_time", System.currentTimeMillis());
            log2.set("is_deleted", 0);
            log2.set("description", "NFT持有者共享总数的10%，" + validOrderCount + " / " + nftt.getMaxQuantity()); // 加入订单数量信息
            log2.set("amount", shareAmount2);
            log2.set("user_id", userId);
            depositLogsToSave.add(log2);
        }

        // 在一个事务中执行所有批量更新和插入
        Db.tx(() -> {
            int[] userUpdateResult = Db.batchUpdate(usersToUpdate, 1000); // 批量更新用户
            int[] logSaveResult = Db.batchSave(depositLogsToSave, 1000);   // 批量保存日志

            AppConfig.LOGGER.info("已批量更新 {} 个用户余额，批量保存 {} 条分润日志。", userUpdateResult.length, logSaveResult.length);
            return true; // 事务成功
        });
    }

    /**
     * 第四类、持有者激励分润，这个是甲方手动分润
     */
//    public void class4() {}
    
    @Override
    public void run() {
        // 确保整个分润过程在一个数据库事务中
        // 因为操作涉及到多个 User 和 BalanceLog 的更新和插入
        Db.tx(() -> {
            try {
                this.class1();
                this.class2();
                this.class3();
//                this.class4();
                return true; // 事务成功
            } catch (Exception e) {
                AppConfig.LOGGER.error("订单分润处理异常，订单ID: {}, 错误: {}", order.getLong("id"), e.getMessage(), e);
                return false; // 事务失败，回滚
            }
        });
    }
}
