/*
 * Copyright [2022] [https://www.xiaonuo.vip]
 *
 * Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Snowy源码头部的版权声明。
 * 3.本项目代码可免费商业使用，商业使用请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://www.xiaonuo.vip
 * 5.不可二次分发开源参与同类竞品，如有想法可联系团队xiaonuobase@qq.com商议合作。
 * 6.若您的项目无法满足以上几点，需要更多功能代码，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package vip.xiaonuo.biz.modular.depotGrainInfoPrice.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.xiaonuo.biz.core.exception.BizExceptionEnum;
import vip.xiaonuo.biz.modular.depotGrain.service.DepotGrainService;
import vip.xiaonuo.biz.modular.depotGrainInfo.service.DepotGrainInfoService;
import vip.xiaonuo.biz.modular.depotGrainInfoPrice.entity.BaseDepotGrainInfoPrice;
import vip.xiaonuo.biz.modular.depotGrainInfoPrice.entity.DepotGrainInfoPriceChildren;
import vip.xiaonuo.biz.modular.depotGrainInfoPrice.param.*;
import vip.xiaonuo.biz.modular.depotGrainInfoWarn.service.DepotGrainInfoWarnService;
import vip.xiaonuo.biz.modular.depotZonePrice.entity.DepotZonePrice;
import vip.xiaonuo.biz.modular.depotZonePrice.service.DepotZonePriceService;
import vip.xiaonuo.common.enums.CommonSortOrderEnum;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.page.CommonPageRequest;
import vip.xiaonuo.biz.modular.depotGrainInfoPrice.entity.DepotGrainInfoPrice;
import vip.xiaonuo.biz.modular.depotGrainInfoPrice.mapper.DepotGrainInfoPriceMapper;
import vip.xiaonuo.common.util.BeanCopyUtil;
import vip.xiaonuo.common.util.BigDecimalUtil;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 企业客户种类价格表Service接口实现类
 *
 * @author xiang
 * @date 2023/10/07 10:05
 **/
@Service
public class DepotGrainInfoPriceService extends ServiceImpl<DepotGrainInfoPriceMapper, DepotGrainInfoPrice> {

    @Resource
    DepotGrainService depotGrainService;
    @Resource
    DepotGrainInfoService depotGrainInfoService;
    @Resource
    DepotZonePriceService zonePriceService;
    @Resource
    DepotGrainInfoWarnService grainInfoWarnService;

    public Page<DepotGrainInfoPrice> page(DepotGrainInfoPricePageParam param) {
        QueryWrapper<DepotGrainInfoPrice> wrapper = new QueryWrapper<>();
        List<String> depotIdLst = depotGrainService.lstOrgGrep();
        wrapper.lambda().in(CollUtil.isNotEmpty(depotIdLst), DepotGrainInfoPrice::getDepotId, depotIdLst);
        if (ObjectUtil.isNotEmpty(param.getDepotId())) {
            wrapper.lambda().eq(DepotGrainInfoPrice::getDepotId, param.getDepotId());
        }
        if (ObjectUtil.isNotEmpty(param.getGrainGroup())) {
            wrapper.lambda().eq(DepotGrainInfoPrice::getGrainGroup, param.getGrainGroup());
        }
        if (ObjectUtil.isAllNotEmpty(param.getSortField(), param.getSortOrder())) {
            CommonSortOrderEnum.validate(param.getSortOrder());
            wrapper.orderBy(true, param.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
                    StrUtil.toUnderlineCase(param.getSortField()));
        } else {
            wrapper.lambda().orderByDesc(DepotGrainInfoPrice::getId);
        }
        Page page = this.page(CommonPageRequest.defaultPage(), wrapper);
        //匹配粮库名称
        depotGrainService.putDepotName(page.getRecords());
        //匹配主流价格
        putUnionPrice(page.getRecords());
        return page;
    }

    @Resource
    DepotZonePriceService depotZonePriceService;

