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.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.prolog.common.util.DateUtils;
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.Order;
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.product.core.tools.util.ToolKits;
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.model.dto.carrier.settlement.CarrierSettlementManagementDTO;
import com.prolog.rdc.tms.model.dto.pay.BmsBillPayFeeDetailDTO;
import com.prolog.rdc.tms.model.dto.shipbill.BmsLogOperationDTO;
import com.prolog.rdc.tms.model.entity.BmsBillPayFeeDetailModel;
import com.prolog.rdc.tms.model.entity.BmsBillPayFeeHeaderModel;
import com.prolog.rdc.tms.model.entity.BmsPaySettlementDetailModel;
import com.prolog.rdc.tms.model.entity.BmsPaySettlementModel;
import com.prolog.rdc.tms.model.entity.SysRefdatadModel;
import com.prolog.rdc.tms.model.vo.carrier.settlement.CarrierSettlementManagementQueryVO;
import com.prolog.rdc.tms.model.vo.carrier.settlement.CarrierSettlementManagementUpdateVO;
import com.prolog.rdc.tms.service.BmsBillLogService;
import com.prolog.rdc.tms.service.BmsCarrierSettlementManagementService;

import com.prolog.rdc.tms.service.BmsCarrierSettlementService;
import com.prolog.rdc.tms.util.ConvertorUtil;
import org.apache.ibatis.plugin.PluginException;
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/29
 */
@Service
public class BmsCarrierSettlementManagementServiceImpl implements BmsCarrierSettlementManagementService {

    @Autowired
    private BmsPaySettlementMapper paySettlementMapper;

    @Autowired
    private ConvertorUtil convertorUtil;

    @Autowired
    private BmsBillLogService logService;

    @Autowired
    private BmsPaySettlementDetailMapper paySettlementDetailMapper;

    @Autowired
    private BmsBillPayFeeDetailMapper payFeeDetailMapper;

    @Autowired
    private BmsBillPayFeeHeaderMapper payFeeHeaderMapper;

    @Autowired
    private BmsCarrierSettlementService carrierSettlementService;

