package com.prolog.rdc.tms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.prolog.common.util.ToolKits;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.product.core.framework.entity.UserEntity;
import com.prolog.product.core.framework.util.PlgFxUsrUtils;
import com.prolog.rdc.tms.constant.BmsConstant;
import com.prolog.rdc.tms.constant.enums.LogTypeEnum;
import com.prolog.rdc.tms.dao.mapper.BmsBillIncomeFeeDetailMapper;
import com.prolog.rdc.tms.dao.mapper.BmsBillIncomeFeeHeaderMapper;
import com.prolog.rdc.tms.dao.mapper.BmsIncomeSettlementDetailMapper;
import com.prolog.rdc.tms.dao.mapper.BmsIncomeSettlementMapper;
import com.prolog.rdc.tms.dao.mapper.LoadingBillHeadMapper;
import com.prolog.rdc.tms.dao.mapper.LogisticsContractMapper;
import com.prolog.rdc.tms.dao.mapper.ShipBillHeadMapper;
import com.prolog.rdc.tms.model.dto.income.BmsLoadingBillIncomeFeeDTO;
import com.prolog.rdc.tms.model.entity.BmsBillIncomeFeeDetailModel;
import com.prolog.rdc.tms.model.entity.BmsBillIncomeFeeHeaderModel;
import com.prolog.rdc.tms.model.entity.BmsIncomeSettlementDetailModel;
import com.prolog.rdc.tms.model.entity.BmsIncomeSettlementModel;
import com.prolog.rdc.tms.model.entity.BmsLoadingBillHeaderModel;
import com.prolog.rdc.tms.model.entity.BmsLogisticsContractModel;
import com.prolog.rdc.tms.model.entity.BmsShipBillHeaderModel;
import com.prolog.rdc.tms.model.entity.SysRefdatadModel;
import com.prolog.rdc.tms.model.vo.settlement.income.BmsIncomeFeeSettlementVO;
import com.prolog.rdc.tms.model.vo.settlement.income.IncomeFeeSettlementPageVO;
import com.prolog.rdc.tms.proxy.MainDataProxy;
import com.prolog.rdc.tms.service.BmsBillLogService;
import com.prolog.rdc.tms.service.BmsIncomeFeeSettlementService;
import com.prolog.rdc.tms.util.ConvertorUtil;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author hongbo.pan
 * @date 2023/5/4
 */
@Service
public class BmsIncomeFeeSettlementServiceImpl implements BmsIncomeFeeSettlementService {

    @Autowired
    private LoadingBillHeadMapper loadingBillHeadMapper;

    @Autowired
    private ConvertorUtil convertorUtil;

    @Autowired
    private BmsBillIncomeFeeHeaderMapper incomeFeeHeaderMapper;

    @Autowired
    private BmsBillIncomeFeeDetailMapper incomeFeeDetailMapper;

    @Autowired
    private BmsIncomeSettlementMapper incomeSettlementMapper;

    @Autowired
    private BmsIncomeSettlementDetailMapper incomeSettlementDetailMapper;

    @Autowired
    private MainDataProxy mainDataProxy;

    @Autowired
    private BmsBillLogService logService;

    @Autowired
    private ShipBillHeadMapper shipBillHeadMapper;

    @Autowired
    private LogisticsContractMapper logisticsContractMapper;

