package com.dayou.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dayou.common.BaseEntity;
import com.dayou.dto.OrderReportDTO;
import com.dayou.dto.PersonalClaimDTO;
import com.dayou.dto.RealFundAssetsStatDTO;
import com.dayou.dto.history.HisClaimProd;
import com.dayou.entity.*;
import com.dayou.enums.MainBusinessEnum;
import com.dayou.exception.ErrorCode;
import com.dayou.mapper.FinanceClaimMapper;
import com.dayou.mapper.OrderFundMapper;
import com.dayou.mapper.UserMapper;
import com.dayou.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dayou.utils.ExcelUtil;
import com.dayou.utils.LoginContext;
import com.dayou.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import static com.dayou.enums.MainBusinessEnum.PERSONAL_BUSINESS;

/**
 * <p>
 * 实收款认领 服务实现类
 * </p>
 *
 * @author wucl
 * @since 2023-12-08
 */
@DS("mbs")
@Service
public class FinanceClaimServiceImpl extends ServiceImpl<FinanceClaimMapper, FinanceClaim> implements IFinanceClaimService {

    @Autowired
    private FinanceClaimMapper financeClaimMapper;

    @Autowired
    private IFinanceRealFundService financeRealFundService;

    @Autowired
    private IProductionFundService productionFundService;

    @Autowired
    private IPersonalService personalService;

    @Autowired
    private IOrderFundService orderFundService;

    @Autowired
    private OrderFundMapper orderFundMapper;

    @Autowired
    private IFinanceClaimService financeClaimService;

    @Autowired
    private IOrderFundAllotService orderFundAllotService;
    @Autowired
    private UserMapper userMapper;

    @Override
    @SuppressWarnings("unchecked")
    public Page<FinanceClaim> selectPage(Page page,FinanceClaim financeClaim){
        Page ret = this.page(page, new QueryWrapper<FinanceClaim>(financeClaim));
        return ret;
    }


    @Override
    public FinanceClaim detail(Long id){
        return this.getById(id);
    }



    @Override
    public synchronized Boolean add(FinanceClaim financeClaim){
       return financeClaimService.doClaim(financeClaim);
    }