    private void putUnionPrice(List<DepotGrainInfoPrice> prices) {
        List<String> unionPriceIdLst = prices.stream().map(DepotGrainInfoPrice::getZonePriceId).collect(Collectors.toList());
        unionPriceIdLst.removeIf(Strings::isBlank);
        if (unionPriceIdLst.isEmpty()) return;
        List<DepotZonePrice> depotZonePrices = depotZonePriceService.lst(unionPriceIdLst);
        for (DepotGrainInfoPrice one : prices) {
            for (DepotZonePrice price : depotZonePrices) {
                if (!Objects.equals(one.getZonePriceId(), price.getId())) continue;
                one.setZonePrice(price.getZonePrice());
                break;
            }
        }
    }


    public List<DepotGrainInfoPrice> lst(DepotGrainInfoPriceListParam param) {
        QueryWrapper<DepotGrainInfoPrice> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(CollUtil.isNotEmpty(param.getDepotIdLst()), DepotGrainInfoPrice::getDepotId, param.getDepotIdLst());
        wrapper.lambda().eq(ObjectUtil.isNotEmpty(param.getDepotId()), DepotGrainInfoPrice::getDepotId, param.getDepotId());
        wrapper.lambda().eq(ObjectUtil.isNotEmpty(param.getGrainGroup()), DepotGrainInfoPrice::getGrainGroup, param.getGrainGroup());
        wrapper.lambda().orderByDesc(DepotGrainInfoPrice::getGrainGroup);
        return list(wrapper);
    }

    public List<DepotGrainInfoPrice> lst(DepotGrainInfoPrice param) {
        return list(new QueryWrapper<>(param));
    }

    public List<DepotGrainInfoPriceChildren> lstPriceChildren(String depotId) {
        return BeanCopyUtil.copyListProperties(list(new QueryWrapper<>(new DepotGrainInfoPrice(depotId))), DepotGrainInfoPriceChildren::new);
    }

    public DepotGrainInfoPrice oneByGrainGroup(String depotId, String grainGroup) {
        return getOne(new LambdaQueryWrapper<>(new DepotGrainInfoPrice(depotId, grainGroup)));
    }

    public BigDecimal getDampRatio(String depotId, String grainGroup) {
        DepotGrainInfoPrice one = getOne(new LambdaQueryWrapper<>(new DepotGrainInfoPrice(depotId, grainGroup)));
        return one == null ? BigDecimal.ONE : one.getDampRatio();
    }

    public void add(DepotGrainInfoPriceAddParam param) {
        //检查客户档案
        if (depotGrainInfoService.listByDepotId(param.getDepotId()).isEmpty())
            throw new CommonException("客户档案不存在!");
        repeatName(param.getDepotId(), null, param.getGrainGroup(), true);
        DepotGrainInfoPrice depotGrainInfoPrice = BeanUtil.toBean(param, DepotGrainInfoPrice.class);
        this.save(depotGrainInfoPrice);
    }

    /**
     * 检查名称重复
     *
     * @param throwFlag true:抛异常
     * @return true:重复
     * @author xiang
     * @date 2023-01-01
     */
    private boolean repeatName(String depotId, String id, String grainGroup, boolean throwFlag) {
        List<DepotGrainInfoPrice> list = lst(new DepotGrainInfoPrice(depotId, grainGroup));
        if (list.isEmpty()) return false;
        if (Objects.equals(list.get(0).getId(), id) && list.size() == 1) return false;
        if (!throwFlag) return true;
        throw new CommonException(BizExceptionEnum.ERROR00008.getMessage());
    }

    @Transactional(rollbackFor = Exception.class)
    public void editPriceChildren(List<DepotGrainInfoPriceChildren> priceChildren, String depotId) {
        if (CollUtil.isEmpty(priceChildren) || Strings.isBlank(depotId)) return;
        if (priceChildren.size() > priceChildren.stream().map(DepotGrainInfoPriceChildren::getGrainGroup).distinct().count())
            throw new CommonException(BizExceptionEnum.ERROR00008.getMessage());
        List<DepotGrainInfoPrice> prices = BeanCopyUtil.copyListProperties(priceChildren, DepotGrainInfoPrice::new);
        prices.removeIf(one -> Strings.isBlank(one.getGrainGroup()));
        prices.forEach(one -> one.setDepotId(depotId));
        this.saveOrUpdateBatch(prices);
    }

