package com.ruoyi.ss.bonus.service.impl;

import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.domain.vo.CommonCountVO;
import com.ruoyi.common.domain.vo.CommonSumVO;
import com.ruoyi.common.domain.vo.LocalDateDecimalVO;
import com.ruoyi.common.domain.vo.LongDecimalVO;
import com.ruoyi.common.enums.LoginType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.bonus.domain.Bonus;
import com.ruoyi.ss.bonus.domain.BonusProvideQuery;
import com.ruoyi.ss.bonus.domain.BonusQuery;
import com.ruoyi.ss.bonus.domain.BonusVO;
import com.ruoyi.ss.bonus.domain.enums.BonusArrivalType;
import com.ruoyi.ss.bonus.domain.enums.BonusStatus;
import com.ruoyi.ss.bonus.domain.vo.BonusDailyAmountVO;
import com.ruoyi.ss.bonus.domain.vo.BonusMonthAmountVO;
import com.ruoyi.ss.bonus.domain.vo.ProvideBonusVO;
import com.ruoyi.ss.bonus.mapper.BonusMapper;
import com.ruoyi.ss.bonus.service.BonusService;
import com.ruoyi.ss.recordBalance.domain.enums.RecordBalanceBstType;
import com.ruoyi.ss.store.service.StoreService;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.user.service.ISmUserService;
import com.ruoyi.ss.user.service.UserAssembler;
import com.ruoyi.system.domain.enums.config.ConfigKey;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysDeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 分成明细Service业务层处理
 *
 * @author ruoyi
 * @date 2024-08-20
 */
@Service
@Slf4j
public class BonusServiceImpl implements BonusService
{
    @Autowired
    private BonusMapper bonusMapper;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private ISmUserService userService;

    @Autowired
    private ISysDeptService deptService;

    @Autowired
    private StoreService storeService;

    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private UserAssembler userAssembler;

    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    /**
     * 查询分成明细
     *
     * @param id 分成明细主键
     * @return 分成明细
     */
    @Override
    public BonusVO selectBonusById(Long id)
    {
        return bonusMapper.selectBonusById(id);
    }

    /**
     * 查询分成明细列表
     *
     * @param bonus 分成明细
     * @return 分成明细
     */
    @Override
    public List<BonusVO> selectBonusList(BonusQuery bonus)
    {
        return bonusMapper.selectBonusList(bonus);
    }

    /**
     * 新增分成明细
     *
     * @param bonus 分成明细
     * @return 结果
     */
    @Override
    public int insertBonus(Bonus bonus)
    {
        this.buildBeforeCreate(bonus);
        return bonusMapper.insertBonus(bonus);
    }

    // 补全新增前的数据
    private void buildBeforeCreate(Bonus bonus) {
        bonus.setCreateTime(DateUtils.getNowDate());
    }

    /**
     * 修改分成明细
     *
     * @param bonus 分成明细
     * @return 结果
     */
    @Override
    public int updateBonus(Bonus bonus)
    {
        return bonusMapper.updateBonus(bonus);
    }

    /**
     * 批量删除分成明细
     *
     * @param ids 需要删除的分成明细主键
     * @return 结果
     */
    @Override
    public int deleteBonusByIds(Long[] ids)
    {
        return bonusMapper.deleteBonusByIds(ids);
    }

    /**
     * 删除分成明细信息
     *
     * @param id 分成明细主键
     * @return 结果
     */
    @Override
    public int deleteBonusById(Long id)
    {
        return bonusMapper.deleteBonusById(id);
    }

    @Override
    public int batchInsert(List<Bonus> bonusList) {
        if (CollectionUtils.isEmptyElement(bonusList)) {
            return 0;
        }
        for (Bonus bonus : bonusList) {
            this.buildBeforeCreate(bonus);
        }
        return bonusMapper.batchInsert(bonusList);
    }



    @Override
    public int payBonus(List<BonusVO> bonusList) {
        int total = 0;

        // 循环遍历，添加金额到账户上
        for (BonusVO bonus : bonusList) {
            int pay = this.payBonus(bonus);
            total += pay;
        }

        return total;
    }

