package com.zbkj.service.service.bcx;

import cn.hutool.core.collection.CollUtil;
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.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zbkj.common.exception.CrmebException;
import com.zbkj.common.model.bcx.BcxFapiaoApply;
import com.zbkj.common.model.bcx.BcxFapiaoSource;
import com.zbkj.common.page.CommonPage;
import com.zbkj.common.request.PageParamRequest;
import com.zbkj.common.request.bcx.*;
import com.zbkj.common.response.bcx.BcxFapiaoApplyDetailResponse;
import com.zbkj.common.response.bcx.BcxFapiaoApplyResponse;
import com.zbkj.common.response.bcx.BcxFapiaoLastSendToResponse;
import com.zbkj.common.response.bcx.BcxSummaryInfo;
import com.zbkj.common.utils.CrmebDateUtil;
import com.zbkj.common.utils.CrmebUtil;
import com.zbkj.common.utils.DateUtils;
import com.zbkj.common.vo.DateLimitUtilVo;
import com.zbkj.service.dao.bcx.BcxFapiaoApplyDao;
import com.zbkj.service.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 发票申请单 服务实现类
 */
@Service
public class BcxFapiaoApplyServiceImpl extends ServiceImpl<BcxFapiaoApplyDao, BcxFapiaoApply> implements BcxFapiaoApplyService {

    @Resource
    private BcxFapiaoApplyDao dao;
    @Resource
    private UserService userService;
    @Resource
    private BcxFapiaoSourceService bcxFapiaoSourceService;
    @Resource
    private Validator validator;
    @Resource
    private TransactionTemplate transactionTemplate;

    @Override
    public BcxFapiaoApplyResponse queryById(Integer id) {
        BcxFapiaoApply source = dao.selectById(id);
        BcxFapiaoApplyResponse target = transform(source);
        return target;
    }

    /**
     * 开票方 查询发票申请单列表
     */
    @Override
    public PageInfo<BcxFapiaoApplyResponse> queryWriterPagedList(BcxFapiaoApplyRequest request,
                                                                 PageParamRequest pageParamRequest) {
        LambdaQueryWrapper<BcxFapiaoApply> lqw = new LambdaQueryWrapper<>();
        Integer userId = userService.getUserIdException();
        lqw.eq(BcxFapiaoApply::getWriterId, userId);
        return queryPagedList(request, pageParamRequest, lqw);
    }

    /**
     * 收票方 查询发票申请单列表
     */
    @Override
    public PageInfo<BcxFapiaoApplyResponse> queryReceiverPagedList(BcxFapiaoApplyRequest request, PageParamRequest pageParamRequest) {
        LambdaQueryWrapper<BcxFapiaoApply> lqw = new LambdaQueryWrapper<>();
        Integer userId = userService.getUserIdException();
        lqw.eq(BcxFapiaoApply::getReceiverId, userId);
        return queryPagedList(request, pageParamRequest, lqw);
    }

    private PageInfo<BcxFapiaoApplyResponse> queryPagedList(BcxFapiaoApplyRequest request, PageParamRequest pageParamRequest, LambdaQueryWrapper<BcxFapiaoApply> lqw) {
        concatQueryParam(lqw, request);
        Page<BcxFapiaoApply> page = PageHelper.startPage(pageParamRequest.getPage(), pageParamRequest.getLimit());
        List<BcxFapiaoApply> sources = dao.selectList(lqw);
        if (CollUtil.isEmpty(sources)) {
            return CommonPage.copyPageInfo(page, CollUtil.newArrayList());
        }
        List<BcxFapiaoApplyResponse> result = transform(sources);
        return CommonPage.copyPageInfo(page, result);
    }