    @Transactional(rollbackFor = Exception.class)
    public void edit(DepotGrainInfoPriceEditParam param) {
        DepotGrainInfoPrice info = this.queryEntity(param.getId());
        String zonePriceId = info.getZonePriceId();
        BeanUtil.copyProperties(param, info);
        if (Strings.isNotEmpty(param.getZonePriceId()) && !param.getZonePriceId().equals(zonePriceId)) {
            BigDecimal zonePrice = zonePriceService.getPriceById(param.getZonePriceId());
            //价格预警
            if (BigDecimalUtil.isNullOrEqZero(info.getZonePrice()) || zonePrice.compareTo(info.getZonePrice()) != 0) {
                info.setZonePrice(zonePrice);
                grainInfoWarnService.loanPriceWarn(info);
            }
        }
        this.updateById(info);
    }

    @Transactional(rollbackFor = Exception.class)
    public void editBatchByZonePriceId(String zonePriceId, BigDecimal zonePrice) {
        List<DepotGrainInfoPrice> list = lst(new DepotGrainInfoPrice(null, zonePriceId, null));
        for (DepotGrainInfoPrice one : list) {
            one.setZonePrice(zonePrice);
            //价格预警
            grainInfoWarnService.loanPriceWarn(one);
        }
        updateBatchById(list);
    }

    @Transactional(rollbackFor = Exception.class)
    public void delete(List<DepotGrainInfoPriceIdParam> depotGrainInfoPriceIdParamList) {
        // 执行删除
        this.removeBatchByIds(CollStreamUtil.toList(depotGrainInfoPriceIdParamList, DepotGrainInfoPriceIdParam::getId));
    }

    public DepotGrainInfoPrice getPrice(String depotId, String grainGroup) {
        List<DepotGrainInfoPrice> lst = lst(new DepotGrainInfoPrice(depotId, grainGroup));
        return lst.size() == 1 ? lst.get(0) : new DepotGrainInfoPrice();
    }

    public DepotGrainInfoPrice detail(DepotGrainInfoPriceIdParam depotGrainInfoPriceIdParam) {
        return this.queryEntity(depotGrainInfoPriceIdParam.getId());
    }

    public DepotGrainInfoPrice queryEntity(String id) {
        DepotGrainInfoPrice depotGrainInfoPrice = this.getById(id);
        if (ObjectUtil.isEmpty(depotGrainInfoPrice)) {
            throw new CommonException("企业客户种类价格表不存在，id值为：{}", id);
        }
        return depotGrainInfoPrice;
    }

    /**
     * 审批入库申请
     *
     * @param depotId       客户id
     * @param mortgageCount 质押吨数
     * @author xiang
     * @date 2023/09/14 15:00
     **/
    @Transactional(rollbackFor = Exception.class)
    public void loanSync(String depotId, String grainGroup, BigDecimal mortgageCount) {
        DepotGrainInfoPrice info = oneByGrainGroup(depotId, grainGroup);
        //累计贷款重量(万吨)
        info.setLoanWeightHistory(BigDecimalUtil.addVal(info.getLoanWeightHistory(), mortgageCount.movePointLeft(4)));
        this.updateById(info);
    }

    /**
     * 审批出库申请
     *
     * @param depotId    客户id
     * @param repayCount 出库吨数
     * @param grainGroup 种类
     * @author xiang
     * @date 2023/09/14 15:00
     **/
    @Transactional(rollbackFor = Exception.class)
    public void subStorage(String depotId, BigDecimal repayCount, String grainGroup) {
        DepotGrainInfoPrice info = oneByGrainGroup(depotId, grainGroup);
        info.setRepayWeightHistory(BigDecimalUtil.addVal(info.getRepayWeightHistory(), repayCount.negate().movePointLeft(4)));
        this.updateById(info);
    }

