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 cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Lists;
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.BmsBillPayFeeDetailMapper;
import com.prolog.rdc.tms.dao.mapper.BmsBillPayFeeHeaderMapper;
import com.prolog.rdc.tms.dao.mapper.BmsPaySettlementDetailMapper;
import com.prolog.rdc.tms.dao.mapper.BmsPaySettlementMapper;
import com.prolog.rdc.tms.dao.mapper.LogisticsContractMapper;
import com.prolog.rdc.tms.dao.mapper.ShipBillHeadMapper;
import com.prolog.rdc.tms.model.dto.carrier.settlement.CarrierSettlementFeeDetailDTO;
import com.prolog.rdc.tms.model.entity.BmsBillPayFeeDetailModel;
import com.prolog.rdc.tms.model.entity.BmsBillPayFeeHeaderModel;
import com.prolog.rdc.tms.model.entity.BmsLogisticsContractModel;
import com.prolog.rdc.tms.model.entity.BmsPaySettlementDetailModel;
import com.prolog.rdc.tms.model.entity.BmsPaySettlementModel;
import com.prolog.rdc.tms.model.entity.BmsShipBillHeaderModel;
import com.prolog.rdc.tms.model.entity.SysRefdatadModel;
import com.prolog.rdc.tms.model.vo.carrier.settlement.CarrierSettlementQueryVO;
import com.prolog.rdc.tms.model.vo.carrier.settlement.CarrierSettlementVO;
import com.prolog.rdc.tms.proxy.MainDataProxy;
import com.prolog.rdc.tms.service.BmsBillLogService;
import com.prolog.rdc.tms.service.BmsCarrierSettlementService;
import com.prolog.rdc.tms.util.ConvertorUtil;
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.stream.Collectors;

/**
 * @author hongbo.pan
 * @date 2023/6/25
 */
@Service
public class BmsCarrierSettlementServiceImpl implements BmsCarrierSettlementService {

    @Autowired
    private BmsBillPayFeeHeaderMapper payFeeHeaderMapper;

    @Autowired
    private BmsBillPayFeeDetailMapper payFeeDetailMapper;

    @Autowired
    private ShipBillHeadMapper shipBillHeadMapper;

    @Autowired
    private MainDataProxy mainDataProxy;

    @Autowired
    private BmsBillLogService logService;

    @Autowired
    private ConvertorUtil convertorUtil;

    @Autowired
    private LogisticsContractMapper logisticsContractMapper;

    @Autowired
    private BmsPaySettlementMapper paySettlementMapper;

    @Autowired
    private BmsPaySettlementDetailMapper paySettlementDetailMapper;