    /**
     * 构造query wrapper，这里面不能写select，会覆盖上层的select
     */
    private LambdaQueryWrapper<BcxFapiaoApply> concatQueryParam(LambdaQueryWrapper<BcxFapiaoApply> lqw, BcxFapiaoApplyRequest request) {
        lqw.orderByDesc(BcxFapiaoApply::getId);
        if (StrUtil.isNotEmpty(request.getFapiaoNo())) {
            lqw.like(BcxFapiaoApply::getFapiaoNo, request.getFapiaoNo());
        }
        if (StrUtil.isNotEmpty(request.getApplyNo())) {
            lqw.like(BcxFapiaoApply::getApplyNo, request.getApplyNo());
        }
        if (StrUtil.isNotEmpty(request.getReceiver())) {
            lqw.like(BcxFapiaoApply::getReceiver, request.getReceiver());
        }
        if (StrUtil.isNotEmpty(request.getWriter())) {
            lqw.like(BcxFapiaoApply::getWriter, request.getWriter());
        }
        if (ObjectUtil.isNotNull(request.getFapiaoStatus())) {
            lqw.eq(BcxFapiaoApply::getFapiaoStatus, request.getFapiaoStatus());
        }
        if (ObjectUtil.isNotNull(request.getSourceType())) {
            lqw.eq(BcxFapiaoApply::getSourceType, request.getSourceType());
        }
        if (StrUtil.isNotEmpty(request.getFapiaoTime())) {
            DateLimitUtilVo fapiaoTime = CrmebDateUtil.getDateLimit(request.getFapiaoTime());
            lqw.ge(BcxFapiaoApply::getWriteTime, fapiaoTime.getStartTime());
            lqw.le(BcxFapiaoApply::getWriteTime, fapiaoTime.getEndTime());
        }
        if (StrUtil.isNotEmpty(request.getApplyTime())) {
            DateLimitUtilVo applyTime = CrmebDateUtil.getDateLimit(request.getApplyTime());
            lqw.ge(BcxFapiaoApply::getApplyTime, applyTime.getStartTime());
            lqw.le(BcxFapiaoApply::getApplyTime, applyTime.getEndTime());
        }
        return lqw;
    }