    @Override
    public Page<CarrierSettlementManagementDTO> findByCondition(CarrierSettlementManagementQueryVO vo) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        Criteria criteria = Criteria.forClass(BmsPaySettlementModel.class);
        Restriction r1 = ToolKits.isEmpty(vo.getSettlementNo()) ? null : Restrictions.like(BmsPaySettlementModel::getSettlementNo, "%" + vo.getSettlementNo() + "%");
        Restriction r2 = ToolKits.isEmpty(vo.getSettlementName()) ? null : Restrictions.like(BmsPaySettlementModel::getSettlementName, "%" + vo.getSettlementName() + "%");
        Restriction r3 = ToolKits.isEmpty(vo.getSettlementDepartment()) ? null : Restrictions.like(BmsPaySettlementModel::getSettlementDepartment, "%" + vo.getSettlementDepartment() + "%");
        Restriction r4 = ToolKits.isEmpty(vo.getSettlementCompany()) ? null : Restrictions.like(BmsPaySettlementModel::getSettlementCompany, "%" + vo.getSettlementCompany() + "%");
        Restriction r5 = ToolKits.isEmpty(vo.getStatus()) ? null : Restrictions.eq(BmsPaySettlementModel::getStatus, vo.getStatus());
        Restriction r6 = ToolKits.isEmpty(vo.getCarrierName()) ? null : Restrictions.like(BmsPaySettlementModel::getCarrierName, "%" + vo.getCarrierName() + "%");
        Restriction r7 = ToolKits.isEmpty(vo.getCreateStartTime()) ? null : Restrictions.ge(BmsPaySettlementModel::getCreatetime, DateUtils.startOfToday(vo.getCreateStartTime()));
        Restriction r8 = ToolKits.isEmpty(vo.getCreateEndTime()) ? null : Restrictions.le(BmsPaySettlementModel::getCreatetime, DateUtils.endOfToday(vo.getCreateEndTime()));
        Restriction r9 = ToolKits.isEmpty(vo.getUpdateStartTime()) ? null : Restrictions.ge(BmsPaySettlementModel::getUpdatetime, DateUtils.startOfToday(vo.getUpdateStartTime()));
        Restriction r10 = ToolKits.isEmpty(vo.getUpdateEndTime()) ? null : Restrictions.le(BmsPaySettlementModel::getUpdatetime, DateUtils.endOfToday(vo.getUpdateEndTime()));
        Restriction r11 = null;
        if (user.getOwnedAllDataAuthFlag().equals(BmsConstant.USER_DEPARTMENT_AUTH_Y)) {
            r11 = Restrictions.in(BmsPaySettlementModel::getSettlementDepartmentId, user.getDepts().toArray());
        }
        Restriction r12 = Restrictions.ne(BmsPaySettlementModel::getStatus, 99);
        Restriction r13 = Restrictions.eq(BmsPaySettlementModel::getEnterpriseId, user.getEnterpriseId());
        Restriction r14 = Restrictions.eq(BmsPaySettlementModel::getPayFeeType, 1);
        criteria.setRestriction(Restrictions.and(r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14));
        criteria.setOrder(Order.newInstance().desc(BmsPaySettlementModel::getUpdatetime));
        PageUtils.startPage(vo.getPageNum(), vo.getPageSize());
        List<BmsPaySettlementModel> list = paySettlementMapper.findByCriteria(criteria);
        List<CarrierSettlementManagementDTO> dtos = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(list)) {
            List<SysRefdatadModel> dicData = convertorUtil.getDictionary(new String[]{
                    // 结算状态
                    BmsConstant.CLOSE_ACCOUNT_STATUS,
                    // 结算周期
                    BmsConstant.SETTLEMENT_CYCLE
            });
            dtos = list.stream().map(model -> {
                CarrierSettlementManagementDTO dto = BeanUtil.copyProperties(model, CarrierSettlementManagementDTO.class);
                dto.setStatusName(ConvertorUtil.getName(dicData, BmsConstant.CLOSE_ACCOUNT_STATUS, String.valueOf(dto.getStatus())));
                dto.setSettlementCycleName(ConvertorUtil.getName(dicData, BmsConstant.SETTLEMENT_CYCLE, String.valueOf(dto.getSettlementCycle())));
                return dto;
            }).collect(Collectors.toList());
        }
        // 分页设置
        Page<CarrierSettlementManagementDTO> page = PageUtils.getPage(dtos);
        if (list instanceof com.github.pagehelper.Page) {
            com.github.pagehelper.Page<BmsPaySettlementModel> p0 = (com.github.pagehelper.Page<BmsPaySettlementModel>) list;
            page.setPageCount(p0.getPages());
            page.setPageNum(p0.getPageNum());
            page.setPageSize(p0.getPageSize());
            page.setTotalCount(p0.getTotal());
            page.setStartRow(p0.getStartRow());
            page.setEndRow(p0.getEndRow());
        }
        return page;
    }

    @Override
    public CarrierSettlementManagementDTO detail(Long id) {
        BmsPaySettlementModel paySettlementModel = paySettlementMapper.findById(id, BmsPaySettlementModel.class);
        if (ObjectUtil.isEmpty(paySettlementModel)) {
            throw new BizException("结算单不存在");
        }
        List<SysRefdatadModel> dicData = convertorUtil.getDictionary(new String[]{
                // 结算状态
                BmsConstant.CLOSE_ACCOUNT_STATUS,
                // 结算周期
                BmsConstant.SETTLEMENT_CYCLE
        });
        CarrierSettlementManagementDTO dto = BeanUtil.copyProperties(paySettlementModel, CarrierSettlementManagementDTO.class);
        dto.setSettlementCycleName(ConvertorUtil.getName(dicData, BmsConstant.SETTLEMENT_CYCLE, String.valueOf(paySettlementModel.getSettlementCycle())));
        dto.setStatusName(ConvertorUtil.getName(dicData, BmsConstant.CLOSE_ACCOUNT_STATUS, String.valueOf(paySettlementModel.getStatus())));
        return dto;
    }

    @Override
    public List<BmsBillPayFeeDetailDTO> getFeeDetail(Long id) {
        List<BmsBillPayFeeDetailDTO> dtoList = Lists.newArrayList();
        List<BmsBillPayFeeDetailModel> modelList = getPayFeeDetail(Lists.newArrayList(id));
        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_SETTLEMENT, id.toString());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(CarrierSettlementManagementUpdateVO vo) {
        BmsPaySettlementModel settlementModel = paySettlementMapper.findById(vo.getId(), BmsPaySettlementModel.class);
        if (ObjectUtil.isNull(settlementModel)) {
            throw new BizException("结算单不存在");
        }
        if (0 != settlementModel.getStatus()) {
            throw new PluginException("只有草稿状态下的结算单才能修改");
        }
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        // 删除结算单明细
        Criteria criteria = Criteria.forClass(BmsPaySettlementDetailModel.class);
        Restriction r1 = Restrictions.eq(BmsPaySettlementDetailModel::getSettlementId, settlementModel.getId());
        Restriction r2 = Restrictions.eq(BmsPaySettlementDetailModel::getEnterpriseId, settlementModel.getEnterpriseId());
        criteria.setRestriction(Restrictions.and(r1, r2));
        paySettlementDetailMapper.deleteByCriteria(criteria);
        // 更新费用明细
        updatePayFee(Lists.newArrayList(vo.getId()), BmsConstant.CHECK_STATUS_2);
        List<BmsBillPayFeeDetailModel> addPayFeeDetailList = payFeeDetailMapper.findByIds(BmsBillPayFeeDetailModel.class,
                vo.getFeeDetailIdList().toArray(), null);
        // 新增结算单明细
        carrierSettlementService.batchAddPayFeeDetail(addPayFeeDetailList, settlementModel);
        // 更新结算单
        settlementModel.setSettlementCycle(vo.getSettlementCycle());
        settlementModel.setAccountEndDate(vo.getAccountEndDate());
        settlementModel.setInvoiceEndDate(vo.getInvoiceEndDate());
        settlementModel.setTaxPoint(vo.getTaxPoint());
        settlementModel.setMemo(vo.getMemo());
        settlementModel.setUpdateperson(user.getNickname());
        settlementModel.setUpdatetime(DateUtil.date());
        settlementModel.setTax(addPayFeeDetailList.stream().map(BmsBillPayFeeDetailModel::getTax).reduce(BigDecimal.ZERO, NumberUtil::add));
        settlementModel.setNoTaxAmt(addPayFeeDetailList.stream().map(BmsBillPayFeeDetailModel::getNoTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
        settlementModel.setTaxAmt(addPayFeeDetailList.stream().map(BmsBillPayFeeDetailModel::getTaxAmt).reduce(BigDecimal.ZERO, NumberUtil::add));
        paySettlementMapper.insertSelective(settlementModel);
        logService.addLog(LogTypeEnum.PAY_SETTLEMENT, settlementModel.getId().toString(), "更新结算单");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void audit(List<Long> idList) {
        List<BmsPaySettlementModel> settlementModelList = paySettlementMapper.findByIds(BmsPaySettlementModel.class, idList.toArray(), null);
        if (CollectionUtil.isEmpty(settlementModelList)) {
            throw new BizException("结算单不存在");
        }
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        settlementModelList.forEach(settlementModel -> {
            settlementModel.setUpdateperson(user.getNickname());
            settlementModel.setUpdatetime(DateUtil.date());
            settlementModel.setStatus(2);
            paySettlementMapper.update(settlementModel);
            // 新增结算单日志
            logService.addLog(LogTypeEnum.PAY_SETTLEMENT, settlementModel.getId().toString(), "结算审核完成");
        });
        updatePayFee(idList, BmsConstant.CHECK_STATUS_4);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void invalid(List<Long> idList) {
        List<BmsPaySettlementModel> settlementModelList = paySettlementMapper.findByIds(BmsPaySettlementModel.class, idList.toArray(), null);
        if (CollectionUtil.isEmpty(settlementModelList)) {
            throw new BizException("结算单不存在");
        }
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        settlementModelList.forEach(settlementModel -> {
            settlementModel.setUpdateperson(user.getNickname());
            settlementModel.setUpdatetime(DateUtil.date());
            settlementModel.setStatus(99);
            paySettlementMapper.update(settlementModel);
            // 新增结算单日志
            logService.addLog(LogTypeEnum.PAY_SETTLEMENT, settlementModel.getId().toString(), "结算单作废");
        });
        updatePayFee(idList, BmsConstant.CHECK_STATUS_2);
    }

    /**
     * getPayFeeDetail
     * @param settlementIdList
     * @return
     */
    private List<BmsBillPayFeeDetailModel> getPayFeeDetail(List<Long> settlementIdList) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        Criteria criteria = Criteria.forClass(BmsPaySettlementDetailModel.class);
        Restriction r1 = Restrictions.in(BmsPaySettlementDetailModel::getSettlementId, settlementIdList);
        Restriction r2 = Restrictions.eq(BmsPaySettlementDetailModel::getEnterpriseId, user.getEnterpriseId());
        criteria.setRestriction(Restrictions.and(r1, r2));
        List<BmsPaySettlementDetailModel> settlementDetailModelList = paySettlementDetailMapper.findByCriteria(criteria);
        List<BmsBillPayFeeDetailModel> modelList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(settlementDetailModelList)) {
            modelList = payFeeDetailMapper.findByIds(BmsBillPayFeeDetailModel.class,
                    settlementDetailModelList.stream().map(BmsPaySettlementDetailModel::getFeeDetailId).toArray(), null);
        }
        return modelList;
    }

    /**
     * updatePayFee
     * @param settlementIdList
     * @param checkStatus
     */
    private void updatePayFee(List<Long> settlementIdList, Integer checkStatus) {
        List<BmsBillPayFeeDetailModel> payFeeDetailList = getPayFeeDetail(settlementIdList);
        updatePayFeeDetail(payFeeDetailList, checkStatus);
    }

    /**
     * updatePayFeeDetail
     * @param payFeeDetailList
     * @param checkStatus
     */
    private void updatePayFeeDetail(List<BmsBillPayFeeDetailModel> payFeeDetailList, Integer checkStatus) {
        String logMemo = "结算单审核完成";
        if (BmsConstant.CHECK_STATUS_2.equals(checkStatus)){
            logMemo = "修改结算状态为对账确认";
        }
        if (CollectionUtil.isNotEmpty(payFeeDetailList)) {
            UserEntity user = PlgFxUsrUtils.getUserEntity();
            String finalLogMemo = logMemo;
            payFeeDetailList.forEach(payFeeDetail -> {
                payFeeDetail.setCheckStatus(checkStatus);
                payFeeDetail.setUpdatetime(DateUtil.date());
                payFeeDetail.setUpdateperson(user.getNickname());
                payFeeDetailMapper.update(payFeeDetail);
                Criteria criteria = Criteria.forClass(BmsBillPayFeeHeaderModel.class);
                Restriction r1 = Restrictions.eq(BmsBillPayFeeHeaderModel::getBillId, payFeeDetail.getBillId());
                Restriction r2 = Restrictions.eq(BmsBillPayFeeHeaderModel::getBillNo, payFeeDetail.getBillNo());
                Restriction r3 = Restrictions.eq(BmsBillPayFeeHeaderModel::getEnterpriseId, payFeeDetail.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 -> checkStatus.equals(feeDetailModel.getCheckStatus()))) {
                    payFeeHeaderModelList.forEach(payFeeHeader -> {
                        payFeeHeader.setCheckStatus(checkStatus);
                        payFeeHeader.setUpdateperson(user.getNickname());
                        payFeeHeader.setUpdatetime(DateUtil.date());
                        payFeeHeaderMapper.update(payFeeHeader);
                        logService.addLog(LogTypeEnum.PAY_FEE, payFeeHeader.getId().toString(), finalLogMemo);
                        logService.addLog(LogTypeEnum.SHIP_BILL, payFeeHeader.getBillNo(), finalLogMemo);
                    });
                }
            });
        }
    }

    /**
     * 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);
    }
}