    @Override
    public int payBonus(BonusVO bonus) {
        if (bonus == null) {
            return 0;
        }
        Integer result = transactionTemplate.execute(status -> {
            // 更新分成状态为已分成
            int pay = bonusMapper.pay(bonus.getId(), bonus.getWaitAmount(), LocalDateTime.now());
            ServiceUtil.assertion(pay != 1, "分成打款失败，待分金额不足或状态已改变");

            // 根据类型，添加金额
            if (BonusArrivalType.userList().contains(bonus.getArrivalType())) {
                int add = userService.addBalance(bonus.getArrivalId(), bonus.getWaitAmount(), String.format("订单分成:%s", bonus.getBillNo()), RecordBalanceBstType.RECHARGE, bonus.getBillId());
                ServiceUtil.assertion(add != 1, "增加账户金额失败");
            } else if (BonusArrivalType.deptList().contains(bonus.getArrivalType())) {
//                    add = deptService.addBalance(bonus.getArrivalId(), bonus.getAmount(), String.format("订单分成:%s", bonus.getBillNo()), RecordBalanceBstType.RECHARGE, bonus.getBillId());
            }

            return pay;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int partBonus(List<BonusVO> bonusList, BigDecimal money) {
        if (CollectionUtils.isEmptyElement(bonusList) || money == null) {
            return 0;
        }

        BigDecimal decimal100 = new BigDecimal(100);
        BigDecimal dividedAmount = BigDecimal.ZERO; // 已分配金额

        // 获取用户列表
        List<SmUserVO> userList = userService.selectByUserIds(bonusList.stream()
                .filter(item -> BonusArrivalType.userList().contains(item.getArrivalType()))
                .map(BonusVO::getArrivalId)
                .collect(Collectors.toList())
        );
        // 拼接用户实际到账延迟
        userAssembler.assembleRealArrivalDelay(userList);

        LocalDateTime now = LocalDateTime.now();

        // 获取平台的分成
        BonusVO platform = bonusList.stream().filter(bonus -> bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())).findFirst().orElse(null);
        ServiceUtil.assertion(platform == null, "平台不存在");
        BigDecimal platformAmount = money.multiply(platform.getPoint()).divide(decimal100, 2, RoundingMode.HALF_UP);    // 平台预计分成金额

        // 处理最低分成
        BigDecimal minService = sysConfigService.getBigDecimal(ConfigKey.RECHARGE_MIN_SERVICE);
        // 若总金额 < 最低分成，则平台全收
        if (money.compareTo(minService) < 0) {
            this.setAmount(platform, money);
            dividedAmount = platform.getAmount();
        }
        // 若平台分成 < 最低金额，则收取最低金额，其他金额给其他分成方分成
        else if (platformAmount.compareTo(minService) < 0) {
            // 平台设置分成为最低金额
            this.setAmount(platform, minService);
            dividedAmount = platform.getAmount();

            // 计算其他分成方分成金额
            BigDecimal remain = money.subtract(platform.getAmount());           // 剩余可分成金额
            BigDecimal remainPoint = decimal100.subtract(platform.getPoint());  // 剩余总百分比
            for (BonusVO bonus : bonusList) {
                if (bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())) {
                    continue;
                }
                // 计算分成比例占剩余可分成金额的比例
                BigDecimal bonusPoint = bonus.getPoint().multiply(decimal100).divide(remainPoint, 2, RoundingMode.HALF_UP);
                BigDecimal amount = remain.multiply(bonusPoint).divide(decimal100, 2, RoundingMode.HALF_UP);
                this.setAmount(bonus, amount);
                // 增加已分成金额
                dividedAmount = dividedAmount.add(amount);
            }
        }
        // 其余情况，正常收取费用
        else {
            // 循环遍历，构造分成金额
            for (BonusVO bonus : bonusList) {
                BigDecimal amount = money.multiply(bonus.getPoint()).divide(decimal100, 2, RoundingMode.HALF_UP);
                this.setAmount(bonus, amount);
                // 增加已分成金额
                dividedAmount = dividedAmount.add(amount);
            }
        }

        // 若存在误差，则平台吃掉误差
        if (dividedAmount.compareTo(money) != 0) {
            BigDecimal subtract = money.subtract(dividedAmount);    // 误差值
            this.setAmount(platform, platform.getAmount().add(subtract));
        }

        // 设置预计分成时间等数据
        for (BonusVO bonus : bonusList) {
            bonus.setStatus(BonusStatus.WAIT_DIVIDE.getStatus());
            bonus.setWaitAmount(bonus.getAmount());
            // 设置预计分成时间
            if (BonusArrivalType.userList().contains(bonus.getArrivalType())) {
                SmUserVO user = userList.stream().filter(item -> item.getUserId().equals(bonus.getArrivalId())).findFirst().orElse(null);
                if (user == null) {
                    continue;
                }
                bonus.setPrePayTime(now.plusHours(user.getRealArrivalDelay()));
            } else {
                bonus.setPrePayTime(now);
            }
        }

        Integer result = transactionTemplate.execute(status -> {
            // 批量更新分成金额
            int updateAmount = this.batchUpdateAmount(bonusList);
            ServiceUtil.assertion(updateAmount != bonusList.size(), "更新分成金额失败");

            return updateAmount;
        });

        // 立即执行一次分成
        scheduledExecutorService.schedule(() -> {
            this.payBonusBeforeTime(LocalDateTime.now());
        }, 3, TimeUnit.SECONDS);

        return result == null ? 0 : result;
    }

    private void setAmount(BonusVO bonus, BigDecimal amount) {
        ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "分成金额不允许小于0");
        bonus.setAmount(amount);
    }