    @Override
    public Page<CarrierSettlementFeeDetailDTO> findSettlementFeeDetail(CarrierSettlementQueryVO vo) {
        PageUtils.startPage(vo.getPageNum(), vo.getPageSize());
        setDataPermission(vo);
        List<CarrierSettlementFeeDetailDTO> dtoList = payFeeHeaderMapper.findSettlementFeeDetail(vo);
        if (CollectionUtil.isNotEmpty(dtoList)) {
            List<SysRefdatadModel> dicData = getDic();
            dtoList.forEach(dto -> transfer(dto, dicData));
        }
        return PageUtils.getPage(dtoList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void feeSettlement(CarrierSettlementVO vo) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        List<BmsBillPayFeeDetailModel> payFeeDetailModelList = payFeeDetailMapper.findByIds(BmsBillPayFeeDetailModel.class, vo.getFeeDetailIdList().toArray(), null);
        if (CollectionUtil.isNotEmpty(payFeeDetailModelList)) {
            List<BmsShipBillHeaderModel> shipBillHeaderModelList = shipBillHeadMapper.findByIds(BmsShipBillHeaderModel.class, payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getBillId).toArray(), null);
            preCheck(shipBillHeaderModelList);
            String settlementNo = mainDataProxy.getCodeNumberByCode(BmsConstant.BMS_SHIP_BILL_SETTLEMENT_ID);
            BmsPaySettlementModel paySettlementModel = BeanUtil.copyProperties(vo, BmsPaySettlementModel.class);
            paySettlementModel.setCarrierType(BmsConstant.LINE_CARRIER_TYPE_CYF);
            paySettlementModel.setSettlementNo(settlementNo);
            paySettlementModel.setPayFeeType(1);
            paySettlementModel.setStatus(0);
            paySettlementModel.setTax(payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getTax).reduce(BigDecimal.ZERO, NumberUtil::add));
            paySettlementModel.setNoTaxAmt(payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getNoTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
            paySettlementModel.setTaxAmt(payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
            paySettlementModel.setCreateperson(user.getNickname());
            paySettlementModel.setCreatetime(DateUtil.date());
            paySettlementModel.setUpdateperson(user.getNickname());
            paySettlementModel.setUpdatetime(DateUtil.date());
            paySettlementModel.setEnterpriseId(user.getEnterpriseId());
            paySettlementModel.setEnterpriseName(user.getEnterpriseName());
            paySettlementMapper.insertSelective(paySettlementModel);
            logService.addLog(LogTypeEnum.PAY_SETTLEMENT, paySettlementModel.getId().toString(), "新增结算单");
            batchAddPayFeeDetail(payFeeDetailModelList, paySettlementModel);
        }
    }

    @Override
    public BmsPaySettlementModel initPaySettlement(List<Long> feeDetailIdList) {
        List<BmsBillPayFeeDetailModel> payFeeDetailModelList = payFeeDetailMapper.findByIds(BmsBillPayFeeDetailModel.class, feeDetailIdList.toArray(), null);
        if (CollectionUtil.isEmpty(payFeeDetailModelList)) {
            throw new BizException("费用不存在");
        }
        List<BmsShipBillHeaderModel> shipBillHeaderModelList = shipBillHeadMapper.findByIds(BmsShipBillHeaderModel.class, payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getBillId).toArray(), null);
        preCheck(shipBillHeaderModelList);
        BmsShipBillHeaderModel shipBillHeaderModel = shipBillHeaderModelList.get(0);
        BmsPaySettlementModel paySettlementModel = new BmsPaySettlementModel();
        paySettlementModel.setSettlementCompany(shipBillHeaderModel.getSettlementDepartment());
        paySettlementModel.setSettlementCompanyId(NumberUtil.parseLong(shipBillHeaderModel.getSettlementDepartmentId()));
        paySettlementModel.setSettlementCompanyCode(shipBillHeaderModel.getSettlementDepartmentCode());
        paySettlementModel.setSettlementDepartment(shipBillHeaderModel.getSettlementDepartment());
        paySettlementModel.setSettlementDepartmentId(shipBillHeaderModel.getSettlementDepartmentId());
        paySettlementModel.setSettlementDepartmentCode(shipBillHeaderModel.getSettlementDepartmentCode());
        paySettlementModel.setCarrierId(shipBillHeaderModel.getCarrierId());
        paySettlementModel.setCarrierCode(shipBillHeaderModel.getCarrierCode());
        paySettlementModel.setCarrierName(shipBillHeaderModel.getCarrierName());
        paySettlementModel.setTax(payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getTax).reduce(BigDecimal.ZERO, NumberUtil::add));
        paySettlementModel.setNoTaxAmt(payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getNoTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
        paySettlementModel.setTaxAmt(payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
        List<String> taxPoint = payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getTaxPoint).distinct().collect(Collectors.toList());
        if (taxPoint.size() == 1) {
            paySettlementModel.setTaxPoint(taxPoint.get(0));
        }
        Criteria criteria = Criteria.forClass(BmsLogisticsContractModel.class);
        Restriction r1 = Restrictions.in(BmsLogisticsContractModel::getContractName, payFeeDetailModelList.stream().map(BmsBillPayFeeDetailModel::getBillingContract).distinct().collect(Collectors.toList()));
        Restriction r2 = Restrictions.eq(BmsLogisticsContractModel::getEnterpriseId, shipBillHeaderModel.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) {
            paySettlementModel.setSettlementCycle(settlementType.get(0).toString());
        }
        return paySettlementModel;
    }


