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.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.ShipBillHeadMapper;
import com.prolog.rdc.tms.model.dto.carrier.reconciliation.CarrierReconciliationFeeDetailDTO;
import com.prolog.rdc.tms.model.dto.carrier.reconciliation.CarrierReconciliationShipBillDTO;
import com.prolog.rdc.tms.model.dto.pay.BmsBillPayFeeDetailDTO;
import com.prolog.rdc.tms.model.dto.shipbill.BmsLogOperationDTO;
import com.prolog.rdc.tms.model.dto.shipbill.BmsShipBillDetailDTO;
import com.prolog.rdc.tms.model.entity.BmsBillPayFeeDetailModel;
import com.prolog.rdc.tms.model.entity.BmsBillPayFeeHeaderModel;
import com.prolog.rdc.tms.model.entity.BmsShipBillHeaderModel;
import com.prolog.rdc.tms.model.entity.SysRefdatadModel;
import com.prolog.rdc.tms.model.vo.carrier.reconciliation.CarrierReconciliationOperateVO;
import com.prolog.rdc.tms.model.vo.carrier.reconciliation.CarrierReconciliationQueryVO;
import com.prolog.rdc.tms.service.BmsBillLogService;
import com.prolog.rdc.tms.service.BmsCarrierReconciliationService;
import com.prolog.rdc.tms.service.BmsShipBillService;
import com.prolog.rdc.tms.util.ConvertorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author hongbo.pan
 * @date 2023/6/20
 */
@Service
public class BmsCarrierReconciliationServiceImpl implements BmsCarrierReconciliationService {

    @Autowired
    private BmsBillPayFeeHeaderMapper payFeeHeaderMapper;

    @Autowired
    private BmsBillPayFeeDetailMapper payFeeDetailMapper;

    @Autowired
    private ShipBillHeadMapper shipBillHeadMapper;

    @Autowired
    private BmsShipBillService shipBillService;

    @Autowired
    private BmsBillLogService logService;

    @Autowired
    private ConvertorUtil convertorUtil;

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

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

    @Override
    public List<BmsShipBillDetailDTO> getItem(Long id) {
        List<BmsShipBillDetailDTO> list = Lists.newArrayList();
        List<Long> shipBillIdList = getShipBillIdList(id);
        if (CollectionUtil.isNotEmpty(shipBillIdList)) {
            list = shipBillIdList.stream().flatMap(shipBillId -> shipBillService.getItem(shipBillId).stream()).collect(Collectors.toList());
        }
        return list;
    }

    @Override
    public List<Object> getAudit(Long id) {
        return Lists.newArrayList();
    }

    @Override
    public List<BmsBillPayFeeDetailDTO> getDetail(Long id) {
        BmsBillPayFeeHeaderModel headerModel = payFeeHeaderMapper.findById(id, BmsBillPayFeeHeaderModel.class);
        List<BmsBillPayFeeDetailDTO> dtoList = Lists.newArrayList();
        List<BmsBillPayFeeDetailModel> modelList = findDetail(headerModel);
        if (CollectionUtil.isNotEmpty(modelList)) {
            List<SysRefdatadModel> dicData = convertorUtil.getDictionary(new String[]{
                    BmsConstant.EXPENSE_STATUS, // 费用状态
                    BmsConstant.BILLING_STATUS // 计费状态
            });
            dtoList = modelList.stream().map(dto -> {
                BmsBillPayFeeDetailDTO detailDTO = BeanUtil.copyProperties(dto, BmsBillPayFeeDetailDTO.class);
                detailDTO.setExpenseStatusName(ConvertorUtil.getName(dicData, BmsConstant.EXPENSE_STATUS, String.valueOf(detailDTO.getExpenseStatus())));
                detailDTO.setBillingSourceName(detailDTO.getBillingSource() == 1 ? "价格表生成" : "手工录入");
                detailDTO.setBillingTypeName(detailDTO.getBillingType());
                detailDTO.setBillingStatusName(ConvertorUtil.getName(dicData, BmsConstant.BILLING_STATUS, String.valueOf(detailDTO.getBillingStatus())));
                return detailDTO;
            }).collect(Collectors.toList());
        }
        return dtoList;
    }