    @Override
    public List<CommonCountVO<Long>> selectCountByArrival(BonusQuery query) {
        return bonusMapper.selectCountByArrival(query);
    }

    @Override
    public List<CommonSumVO<Long>> selectBillAmountByArrival(BonusQuery query) {
        return bonusMapper.selectBillAmountByArrival(query);
    }

    @Override
    public List<ProvideBonusVO> selectProvideBonus(BonusProvideQuery query) {
        return bonusMapper.selectProvideBonus(query);
    }

    @Override
    public int refundWhenWaitDivide(Long id, BigDecimal amount) {
        if (id == null) {
            return 0;
        }
        ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "退款金额必须大于0");
        return bonusMapper.refundWhenWaitDivide(id, amount);
    }

    @Override
    public int refundWhenDividend(Long id, BigDecimal amount) {
        if (id == null) {
            return 0;
        }
        ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "退款金额必须大于0");
        return bonusMapper.refundWhenDividend(id, amount);
    }

    @Override
    public <T extends Bonus> List<T> filterBonusScope(List<T> bonusList) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long userId = loginUser.getUserId();
        Long deptId = loginUser.getDeptId();
        return bonusList.stream().filter(bonus -> {
            // 前台用户过滤
            if (LoginType.FRONT.equals(loginUser.getLoginType())) {
                return BonusArrivalType.userList().contains(bonus.getArrivalType()) && (
                        Arrays.asList(bonus.getAncestors().split(",")).contains(userId.toString())
                        || bonus.getArrivalId().equals(userId)
                );
            }
            // 后台用户过滤
            else if (LoginType.ADMIN.equals(loginUser.getLoginType())) {
                if (BonusArrivalType.userList().contains(bonus.getArrivalType())) {
                    return true;
                }
                if (BonusArrivalType.deptList().contains(bonus.getArrivalType())) {
                    return Arrays.asList(bonus.getAncestors().split(",")).contains(deptId.toString())
                            || bonus.getArrivalId().equals(deptId);
                }
            }
            return false;
        }).collect(Collectors.toList());
    }

    @Override
    public List<BonusMonthAmountVO> selectMonthPayedAmount(BonusQuery query) {
        return bonusMapper.selectMonthPayedAmount(query);
    }

    @Override
    public List<BonusDailyAmountVO> selectDailyAmount(BonusQuery query) {
        return bonusMapper.selectDailyAmount(query);
    }

    @Override
    public int selectCount(BonusQuery query) {
        return bonusMapper.selectCount(query);
    }

    @Override
    public int payBonusBeforeTime(LocalDateTime time) {
        // 查询预计分成时间当前时间之前，且为待分成的分成单
        BonusQuery query = new BonusQuery();
        query.setStatus(BonusStatus.WAIT_DIVIDE.getStatus());
        query.setPrePayTimeEnd(time);
        List<BonusVO> list = this.selectBonusList(query);

        if (CollectionUtils.isEmptyElement(list)) {
            log.info("暂无待分成的分成单");
            return 0;
        }

        int sum = 0;
        for (BonusVO bonus : list) {
            try {
                sum += this.payBonus(bonus);
            } catch (Exception e) {
                log.warn("分成打款失败：id={}, e={}", bonus.getId(), e.getMessage());
            }
        }

        return sum;
    }

    @Override
    public List<LongDecimalVO> selectWaitAmountGroupByArrival(BonusQuery query) {
        return bonusMapper.selectWaitAmountGroupByArrival(query);
    }

    @Override
    public int payBonusById(Long id) {
        BonusVO bonus = this.selectBonusById(id);
        return this.payBonus(bonus);
    }

    @Override
    public List<LocalDateDecimalVO> selectDailyPayedAmount(BonusQuery query) {
        return bonusMapper.selectDailyPayedAmount(query);
    }

    @Override
    public BigDecimal selectSumOfPayedAmount(BonusQuery query) {
        return bonusMapper.selectSumOfPayedAmount(query);
    }

    private int batchUpdateAmount(List<BonusVO> list) {
        if (CollectionUtils.isEmptyElement(list)) {
            return 0;
        }
        return bonusMapper.batchUpdateAmount(list);
    }
}