    @Override
    public Page<BmsLoadingBillIncomeFeeDTO> findLoadingBillIncomeFeeForSettlement(IncomeFeeSettlementPageVO vo) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        ToolKits.isParamNull(user, "当前用户没有操作权限");
        ToolKits.isParamNull(user.getDepts(), "当前用户没有部门权限");
        // 权限控制 仅支持自有账号
        if (!BmsConstant.USER_TYPE_ZY.equals(user.getUserType())) {
            throw new BizException("当前人员没有查询权限");
        }
        // 权限赋值
        vo.setEnterpriseId(user.getEnterpriseId());
        vo.setSettlementDepartmentTds(user.getDepts());
        // 执行查询
        PageUtils.startPage(vo.getPageNum(), vo.getPageSize());
        List<BmsLoadingBillIncomeFeeDTO> dtoList = incomeFeeHeaderMapper.findLoadingBillIncomeFeeForSettlement(vo);
        if (CollectionUtil.isNotEmpty(dtoList)) {
            List<SysRefdatadModel> dicData = getLoadingDic();
            dtoList.forEach(dto -> {
                dto.setStatusName(ConvertorUtil.getName(dicData, BmsConstant.LOADING_BILL_STATUS, String.valueOf(dto.getStatus())));
                dto.setSourceName(ConvertorUtil.getName(dicData, BmsConstant.SOURCE, String.valueOf(dto.getSource())));
                dto.setSynchronousStatusName(ConvertorUtil.getName(dicData, BmsConstant.SYNCHRONOUS_STATUS, String.valueOf(dto.getSynchronousStatus())));
                dto.setFeeCltTypeName(ConvertorUtil.getName(dicData, BmsConstant.FEE_CLT_TYPE, String.valueOf(dto.getFeeCltType())));
                dto.setCostStatusName(ConvertorUtil.getName(dicData, BmsConstant.COST_STATUS, String.valueOf(dto.getCostStatus())));
                dto.setSettlementStatusName(ConvertorUtil.getName(dicData, BmsConstant.SETTLEMENT_STATUS, String.valueOf(dto.getSettlementStatus())));
                dto.setCarrierTypeName(ConvertorUtil.getName(dicData, BmsConstant.LINE_CARRIER_TYPE, String.valueOf(dto.getCarrierType())));
                dto.setCheckStatusName(ConvertorUtil.getName(dicData, BmsConstant.CHECK_STATUS, String.valueOf(dto.getCheckStatus())));
            });
        }
        return PageUtils.getPage(dtoList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void feeSettlement(BmsIncomeFeeSettlementVO vo) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        List<BmsBillIncomeFeeHeaderModel> incomeFeeHeaderModelList = incomeFeeHeaderMapper.findByIds(BmsBillIncomeFeeHeaderModel.class, vo.getFeeIdList().toArray(), null);
        if (CollectionUtil.isNotEmpty(incomeFeeHeaderModelList)) {
            Map<Long, List<BmsBillIncomeFeeDetailModel>> incomeFeeDetailModelMap = getIncomeFeeDetailModelMap(incomeFeeHeaderModelList);
            List<BmsLoadingBillHeaderModel> loadingBillHeaderModelList = loadingBillHeadMapper.findByIds(BmsLoadingBillHeaderModel.class, incomeFeeHeaderModelList.stream().map(BmsBillIncomeFeeHeaderModel::getBillId).toArray(), null);
            preCheck(loadingBillHeaderModelList);
            String settlementNo = mainDataProxy.getCodeNumberByCode(BmsConstant.BMS_SHIP_BILL_SETTLEMENT_ID);
            BmsIncomeSettlementModel incomeSettlementModel = BeanUtil.copyProperties(vo, BmsIncomeSettlementModel.class);
            incomeSettlementModel.setSettlementNo(settlementNo);
            incomeSettlementModel.setStatus(0);
            incomeSettlementModel.setTax(incomeFeeDetailModelMap.values().stream().flatMap(List::stream).map(BmsBillIncomeFeeDetailModel::getTax).reduce(BigDecimal.ZERO, NumberUtil::add));
            incomeSettlementModel.setNoTaxAmt(incomeFeeDetailModelMap.values().stream().flatMap(List::stream).map(BmsBillIncomeFeeDetailModel::getNoTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
            incomeSettlementModel.setTaxAmt(incomeFeeDetailModelMap.values().stream().flatMap(List::stream).map(BmsBillIncomeFeeDetailModel::getTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
            incomeSettlementModel.setCreateperson(user.getNickname());
            incomeSettlementModel.setCreatetime(DateUtil.date());
            incomeSettlementModel.setUpdateperson(user.getNickname());
            incomeSettlementModel.setUpdatetime(DateUtil.date());
            incomeSettlementModel.setEnterpriseId(user.getEnterpriseId());
            incomeSettlementModel.setEnterpriseName(user.getEnterpriseName());
            incomeSettlementMapper.insertSelective(incomeSettlementModel);
            logService.addLog(LogTypeEnum.SETTLEMENT, incomeSettlementModel.getId().toString(), "新增结算单");
            batchAddIncomeFeeDetail(incomeFeeHeaderModelList, incomeSettlementModel, incomeFeeDetailModelMap);
        }
    }

    @Override
    public BmsIncomeSettlementModel initIncomeSettlement(List<Long> feeIdList) {
        List<BmsBillIncomeFeeHeaderModel> incomeFeeHeaderModelList = incomeFeeHeaderMapper.findByIds(BmsBillIncomeFeeHeaderModel.class, feeIdList.toArray(), null);
        if (CollectionUtil.isEmpty(incomeFeeHeaderModelList)) {
            throw new BizException("费用不存在");
        }
        List<BmsLoadingBillHeaderModel> loadingBillHeaderModelList = loadingBillHeadMapper.findByIds(BmsLoadingBillHeaderModel.class, incomeFeeHeaderModelList.stream().map(BmsBillIncomeFeeHeaderModel::getBillId).toArray(), null);
        preCheck(loadingBillHeaderModelList);
        Map<Long, List<BmsBillIncomeFeeDetailModel>> incomeFeeDetailModelMap = getIncomeFeeDetailModelMap(incomeFeeHeaderModelList);
        BmsLoadingBillHeaderModel loadingBillHeaderModel = loadingBillHeaderModelList.get(0);
        BmsIncomeSettlementModel incomeSettlementModel = new BmsIncomeSettlementModel();
        incomeSettlementModel.setSettlementCompany(loadingBillHeaderModel.getSettlementDepartment());
        incomeSettlementModel.setSettlementCompanyId(NumberUtil.parseLong(loadingBillHeaderModel.getSettlementDepartmentId()));
        incomeSettlementModel.setSettlementCompanyCode(loadingBillHeaderModel.getSettlementDepartmentCode());
        incomeSettlementModel.setSettlementDepartment(loadingBillHeaderModel.getSettlementDepartment());
        incomeSettlementModel.setSettlementDepartmentId(loadingBillHeaderModel.getSettlementDepartmentId());
        incomeSettlementModel.setSettlementDepartmentCode(loadingBillHeaderModel.getSettlementDepartmentCode());
        incomeSettlementModel.setCargoOwnerId(loadingBillHeaderModel.getCargoOwnerId());
        incomeSettlementModel.setCargoOwnerCode(loadingBillHeaderModel.getCargoOwnerCode());
        incomeSettlementModel.setCargoOwnerName(loadingBillHeaderModel.getCargoOwnerName());
        incomeSettlementModel.setTax(incomeFeeDetailModelMap.values().stream().flatMap(List::stream).map(BmsBillIncomeFeeDetailModel::getTax).reduce(BigDecimal.ZERO, NumberUtil::add));
        incomeSettlementModel.setNoTaxAmt(incomeFeeDetailModelMap.values().stream().flatMap(List::stream).map(BmsBillIncomeFeeDetailModel::getNoTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
        incomeSettlementModel.setTaxAmt(incomeFeeDetailModelMap.values().stream().flatMap(List::stream).map(BmsBillIncomeFeeDetailModel::getTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
        List<String> taxPoint = incomeFeeDetailModelMap.values().stream().flatMap(List::stream).map(BmsBillIncomeFeeDetailModel::getTaxPoint).distinct().collect(Collectors.toList());
        if (taxPoint.size() == 1) {
            incomeSettlementModel.setTaxPoint(taxPoint.get(0));
        }
        Criteria criteria = Criteria.forClass(BmsLogisticsContractModel.class);
        Restriction r1 = Restrictions.in(BmsLogisticsContractModel::getContractName, incomeFeeHeaderModelList.stream().map(BmsBillIncomeFeeHeaderModel::getCostContract).distinct().collect(Collectors.toList()));
        Restriction r2 = Restrictions.eq(BmsLogisticsContractModel::getEnterpriseId, loadingBillHeaderModel.getEnterpriseId());
        criteria.setRestriction(Restrictions.and(r1, r2));
        List<BmsLogisticsContractModel> contractModelList = logisticsContractMapper.findByCriteria(criteria);
        List<Integer> settlementType = contractModelList.stream().map(BmsLogisticsContractModel::getSettlementType).distinct().collect(Collectors.toList());
        if (settlementType.size() == 1) {
            incomeSettlementModel.setSettlementCycle(settlementType.get(0).toString());
        }
        return incomeSettlementModel;
    }

    /**
     * 派车单翻译数据字典
     */
    private List<SysRefdatadModel> getLoadingDic() {
        return convertorUtil.getDictionary(new String[] {
                // 核算状态
                BmsConstant.SETTLEMENT_STATUS,
                // 计费状态
                BmsConstant.COST_STATUS,
                // 同步状态
                BmsConstant.SYNCHRONOUS_STATUS,
                // 承运方类型
                BmsConstant.LINE_CARRIER_TYPE,
                // 车辆类别
                BmsConstant.VEHICLE_TYPE_CATEGORY,
                // 费用计算类型
                BmsConstant.FEE_CLT_TYPE,
                // 来源
                BmsConstant.SOURCE,
                BmsConstant.LOADING_BILL_STATUS,
                BmsConstant.CHECK_STATUS
        });
    }

    /**
     * getIncomeFeeDetailModelMap
     * @param incomeFeeHeaderModelList
     * @return
     */
    private Map<Long, List<BmsBillIncomeFeeDetailModel>> getIncomeFeeDetailModelMap(List<BmsBillIncomeFeeHeaderModel> incomeFeeHeaderModelList) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        Criteria criteria = Criteria.forClass(BmsBillIncomeFeeDetailModel.class);
        Restriction r1 = Restrictions.in(BmsBillIncomeFeeDetailModel::getBillId, incomeFeeHeaderModelList.stream().map(BmsBillIncomeFeeHeaderModel::getBillId).collect(Collectors.toList()));
        Restriction r2 = Restrictions.in(BmsBillIncomeFeeDetailModel::getBillNo, incomeFeeHeaderModelList.stream().map(BmsBillIncomeFeeHeaderModel::getBillNo).collect(Collectors.toList()));
        Restriction r3 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getEnterpriseId, user.getEnterpriseId());
        criteria.setRestriction(Restrictions.and(r1, r2, r3));
        List<BmsBillIncomeFeeDetailModel> incomeFeeDetailModelList = incomeFeeDetailMapper.findByCriteria(criteria);
        return incomeFeeDetailModelList.stream().collect(Collectors.groupingBy(BmsBillIncomeFeeDetailModel::getBillId));
    }

    /**
     * batchAddIncomeFeeDetail
     * @param incomeFeeHeaderModelList
     * @param incomeSettlementModel
     * @param incomeFeeDetailModelMap
     */
    private void batchAddIncomeFeeDetail(List<BmsBillIncomeFeeHeaderModel> incomeFeeHeaderModelList, BmsIncomeSettlementModel incomeSettlementModel, Map<Long, List<BmsBillIncomeFeeDetailModel>> incomeFeeDetailModelMap) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        incomeFeeHeaderModelList.stream().forEach(incomeFeeHeaderModel -> {
            incomeFeeHeaderModel.setCheckStatus(BmsConstant.CHECK_STATUS_3);
            incomeFeeHeaderModel.setUpdateperson(user.getNickname());
            incomeFeeHeaderModel.setUpdatetime(DateUtil.date());
            incomeFeeHeaderMapper.update(incomeFeeHeaderModel);
            logService.addLog(LogTypeEnum.FEE, incomeFeeHeaderModel.getId().toString(), "加入结算单，结算单号" + incomeSettlementModel.getSettlementNo());
            // 派车单的收入结算需要更新运单的收入结算状态
            if (incomeFeeHeaderModel.getBillType() == 20) {
                Criteria criteria = Criteria.forClass(BmsShipBillHeaderModel.class);
                Restriction r1 = Restrictions.eq(BmsShipBillHeaderModel::getLoadingId, incomeFeeHeaderModel.getBillId());
                Restriction r2 = Restrictions.eq(BmsShipBillHeaderModel::getLoadingBillNo, incomeFeeHeaderModel.getBillNo());
                Restriction r3 = Restrictions.eq(BmsShipBillHeaderModel::getEnterpriseId, user.getEnterpriseId());
                criteria.setRestriction(Restrictions.and(r1, r2, r3));
                List<BmsShipBillHeaderModel> shipBillHeaderModelList = shipBillHeadMapper.findByCriteria(criteria);
                if (CollectionUtil.isNotEmpty(shipBillHeaderModelList)) {
                    criteria = Criteria.forClass(BmsBillIncomeFeeHeaderModel.class);
                    r1 = Restrictions.in(BmsBillIncomeFeeHeaderModel::getBillId, shipBillHeaderModelList.stream().map(BmsShipBillHeaderModel::getId).collect(Collectors.toList()));
                    r2 = Restrictions.in(BmsBillIncomeFeeHeaderModel::getBillNo, shipBillHeaderModelList.stream().map(BmsShipBillHeaderModel::getShipbillNo).collect(Collectors.toList()));
                    r3 = Restrictions.eq(BmsBillIncomeFeeHeaderModel::getEnterpriseId, user.getEnterpriseId());
                    criteria.setRestriction(Restrictions.and(r1, r2, r3));
                    List<BmsBillIncomeFeeHeaderModel> headerModelList = incomeFeeHeaderMapper.findByCriteria(criteria);
                    if (CollectionUtil.isNotEmpty(headerModelList)) {
                        headerModelList.stream().forEach(headerModel -> {
                            headerModel.setCheckStatus(BmsConstant.CHECK_STATUS_3);
                            headerModel.setUpdateperson(user.getNickname());
                            headerModel.setUpdatetime(DateUtil.date());
                            incomeFeeHeaderMapper.update(headerModel);
                            logService.addLog(LogTypeEnum.FEE, headerModel.getId().toString(), "加入结算单，结算单号" + incomeSettlementModel.getSettlementNo());
                        });
                    }
                }
            }
            List<BmsBillIncomeFeeDetailModel> billIncomeFeeDetailModelList = incomeFeeDetailModelMap.get(incomeFeeHeaderModel.getBillId());
            if (CollectionUtil.isNotEmpty(billIncomeFeeDetailModelList)) {
                billIncomeFeeDetailModelList.forEach(bmsBillIncomeFeeDetailModel -> {
                    BmsIncomeSettlementDetailModel detailModel = new BmsIncomeSettlementDetailModel();
                    detailModel.setSettlementId(incomeSettlementModel.getId());
                    detailModel.setFeeId(incomeFeeHeaderModel.getId());
                    detailModel.setFeeDetailId(bmsBillIncomeFeeDetailModel.getId());
                    detailModel.setExpenseNo(bmsBillIncomeFeeDetailModel.getExpenseNo());
                    detailModel.setBillId(incomeFeeHeaderModel.getBillId());
                    detailModel.setBillNo(incomeFeeHeaderModel.getBillNo());
                    detailModel.setBillType(incomeFeeHeaderModel.getBillType());
                    detailModel.setTaxPoint(bmsBillIncomeFeeDetailModel.getTaxPoint());
                    detailModel.setTaxAmt(bmsBillIncomeFeeDetailModel.getTaxAmt());
                    detailModel.setNoTaxAmt(bmsBillIncomeFeeDetailModel.getNoTaxAmt());
                    detailModel.setTax(bmsBillIncomeFeeDetailModel.getTax());
                    detailModel.setCreateperson(user.getNickname());
                    detailModel.setCreatetime(DateUtil.date());
                    detailModel.setUpdateperson(user.getNickname());
                    detailModel.setUpdatetime(DateUtil.date());
                    detailModel.setEnterpriseId(user.getEnterpriseId());
                    detailModel.setEnterpriseName(user.getEnterpriseName());
                    incomeSettlementDetailMapper.save(detailModel);
                });
            }
        });
    }

    /**
     * preCheck
     * @param loadingBillHeaderModelList
     */
    private void preCheck(List<BmsLoadingBillHeaderModel> loadingBillHeaderModelList) {
        if (CollectionUtil.isEmpty(loadingBillHeaderModelList)) {
            throw new BizException("派车单不存在");
        }
        if (loadingBillHeaderModelList.stream().map(BmsLoadingBillHeaderModel::getSettlementDepartmentId).distinct().count() > 1) {
            throw new BizException("结算部门不相同");
        }
        if (loadingBillHeaderModelList.stream().map(BmsLoadingBillHeaderModel::getCargoOwnerId).distinct().count() > 1) {
            throw new BizException("货主不相同");
        }
    }


}