    @Override
    public List<BmsLogOperationDTO> getLog(Long id) {
        return logService.getLog(LogTypeEnum.PAY_FEE, id.toString());
    }

    @Override
    public void submit(CarrierReconciliationOperateVO vo) {
        if ("S".equals(vo.getType())) {
            submitByFee(vo.getIdList());
        } else {
            submitByFeeDetail(vo.getIdList());
        }

    }

    @Override
    public void confirm(CarrierReconciliationOperateVO vo) {
        if ("S".equals(vo.getType())) {
            confirmByFee(vo.getIdList());
        } else {
            confirmByFeeDetail(vo.getIdList());
        }
    }

    /**
     * submitByFee
     * @param idList
     */
    private void submitByFee(List<Long> idList) {
        operateByFee(idList, BmsConstant.CHECK_STATUS_0, BmsConstant.CHECK_STATUS_1);
    }

    /**
     * submitByFee
     * @param idList
     */
    private void submitByFeeDetail(List<Long> idList) {
        operateByFeeDetail(idList, BmsConstant.CHECK_STATUS_0, BmsConstant.CHECK_STATUS_1);
    }

    /**
     * confirmByFee
     * @param idList
     */
    private void confirmByFee(List<Long> idList) {
        operateByFee(idList, BmsConstant.CHECK_STATUS_1, BmsConstant.CHECK_STATUS_2);
    }

    /**
     * confirmByFeeDetail
     * @param idList
     */
    private void confirmByFeeDetail(List<Long> idList) {
        operateByFeeDetail(idList, BmsConstant.CHECK_STATUS_1, BmsConstant.CHECK_STATUS_2);
    }

    /**
     * operateByFee
     * @param idList
     */
    private void operateByFee(List<Long> idList, Integer checkStatus, Integer toCheckStatus) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        List<BmsBillPayFeeHeaderModel> payFeeHeaderModelList = payFeeHeaderMapper.findByIds(BmsBillPayFeeHeaderModel.class, idList.toArray(), null);
        if (CollectionUtil.isEmpty(payFeeHeaderModelList)) {
            throw new BizException("费用不存在");
        }
        if (payFeeHeaderModelList.stream().anyMatch(payFeeHeaderModel -> !checkStatus.equals(payFeeHeaderModel.getCheckStatus()))) {
            throw new BizException("费用结算状态错误");
        }
        List<BmsBillPayFeeDetailModel> detailModelList = payFeeHeaderModelList.stream().flatMap(payFee -> findDetail(payFee).stream()).collect(Collectors.toList());
        detailModelList.forEach(detailModel -> {
            detailModel.setCheckStatus(toCheckStatus);
            detailModel.setUpdateperson(user.getNickname());
            detailModel.setUpdatetime(DateUtil.date());
            payFeeDetailMapper.update(detailModel);
        });