    @Transactional
    @Override
    public Boolean doClaim(FinanceClaim financeClaim){
        BigDecimal notClaimAmount = financeRealFundService.getNotClaimAmount(financeClaim.getRealFundId());
        if (financeClaim.getClaimAmount().compareTo(notClaimAmount)<=0){
            financeClaim.setClaimUserId(LoginContext.getCurrentUserId());
            this.save(financeClaim);
            //productionFundService.updateRealFundForProduction(financeClaim.getOrderFundId());
            return Boolean.TRUE;
        }else{
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"认领金额已超过实收款剩余金额，请检查！");
            return Boolean.FALSE;
        }
    }


    @Override
    public synchronized Boolean update(FinanceClaim financeClaim){
        return financeClaimService.doClaimUpdate(financeClaim);
    }

    @Override
    @Transactional
    public Boolean doClaimUpdate(FinanceClaim financeClaim){
        FinanceClaim xFinanceClaim = this.getById(financeClaim.getId());
        BigDecimal claimAmount = xFinanceClaim.getClaimAmount();
        BigDecimal notClaimAmount = financeRealFundService.getNotClaimAmount(financeClaim.getRealFundId());
        notClaimAmount = claimAmount.add(notClaimAmount);
        if (financeClaim.getClaimAmount().compareTo(notClaimAmount)<=0){
            financeClaim.setClaimUserId(LoginContext.getCurrentUserId());
            this.updateById(financeClaim);
            productionFundService.updateRealFundForProduction(xFinanceClaim.getOrderFundId());
            return Boolean.TRUE;
        }else{
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"认领金额已超过实收款剩余金额，请检查！");
            return Boolean.FALSE;
        }
    }

    @Transactional
    @Override
    public Boolean delete(Long id){
        //逻辑删除
        FinanceClaim xFinanceClaim = this.getById(id);
        this.removeById(id);
        //重新统计订单收款
        orderFundService.updateRealAmount(xFinanceClaim.getOrderFundId());

        //回滚直接认领到产品的金额
        if (xFinanceClaim.getProductionFundId()!=null){
            productionFundService.updateProductionRealAmount(xFinanceClaim.getProductionFundId(),xFinanceClaim.getClaimAmount());
        }
        //如果是先认领到订单，后分配到产品
        List<OrderFundAllot> allots = orderFundAllotService.list(new LambdaQueryWrapper<OrderFundAllot>().eq(OrderFundAllot::getOrderFundId, xFinanceClaim.getOrderFundId()));
        if (CollectionUtil.isNotEmpty(allots)){
            allots.stream().forEach(x->{
                productionFundService.updateProductionRealAmount(x.getProductionFundId(),x.getAllotAmount());
            });
            orderFundAllotService.removeByIds(allots.stream().map(BaseEntity::getId).collect(Collectors.toList()));
        }

        return Boolean.TRUE;
    }

    @Override
    public List<FinanceClaimVO> getList(Long realFundId) {
        List<FinanceClaimVO> list = financeClaimMapper.getList(realFundId);
        return list;
    }

    @Override
    public BigDecimal getTotalClaimAmountByProductionFundId(Long id) {
        BigDecimal totalClaimAmount =  financeClaimMapper.getTotalClaimAmountByProductionFundId(id);
        return totalClaimAmount;
    }

    @Override
    public BigDecimal getTotalClaimAmountByOrderFundId(Long id) {
        BigDecimal totalClaimAmount =  financeClaimMapper.getTotalClaimAmountByOrderFundId(id);
        return totalClaimAmount;
    }

    @Override
    public synchronized Boolean personalClaimByTemplate(MultipartFile file,Long financeFundId) {
        //检查这笔实收款剩余金额
        BigDecimal residueAmount = checkFinanceFound(financeFundId);
        List<PersonalClaimDTO> personalClaimDTOS = ExcelUtil.importExcel(PersonalClaimDTO.class,
                personalClaimDTO->{
                    String orderId = personalClaimDTO.getOrderId();
                    BigDecimal amount = personalClaimDTO.getAmount();
                    if (StrUtil.isNotBlank(orderId)){
                        checkOrderId(orderId);
                    }
                    if (amount!=null){
                        checkAmount(orderId,amount);
                    }
                    return true;
                },file, 1);
        return financeClaimService.doClaimByTemplateDTO(personalClaimDTOS,financeFundId,residueAmount);
    }

    @Override
    public synchronized BigDecimal residueAmount(Long financeFundId) {
        return financeClaimMapper.residueAmount(financeFundId);
    }

    @Override
    public Page<RealFundMajorStatVO> majorStat(Page page, RealFundMajorStatVO majorStat,String keyword) {
        return financeClaimMapper.majorStat(page,majorStat,keyword);
    }

    @Override
    public List<RealFundMajorStatVO> majorStatExport(RealFundMajorStatVO majorStat,String keyword) {
        return financeClaimMapper.majorStatExport(majorStat,keyword);
    }

    /**
     * 分页查询资产实收款统计
     * @param page 分页信息
     * @param dto 查询dto
     * @return Page<RealFundAssetsStatVO>
     */
    @Override
    public Page<RealFundAssetsStatVO> assetsSate(Page page, RealFundAssetsStatDTO dto,String keyword) {
        Page<RealFundAssetsStatVO> realFundAssetsStatVOPage = financeClaimMapper.assetsSate(page, dto, keyword);
        List<RealFundAssetsStatVO> records = realFundAssetsStatVOPage.getRecords();
        for (RealFundAssetsStatVO record : records){
            List<Long> users = JSON.parseArray(record.getMembers(), Long.class);
            if (CollectionUtil.isNotEmpty(users)){
                List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().in(User::getId, users).select(User::getName).ne(BaseEntity::getId, 1L));
                List<String> names = userList.stream().map(User::getName).collect(Collectors.toList());
                record.setJoiner(String.join(",",names));
            }
        }
        return realFundAssetsStatVOPage;
    }

    /**
     * 资产实收款统计导出
     * @param dto 查询dto
     * @return Page<RealFundAssetsStatVO>
     */
    @Override
    public List<RealFundAssetsStatVO> assetsSateExport(RealFundAssetsStatDTO dto,String keyword) {
        List<RealFundAssetsStatVO> realFundAssetsStatVOS = financeClaimMapper.assetsSateExport(dto, keyword);
        for (RealFundAssetsStatVO record : realFundAssetsStatVOS){
            List<Long> users = JSON.parseArray(record.getMembers(), Long.class);
            if (CollectionUtil.isNotEmpty(users)){
                List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().in(User::getId, users).select(User::getName).ne(BaseEntity::getId, 1L));
                List<String> names = userList.stream().map(User::getName).collect(Collectors.toList());
                record.setJoiner(String.join(",",names));
            }
        }
        return realFundAssetsStatVOS;
    }

    @Override
    public Page<RealFundPersonalVO> personalStat(Page page, RealFundPersonalVO personalVO) {
        return financeClaimMapper.personalStat(page,personalVO);
    }

    @Override
    public List<RealFundPersonalVO> personalStatExport(RealFundPersonalVO personalVO) {
        return financeClaimMapper.personalStatExport(personalVO);
    }

    @Override
    public List<OrderFund> queryOrderTotalAmount() {
        return financeClaimMapper.queryOrderTotalAmount();
    }

    @Override
    public Page<FinanceClaimsVO> financeClaimsList(Page page, FinanceClaimsVO financeClaimVO,String keyword) {
        return financeClaimMapper.financeClaimsList(page,financeClaimVO,keyword);
    }

    @Override
    public List<FinanceClaimsVO> exportList(FinanceClaimsVO financeClaimVO, String keyword) {
        return financeClaimMapper.exportList(financeClaimVO,keyword);
    }

    @Override
    public List<HisClaimProd> updateClaimProductionId(String businessType) {
        return financeClaimMapper.updateClaimProductionId(businessType);
    }


    @Override
    public synchronized BigDecimal checkFinanceFound(Long financeFundId) {
        BigDecimal residueAmount = residueAmount(financeFundId);
        if (residueAmount==null){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"未查询到此笔实收款");
        }
        if (residueAmount.compareTo(BigDecimal.ZERO)<=0){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"该笔实收款余额为0，无法再次认领");
        }
        return residueAmount;

    }

    @Transactional
    @Override
    public  Boolean doClaimByTemplateDTO(List<PersonalClaimDTO> personalClaimDTOS,Long financeFundId,BigDecimal residueAmount) {
        if (CollectionUtil.isEmpty(personalClaimDTOS)){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"未解析到模板数据,请检查模板。");
        }
        BigDecimal thisTimeClaimTotalAmount = personalClaimDTOS.stream().collect(Collectors.mapping(PersonalClaimDTO::getAmount,
                Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)));

        if (thisTimeClaimTotalAmount.compareTo(residueAmount)>0){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"模板认领总金额大于实收款剩余金额，请检查。");
        }
        personalClaimDTOS.stream().forEach(x->{
            //检查该订单是否创建应收款记录
            OrderFund orderFund = orderFundService.getOne(new LambdaQueryWrapper<OrderFund>()
                    .eq(OrderFund::getOrderId, x.getOrderId())
                    .eq(BaseEntity::getDeleted, Boolean.FALSE)
                    .eq(OrderFund::getBusinessType,PERSONAL_BUSINESS.name()));
            if (orderFund==null){
                Personal personal = personalService.getOne(new LambdaQueryWrapper<Personal>().select(BaseEntity::getId, Personal::getLocation).eq(Personal::getOrderId, x.getOrderId()).eq(BaseEntity::getDeleted, Boolean.FALSE));
                if (personal!=null){
                    orderFund = new OrderFund();
                    orderFund.setOrderId(x.getOrderId());
                    orderFund.setBusinessType(PERSONAL_BUSINESS.name());
                    orderFund.setBusinessId(personal.getId());
                    orderFund.setOrderName(personal.getLocation());
                    orderFundService.save(orderFund);
                }
            }
            FinanceClaim financeClaim = new FinanceClaim();
            financeClaim.setRealFundId(financeFundId);
            financeClaim.setOrderFundId(orderFund.getId());
            financeClaim.setClaimUserId(LoginContext.getCurrentUserId());
            financeClaim.setClaimDatetime(new Date());
            financeClaim.setClaimAmount(x.getAmount());
            this.save(financeClaim);
        });

        //更新订单认领总金额
        Map<String, BigDecimal> orderIdGroup = personalClaimDTOS.stream()
                .collect(Collectors.groupingBy(PersonalClaimDTO::getOrderId,
                        Collectors.mapping(PersonalClaimDTO::getAmount, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));

        for(Map.Entry<String,BigDecimal> orderAmount :orderIdGroup.entrySet()){
            orderFundMapper.updateRealAmountByOrderId(orderAmount.getKey(),orderAmount.getValue());
        }

        return Boolean.TRUE;
    }

    private void checkAmount(String orderId,BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO)<0){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"个贷项目编号["+orderId+"]认领金额不能为负数");
        }
    }

    private void checkOrderId(String orderId) {
        int count = personalService.count(new LambdaQueryWrapper<Personal>().eq(Personal::getOrderId, orderId).eq(BaseEntity::getDeleted, Boolean.FALSE));
        if (count!=1){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"个贷项目编号["+orderId+"]不存在或有多个");
        }
    }



}