    @Override
    public BcxFapiaoApply create(BcxFapiaoApplyCreateRequest request) {
        Integer userId = userService.getUserIdException();
        validateTitle(request);
        if (request.getApplyId() != null) {
            BcxFapiaoApply apply = getById(request.getApplyId());
            if (apply == null || !userId.equals(apply.getReceiverId())){
                throw new CrmebException("找不到申请单："+request.getApplyId());
            }
            BeanUtils.copyProperties(request, apply);
            apply.setFapiaoStatus(2);
            updateById(apply);
            return apply;
        }
        String sourceIds = request.getSourceIds();
        if (StrUtil.isBlank(sourceIds)){
            throw new CrmebException("源单id不能为空");
        }
        List<String> ids = Arrays.asList(sourceIds.split(","));
        List<Integer> idList = ids.stream().map(Integer::parseInt).collect(Collectors.toList());
        Map<Integer, BcxFapiaoSource> sourceMap = bcxFapiaoSourceService.getMapBySourceIdList(idList);
        if (sourceMap.size() != ids.size()) {
            throw new CrmebException("源单不存在" + sourceIds);
        }
        Collection<BcxFapiaoSource> sources = sourceMap.values();
        List<Integer> writers = sources.stream().map(BcxFapiaoSource::getWriterId).distinct().collect(Collectors.toList());
        if (writers.size() > 1) {
            throw new CrmebException("开票方不同，不能合并");
        }
        List<Integer> receiverIds = sources.stream().map(BcxFapiaoSource::getReceiverId).distinct().collect(Collectors.toList());
        if (receiverIds.size() > 1) {
            throw new CrmebException("收票方不同，不能合并");
        }
        if (!userId.equals(receiverIds.get(0))) {
            throw new CrmebException("非法请求");
        }
        List<Integer> bizTypes = sources.stream().map(BcxFapiaoSource::getCategoryId).distinct().collect(Collectors.toList());
        if (bizTypes.size() > 1) {
            throw new CrmebException("商品分类不同，不能合并");
        }
        List<Integer> sourceTypes = sources.stream().map(BcxFapiaoSource::getSourceType).distinct().collect(Collectors.toList());
        if (sourceTypes.size() > 1) {
            throw new CrmebException("费用类型不同，不能合并");
        }
        if (sources.stream().anyMatch(s -> StrUtil.isNotBlank(s.getApplyNo()))) {
            throw new CrmebException("有单据已申请，不能重复申请开票");
        }

        BcxFapiaoApply apply = new BcxFapiaoApply();
        BeanUtils.copyProperties(request, apply);
        apply.setApplyNo(CrmebUtil.getOrderNo("FP"));
        apply.setApplyTime(new Date());
        apply.setFapiaoAmount(sources.stream().map(BcxFapiaoSource::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
        apply.setFapiaoStatus(2);
        apply.setWriterId(writers.get(0));
        apply.setWriter(sources.stream().findFirst().get().getWriter());
        apply.setReceiverId(receiverIds.get(0));
        apply.setReceiver(sources.stream().findFirst().get().getReceiver());
        apply.setSourceType(sourceTypes.get(0));
        sources.forEach(s -> {
            s.setApplyNo(apply.getApplyNo());
        });
        if (transactionTemplate.execute(transactionStatus -> {
            bcxFapiaoSourceService.updateBatchById(sources);
            return save(apply);
        })) {
            return apply;
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean writeFapiao(BcxWriteFapiaoRequest request) {
        Integer userId = userService.getUserIdException();
        String applyNo = request.getApplyNo();
        LambdaQueryWrapper<BcxFapiaoApply> lqw = new LambdaQueryWrapper<>();
        lqw.eq(BcxFapiaoApply::getApplyNo, applyNo);
        List<BcxFapiaoApply> fapiaoList = dao.selectList(lqw);
        if (fapiaoList.size() < 1) {
            throw new CrmebException("不是有效的开票申请单" + applyNo);
        }
        if (fapiaoList.stream().anyMatch(f -> f.getFapiaoStatus() == 1 || !Objects.equals(f.getWriterId(), userId))) {
            throw new CrmebException("必须是都未开票的本开票方的申请单才能开票");
        }
        fapiaoList.forEach(f -> {
            f.setFapiaoNo(request.getFapiaoNo());
            f.setFapiaoStatus(1);
            f.setWriteTime(DateUtils.parseDate(request.getWriteTime()));
            f.setVendorId(request.getVendorId());
            f.setVendorName(request.getVendorName());
            f.setFapiaoOrg(request.getFapiaoOrg());
            if (StrUtil.isNotEmpty(request.getRejectReason())) {
                //f.setFapiaoStatus(3);
                f.setRejectReason(request.getRejectReason());
            }
        });
        updateBatchById(fapiaoList);
        return true;
    }

    @Override
    public BcxFapiaoApplyDetailResponse queryByApplyNo(String applyNo) {
        LambdaQueryWrapper<BcxFapiaoApply> lqw = new LambdaQueryWrapper<>();
        lqw.eq(BcxFapiaoApply::getApplyNo, applyNo);
        List<BcxFapiaoApply> fapiaoList = dao.selectList(lqw);
        if (fapiaoList.size() < 1) {
            throw new CrmebException("不是有效的开票申请单" + applyNo);
        }
        BcxFapiaoApplyDetailResponse response = new BcxFapiaoApplyDetailResponse();
        BeanUtils.copyProperties(fapiaoList.get(0), response);

        List<BcxFapiaoSource> sources = bcxFapiaoSourceService.queryByApplyNo(applyNo);
        response.setSources(sources);
        return response;
    }

    @Override
    public boolean rejectById(BcxFapiaoApplyRejectRequest request) {
        LambdaUpdateWrapper<BcxFapiaoApply> lqw = new LambdaUpdateWrapper<>();
        lqw.set(BcxFapiaoApply::getFapiaoStatus, 3);
        lqw.set(BcxFapiaoApply::getRejectReason, request.getRejectReason());
        lqw.eq(BcxFapiaoApply::getId, request.getId());
        return update(lqw);
    }

    @Override
    public BcxSummaryInfo getWriterSummaryInfo(BcxFapiaoApplyRequest request) {
        QueryWrapper<BcxFapiaoApply> qw = new QueryWrapper<>();
        qw.select("count(1) as totalCount, sum(fapiao_amount) as totalPrice");
        LambdaQueryWrapper<BcxFapiaoApply> lqw = concatQueryParam(qw.lambda(), request);
        Integer userId = userService.getUserIdException();
        lqw.eq(BcxFapiaoApply::getWriterId, userId);
        List<Map<String, Object>> maps = dao.selectMaps(lqw);
        Map<String, Object> map = maps.get(0);
        if (map == null) {
            return new BcxSummaryInfo(0, BigDecimal.ZERO);
        }
        Object totalCount = map.get("totalCount");
        Object totalPrice = map.get("totalPrice");
        return new BcxSummaryInfo(totalCount == null ? 0 : Integer.valueOf(totalCount.toString()),
                new BigDecimal(totalPrice == null ? "0" : totalPrice.toString()));
    }

    @Override
    public BcxSummaryInfo getReceiverSummaryInfo(BcxFapiaoApplyRequest request) {
        QueryWrapper<BcxFapiaoApply> qw = new QueryWrapper<>();
        qw.select("count(1) as totalCount, sum(fapiao_amount) as totalPrice");
        LambdaQueryWrapper<BcxFapiaoApply> lqw = concatQueryParam(qw.lambda(), request);
        Integer userId = userService.getUserIdException();
        lqw.eq(BcxFapiaoApply::getReceiverId, userId);
        List<Map<String, Object>> maps = dao.selectMaps(lqw);
        Map<String, Object> map = maps.get(0);
        if (map == null) {
            return new BcxSummaryInfo(0, BigDecimal.ZERO);
        }
        Object totalCount = map.get("totalCount");
        Object totalPrice = map.get("totalPrice");
        return new BcxSummaryInfo(totalCount == null ? 0 : Integer.valueOf(totalCount.toString()),
                new BigDecimal(totalPrice == null ? "0" : totalPrice.toString()));
    }

    @Override
    public BcxFapiaoLastSendToResponse lastSendTo() {
        Integer userId = userService.getUserIdException();
        LambdaQueryWrapper<BcxFapiaoApply> lqw = new LambdaQueryWrapper<>();
        lqw.eq(BcxFapiaoApply::getReceiverId, userId);
        lqw.orderByDesc(BcxFapiaoApply::getUpdateTime);
        lqw.last("limit 1");
        BcxFapiaoApply last = dao.selectOne(lqw);
        BcxFapiaoLastSendToResponse response = new BcxFapiaoLastSendToResponse();
        if (last != null) {
            BeanUtils.copyProperties(last, response);
        }
        return response;
    }

    public void validateTitle(BcxFapiaoApplyCreateRequest request) {
        if (request.getTitleType() == 0 && request.getFapiaoType() == 1) {
            throw new CrmebException("个人只能开增值税普通发票");
        }
        if (request.getTitleType() == 1) {//企业
            if (request.getFapiaoType() == 0) {//普通
                validator.validate(request, BcxFapiaoTitleRequest.CompanyPlainGroup.class);
            } else {
                validator.validate(request, BcxFapiaoTitleRequest.CompanyDedicatedGroup.class);
            }
        }
    }

    private List<BcxFapiaoApplyResponse> transform(List<BcxFapiaoApply> sources) {
        List<BcxFapiaoApplyResponse> result = new ArrayList<>();
        for (BcxFapiaoApply source : sources) {
            BcxFapiaoApplyResponse target = transform(source);
            result.add(target);
        }
        return result;
    }

    private BcxFapiaoApplyResponse transform(BcxFapiaoApply source) {
        BcxFapiaoApplyResponse target = new BcxFapiaoApplyResponse();
        BeanUtils.copyProperties(source, target);
        // set another perperties
        return target;
    }
}