    /**
     * 设置数据权限
     * @param vo
     * @return
     */
    private CarrierSettlementQueryVO setDataPermission(CarrierSettlementQueryVO vo) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        // 承运商
        if ("S".equals(user.getUserType())){
            vo.setCarrierId(Long.valueOf(user.getLoginUserCompanyInfo().getId()));
        } else {
            // 部门权限
            vo.setDeptList(user.getDepts());
            // 数据权限
            if (user.getOwnedAllDataAuthFlag() == 0) {
                // 货主
                vo.setConsignorList(user.getConsignors());
                // 客户
                vo.setCustomerList(user.getCustomers());
                // 供应商
                vo.setSupplierList(user.getSuppliers());
                // 仓库
                vo.setWarehouseList(user.getWarehouses());
                List<Long> consigneeUnitIdList = Lists.newArrayList();
                if (CollectionUtil.isNotEmpty(vo.getCustomerList())) {
                    consigneeUnitIdList.addAll(vo.getCustomerList());
                }
                if (CollectionUtil.isNotEmpty(vo.getSupplierList())) {
                    consigneeUnitIdList.addAll(vo.getSupplierList());
                }
                if (CollectionUtil.isNotEmpty(vo.getWarehouseList())) {
                    consigneeUnitIdList.addAll(vo.getWarehouseList());
                }
                vo.setConsigneeUnitIdList(consigneeUnitIdList.stream().filter(ObjectUtil::isNotNull).distinct().collect(Collectors.toList()));
            }
        }
        return vo;
    }

    /**
     * transfer
     * @param dto
     * @param dicData
     * @return CarrierReconciliationShipBillDTO
     */
    private CarrierSettlementFeeDetailDTO transfer(CarrierSettlementFeeDetailDTO dto, List<SysRefdatadModel> dicData) {
        dto.setOrderTypeName(ConvertorUtil.getName(dicData, BmsConstant.ORDER_TYPE, String.valueOf(dto.getOrderType())));
        dto.setOrderBusinessTypeName(ConvertorUtil.getName(dicData, BmsConstant.ORDER_BUSINESS_TYPE, String.valueOf(dto.getOrderBusinessType())));
        dto.setTemperatureRequirementName(ConvertorUtil.getName(dicData, BmsConstant.TEMPERATURE_REQUIREMENT, String.valueOf(dto.getTemperatureRequirement())));
        dto.setShipperTypeName(ConvertorUtil.getName(dicData, BmsConstant.SHIPPER_TYPE, String.valueOf(dto.getShipperType())));
        dto.setConsigneeTypeName(ConvertorUtil.getName(dicData, BmsConstant.CONSIGNEE_TYPE, String.valueOf(dto.getConsigneeType())));
        dto.setSourceName(ConvertorUtil.getName(dicData, BmsConstant.SOURCE, String.valueOf(dto.getSource())));
        dto.setShipBillStatusName(ConvertorUtil.getName(dicData, BmsConstant.SHIPBILL_STATUS, String.valueOf(dto.getShipBillStatus())));
        dto.setSynchronousStatusName(ConvertorUtil.getName(dicData, BmsConstant.SYNCHRONOUS_STATUS, String.valueOf(dto.getSynchronousStatus())));
        dto.setAbnormalStatusName(ConvertorUtil.getName(dicData, BmsConstant.ABNORMAL_STATUS, String.valueOf(dto.getAbnormalStatus())));
        dto.setDeliveryMethodTypeName(ConvertorUtil.getName(dicData, BmsConstant.DELIVERY_METHOD_TYPE, String.valueOf(dto.getDeliveryMethodType())));
        dto.setTransportTypeName(ConvertorUtil.getName(dicData, BmsConstant.TRANSPORT_TYPE, String.valueOf(dto.getTransportType())));
        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.setCheckStatusName(ConvertorUtil.getName(dicData, BmsConstant.CHECK_STATUS, String.valueOf(dto.getCheckStatus())));
        return dto;
    }

    /**
     * getDic
     * @return List<SysRefdatadModel>
     */
    private List<SysRefdatadModel> getDic() {
        return convertorUtil.getDictionary(new String[] {
                BmsConstant.SHIPBILL_STATUS,
                BmsConstant.SHIPPER_TYPE,
                BmsConstant.ABNORMAL_STATUS,
                BmsConstant.ORDER_TYPE,
                BmsConstant.CONSIGNEE_TYPE,
                BmsConstant.ORDER_BUSINESS_TYPE,
                BmsConstant.SETTLEMENT_STATUS,
                BmsConstant.SOURCE,
                BmsConstant.COST_STATUS,
                BmsConstant.SYNCHRONOUS_STATUS,
                BmsConstant.TEMPERATURE_REQUIREMENT,
                BmsConstant.LINE_CARRIER_TYPE,
                BmsConstant.DELIVERY_METHOD_TYPE,
                BmsConstant.TRANSPORT_TYPE,
                BmsConstant.FEE_CLT_TYPE,
                BmsConstant.CHECK_STATUS
        });
    }

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

    /**
     * batchAddPayFeeDetail
     * @param payFeeDetailModelList
     * @param paySettlementModel
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchAddPayFeeDetail(List<BmsBillPayFeeDetailModel> payFeeDetailModelList, BmsPaySettlementModel paySettlementModel) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        payFeeDetailModelList.stream().forEach(payFeeDetailModel -> {
            payFeeDetailModel.setCheckStatus(BmsConstant.CHECK_STATUS_3);
            payFeeDetailModel.setUpdateperson(user.getNickname());
            payFeeDetailModel.setUpdatetime(DateUtil.date());
            payFeeDetailMapper.update(payFeeDetailModel);
            List<BmsBillPayFeeHeaderModel> payFeeHeaderList = getPayFeeHeader(payFeeDetailModel);
            BmsPaySettlementDetailModel paySettlementDetailModel = new BmsPaySettlementDetailModel();
            paySettlementDetailModel.setSettlementId(paySettlementModel.getId());
            if (CollectionUtil.isNotEmpty(payFeeHeaderList)) {
                BmsBillPayFeeHeaderModel payFeeHeader = payFeeHeaderList.get(0);
                paySettlementDetailModel.setFeeId(payFeeHeader.getId());
                List<BmsBillPayFeeDetailModel> payFeeDetailList = getPayFeeDetail(payFeeHeader);
                if (CollectionUtil.isNotEmpty(payFeeDetailList) && payFeeDetailList.stream().allMatch(payFeeDetail -> BmsConstant.CHECK_STATUS_3.equals(payFeeDetail.getCheckStatus()))) {
                    payFeeHeader.setCheckStatus(BmsConstant.CHECK_STATUS_3);
                    payFeeHeader.setUpdateperson(user.getNickname());
                    payFeeHeader.setUpdatetime(DateUtil.date());
                    payFeeHeaderMapper.update(payFeeHeader);
                    logService.addLog(LogTypeEnum.PAY_FEE, payFeeHeader.getId().toString(), "加入结算单，结算单号" + paySettlementModel.getSettlementNo());
                }
            }
            paySettlementDetailModel.setFeeDetailId(payFeeDetailModel.getId());
            paySettlementDetailModel.setExpenseNo(payFeeDetailModel.getExpenseNo());
            paySettlementDetailModel.setBillId(payFeeDetailModel.getBillId());
            paySettlementDetailModel.setBillNo(payFeeDetailModel.getBillNo());
            paySettlementDetailModel.setBillType(10);
            paySettlementDetailModel.setPayFeeType(1);
            paySettlementDetailModel.setTaxPoint(payFeeDetailModel.getTaxPoint());
            paySettlementDetailModel.setTaxAmt(payFeeDetailModel.getTaxAmt());
            paySettlementDetailModel.setNoTaxAmt(payFeeDetailModel.getNoTaxAmt());
            paySettlementDetailModel.setTax(payFeeDetailModel.getTax());
            paySettlementDetailModel.setCreateperson(user.getNickname());
            paySettlementDetailModel.setCreatetime(DateUtil.date());
            paySettlementDetailModel.setUpdateperson(user.getNickname());
            paySettlementDetailModel.setUpdatetime(DateUtil.date());
            paySettlementDetailModel.setEnterpriseId(user.getEnterpriseId());
            paySettlementDetailModel.setEnterpriseName(user.getEnterpriseName());
            paySettlementDetailMapper.save(paySettlementDetailModel);
        });
    }

    /**
     * getPayFeeDetail
     * @param payFeeHeaderModel
     * @return
     */
    private List<BmsBillPayFeeDetailModel> getPayFeeDetail(BmsBillPayFeeHeaderModel payFeeHeaderModel) {
        Criteria criteria = Criteria.forClass(BmsBillPayFeeDetailModel.class);
        Restriction r1 = Restrictions.eq(BmsBillPayFeeDetailModel::getBillId, payFeeHeaderModel.getBillId());
        Restriction r2 = Restrictions.eq(BmsBillPayFeeDetailModel::getBillNo, payFeeHeaderModel.getBillNo());
        Restriction r3 = Restrictions.eq(BmsBillPayFeeDetailModel::getEnterpriseId, payFeeHeaderModel.getEnterpriseId());
        criteria.setRestriction(Restrictions.and(r1, r2, r3));
        return payFeeDetailMapper.findByCriteria(criteria);
    }

    /**
     * getPayFeeHeader
     * @param payFeeDetailModel
     * @return
     */
    private List<BmsBillPayFeeHeaderModel> getPayFeeHeader(BmsBillPayFeeDetailModel payFeeDetailModel) {
        Criteria criteria = Criteria.forClass(BmsBillPayFeeHeaderModel.class);
        Restriction r1 = Restrictions.eq(BmsBillPayFeeHeaderModel::getBillId, payFeeDetailModel.getBillId());
        Restriction r2 = Restrictions.eq(BmsBillPayFeeHeaderModel::getBillNo, payFeeDetailModel.getBillNo());
        Restriction r3 = Restrictions.eq(BmsBillPayFeeHeaderModel::getEnterpriseId, payFeeDetailModel.getEnterpriseId());
        criteria.setRestriction(Restrictions.and(r1, r2, r3));
        return payFeeHeaderMapper.findByCriteria(criteria);
    }
}