    /**
     * 匹配种类价格
     *
     * @author xiang
     * @date 2023-01-01
     */
    public <T> void putPrice(List<? extends BaseDepotGrainInfoPrice> sources) {
        if (CollUtil.isEmpty(sources)) return;
        List<DepotGrainInfoPrice> lst = lst(new DepotGrainInfoPriceListParam(sources.stream().map(BaseDepotGrainInfoPrice::getDepotId).distinct().collect(Collectors.toList())));
        for (BaseDepotGrainInfoPrice s : sources) {
            s.setPriceChildren(new ArrayList<>());
            for (DepotGrainInfoPrice one : lst) {
                if (!Objects.equals(s.getDepotId(), one.getDepotId()))
                    continue;
                s.getPriceChildren().add(BeanUtil.toBean(one, DepotGrainInfoPriceChildren.class));
            }
        }
    }

    /**
     * 车辆出场，自动入库
     *
     * @param realWeight         入库重量(吨)
     * @param realAmount         交易金额 用于计算平均质押价格
     * @param dryInWeightHistory 干粮重量(吨) 用于计算平均质押价格
     * @author xiang
     * @date 2023-01-01
     */
    public void inInventory(String id, BigDecimal realWeight, BigDecimal realAmount, BigDecimal dryInWeightHistory) {
        if (realWeight.doubleValue() == 0 || realAmount.doubleValue() == 0 || dryInWeightHistory.doubleValue() == 0) return;

        DepotGrainInfoPrice upOne = getById(id);
        //累计入库量(万吨)
        upOne.setRealInWeightHistory(BigDecimalUtil.addVal(upOne.getRealInWeightHistory(), realWeight.movePointLeft(4)));
        //库存量(万吨)
        upOne.setLoanTonW(BigDecimalUtil.subVal(upOne.getRealInWeightHistory(), upOne.getRealOutWeightHistory()));
        //累计入库金额
        upOne.setInAmountHistory(BigDecimalUtil.addVal(upOne.getInAmountHistory(), realAmount));
        //累计入库干粮重量(吨)
        upOne.setDryInWeightHistory(BigDecimalUtil.addVal(upOne.getDryInWeightHistory(), dryInWeightHistory));
        //平均质押价格
        upOne.setLoanPriceAvg(upOne.getInAmountHistory().divide(upOne.getDryInWeightHistory(), 2, BigDecimal.ROUND_HALF_UP));
        updateById(upOne);

        //档案添加新增数量
        depotGrainInfoService.inInventory(upOne.getDepotId(), realWeight, realAmount);

        //价格预警
        grainInfoWarnService.loanPriceWarn(upOne);
    }

    /**
     * 车辆出场，自动出库
     *
     * @param realWeight 重量(吨)
     * @param realAmount 交易金额
     * @author xiang
     * @date 2023-01-01
     */
    public void outInventory(String depotId, String grainGroup, BigDecimal realWeight, BigDecimal realAmount) {
        DepotGrainInfoPrice upOne = oneByGrainGroup(depotId, grainGroup);
        //累计出库量(万吨)
        upOne.setRealOutWeightHistory(BigDecimalUtil.addVal(upOne.getRealOutWeightHistory(), realWeight.movePointLeft(4)));
        //库存量(万吨)
        upOne.setLoanTonW(BigDecimalUtil.subVal(upOne.getRealInWeightHistory(), upOne.getRealOutWeightHistory()));
        //累计出库金额
        upOne.setOutAmountHistory(BigDecimalUtil.addVal(upOne.getOutAmountHistory(), realAmount));
        updateById(upOne);

        //档案添加新增数量
        depotGrainInfoService.outInventory(upOne.getDepotId(), realWeight, realAmount);
    }

    /**
     * 检查粮食种类
     *
     * @param depotId    客户
     * @param grainGroup 粮食种类
     * @author xiang
     * @date 2023/09/14 15:00
     **/
    public void checkGrainGroup(String depotId, String grainGroup) {
        if (ArrayUtil.hasEmpty(depotId, grainGroup))
            throw new CommonException(String.format("检查粮食种类参数不完整;depotId:%s;grainGroup:%s", depotId, grainGroup));
        if (oneByGrainGroup(depotId, grainGroup) == null)
            throw new CommonException(String.format("检查粮食种类不存在;请联系管理员;粮库:%s;种类:%s", depotId, grainGroup));
    }

}