        String memo = BmsConstant.CHECK_STATUS_1.equals(toCheckStatus) ? "对账提交" : "对账确认";
        payFeeHeaderModelList.forEach(payFeeHeader -> {
            payFeeHeader.setCheckStatus(toCheckStatus);
            payFeeHeader.setUpdateperson(user.getNickname());
            payFeeHeader.setUpdatetime(DateUtil.date());
            payFeeHeaderMapper.update(payFeeHeader);
            logService.addLog(LogTypeEnum.PAY_FEE, payFeeHeader.getId().toString(), memo);
            logService.addLog(LogTypeEnum.SHIP_BILL, payFeeHeader.getBillNo(), memo);
        });
    }

    /**
     * operateByFeeDetail
     * @param idList
     */
    private void operateByFeeDetail(List<Long> idList, Integer checkStatus, Integer toCheckStatus) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        List<BmsBillPayFeeDetailModel> payFeeDetailModelList = payFeeDetailMapper.findByIds(BmsBillPayFeeDetailModel.class, idList.toArray(), null);
        if (CollectionUtil.isEmpty(payFeeDetailModelList)) {
            throw new BizException("费用明细不存在");
        }
        if (payFeeDetailModelList.stream().anyMatch(payFeeDetailModel -> !checkStatus.equals(payFeeDetailModel.getCheckStatus()))) {
            throw new BizException("费用明细结算状态错误");
        }
        String memo = BmsConstant.CHECK_STATUS_1.equals(toCheckStatus) ? "对账提交" : "对账确认";
        payFeeDetailModelList.forEach(detailModel -> {
            detailModel.setCheckStatus(toCheckStatus);
            detailModel.setUpdateperson(user.getNickname());
            detailModel.setUpdatetime(DateUtil.date());
            payFeeDetailMapper.update(detailModel);
            Criteria criteria = Criteria.forClass(BmsBillPayFeeHeaderModel.class);
            Restriction r1 = Restrictions.eq(BmsBillPayFeeHeaderModel::getBillId, detailModel.getBillId());
            Restriction r2 = Restrictions.eq(BmsBillPayFeeHeaderModel::getBillNo, detailModel.getBillNo());
            Restriction r3 = Restrictions.eq(BmsBillPayFeeHeaderModel::getEnterpriseId, detailModel.getEnterpriseId());
            criteria.setRestriction(Restrictions.and(r1, r2, r3));
            List<BmsBillPayFeeHeaderModel> payFeeHeaderModelList = payFeeHeaderMapper.findByCriteria(criteria);
            List<BmsBillPayFeeDetailModel> feeDetailModelList = payFeeHeaderModelList.stream().flatMap(payFee -> findDetail(payFee).stream()).collect(Collectors.toList());
            if (feeDetailModelList.stream().allMatch(feeDetailModel -> toCheckStatus.equals(feeDetailModel.getCheckStatus()))) {
                payFeeHeaderModelList.forEach(payFeeHeader -> {
                    payFeeHeader.setCheckStatus(toCheckStatus);
                    payFeeHeader.setUpdateperson(user.getNickname());
                    payFeeHeader.setUpdatetime(DateUtil.date());
                    payFeeHeaderMapper.update(payFeeHeader);
                    logService.addLog(LogTypeEnum.PAY_FEE, payFeeHeader.getId().toString(), memo);
                    logService.addLog(LogTypeEnum.SHIP_BILL, payFeeHeader.getBillNo(), memo);
                });
            }
        });
    }

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

    /**
     * 设置数据权限
     * @param vo
     * @return
     */
    private CarrierReconciliationQueryVO setDataPermission(CarrierReconciliationQueryVO 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 CarrierReconciliationShipBillDTO transfer(CarrierReconciliationShipBillDTO 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
        });
    }

    /**
     * getShipBillIdList
     * @param id
     * @return
     */
    private List<Long> getShipBillIdList(Long id) {
        List<Long> shipBillIdList = Lists.newArrayList();
        BmsBillPayFeeHeaderModel headerModel = payFeeHeaderMapper.findById(id, BmsBillPayFeeHeaderModel.class);
        if (headerModel.getBillType() == 10) {
            shipBillIdList.add(headerModel.getBillId());
        } else {
            Criteria criteria = Criteria.forClass(BmsShipBillHeaderModel.class);
            Restriction r1 = Restrictions.eq(BmsShipBillHeaderModel::getLoadingId, headerModel.getBillId());
            Restriction r2 = Restrictions.eq(BmsShipBillHeaderModel::getEnterpriseId, headerModel.getEnterpriseId());
            criteria.setRestriction(Restrictions.and(r1, r2));
            List<BmsShipBillHeaderModel> modelList = shipBillHeadMapper.findByCriteria(criteria);
            if (CollectionUtil.isNotEmpty(modelList)) {
                shipBillIdList = modelList.stream().map(BmsShipBillHeaderModel::getId).collect(Collectors.toList());
            }
        }
        return shipBillIdList;
    }
}
