package com.izouma.jiashanxia.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.izouma.jiashanxia.domain.GoodsInfo;
import com.izouma.jiashanxia.domain.UserPackage;
import com.izouma.jiashanxia.domain.UserPackageFlow;
import com.izouma.jiashanxia.dto.*;
import com.izouma.jiashanxia.enums.FlowType;
import com.izouma.jiashanxia.exception.BusinessException;
import com.izouma.jiashanxia.repo.GoodsInfoRepo;
import com.izouma.jiashanxia.repo.UserPackageFlowRepo;
import com.izouma.jiashanxia.repo.UserPackageRepo;
import com.izouma.jiashanxia.utils.JpaUtils;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class UserPackageFlowService {

    private UserPackageFlowRepo userPackageFlowRepo;
    private GoodsInfoRepo       goodsInfoRepo;
    private UserPackageRepo     userPackageRepo;

    public Page<UserPackageFlow> all(PageQuery pageQuery) {
        pageQuery.setSort("createdAt,desc");
        return userPackageFlowRepo
                .findAll(JpaUtils.toSpecification(pageQuery, UserPackageFlow.class), JpaUtils.toPageRequest(pageQuery));
    }

    /*
    核销列表
     */
    public Page<UserPackageFlow> writeOffAll(PageQuery pageQuery) {
        pageQuery.setSort("createdAt,desc");
        return userPackageFlowRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils
                    .toPredicates(pageQuery, UserPackageFlow.class, root, criteriaQuery, criteriaBuilder);
            and.add(criteriaBuilder.equal(root.get("type"), FlowType.WRITE_OFF));
            if (StrUtil.isNotEmpty(pageQuery.getSearch())) {
                List<Predicate> or = new ArrayList<>();
                or.add(and.get(and.size() - 1));
                and.remove(and.get(and.size() - 1));
                or.add(criteriaBuilder.like(root.join("user").get("nickname"), "%" + pageQuery.getSearch() + "%"));
                or.add(criteriaBuilder.like(root.join("writeOffUser")
                        .get("nickname"), "%" + pageQuery.getSearch() + "%"));
                and.add(criteriaBuilder.or(or.toArray(new Predicate[0])));
            }
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }), JpaUtils.toPageRequest(pageQuery));
    }

    public Page<UserPackageFlowVO> writeOffAll1(PageQuery pageQuery) {
        pageQuery.setSort("createdAt,desc");
        Map<Long, String> goodsMap = goodsInfoRepo.findAll()
                .stream()
                .collect(Collectors.toMap(GoodsInfo::getId, GoodsInfo::getName));
        return userPackageFlowRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils
                    .toPredicates(pageQuery, UserPackageFlow.class, root, criteriaQuery, criteriaBuilder);
            and.add(criteriaBuilder.equal(root.get("type"), FlowType.WRITE_OFF));
            if (StrUtil.isNotEmpty(pageQuery.getSearch())) {
                List<Predicate> or = new ArrayList<>();
                or.add(and.get(and.size() - 1));
                and.remove(and.get(and.size() - 1));
                or.add(criteriaBuilder.like(root.join("user").get("nickname"), "%" + pageQuery.getSearch() + "%"));
                or.add(criteriaBuilder.like(root.join("writeOffUser")
                        .get("nickname"), "%" + pageQuery.getSearch() + "%"));
                and.add(criteriaBuilder.or(or.toArray(new Predicate[0])));
            }
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }), JpaUtils.toPageRequest(pageQuery)).map(flow -> {
            List<GoodsVO> goodsVOS = this.toGoodsVO(flow.getContent(), goodsMap);
            UserPackageFlowVO vo = new UserPackageFlowVO();
            BeanUtil.copyProperties(flow, vo);
            vo.setNickname(flow.getUser().getNickname());
            vo.setAvatar(flow.getUser().getAvatar());
            vo.setContent(goodsVOS);
            return vo;
        });
    }

    public List<UserPackageFlowVO> my(Long userId) {
        List<UserPackageFlowVO> vos = new ArrayList<>();
        // 查找用户相关
        List<UserPackageFlow> flows = userPackageFlowRepo.findAllByUserIdOrderByCreatedAtDesc(userId);
        Map<Long, String> goodsMap = goodsInfoRepo.findAll()
                .stream()
                .collect(Collectors.toMap(GoodsInfo::getId, GoodsInfo::getName));
        // 转vo
        flows.forEach(flow -> {
            List<GoodsVO> goodsVOS = this.toGoodsVO(flow.getContent(), goodsMap);
            UserPackageFlowVO vo = new UserPackageFlowVO();
            BeanUtil.copyProperties(flow, vo);
            vo.setContent(goodsVOS);
            vos.add(vo);
        });
        return vos;

    }

    public List<GoodsVO> toGoodsVO(String content, Map<Long, String> goodsMap) {
        List<GoodsDTO> goodsDTOS = JSONObject.parseArray(content, GoodsDTO.class);
        List<GoodsVO> goodsVOS = new ArrayList<>();
        goodsDTOS.forEach(goodsDTO ->
                goodsVOS.add(GoodsVO.builder()
                        .name(goodsMap.get(goodsDTO.getGoodsInfoId()))
                        .num(goodsDTO.getNum())
                        .build())
        );
        return goodsVOS;
    }

    /*
    核销
    */
    public UserPackageFlow writeOff(WriteOffSaveVO writeOffSaveVO) {
        if (writeOffSaveVO.getUserId().equals(writeOffSaveVO.getWriteOffUserId())) {
            throw new BusinessException("不可自己核销自己");
        }

        // 用户已有套餐
        Map<Long, UserPackage> userPackageMap = userPackageRepo.findAllByUserId(writeOffSaveVO.getUserId())
                .stream()
                .collect(Collectors.toMap(UserPackage::getGoodsInfoId, userPackage -> userPackage));

        List<GoodsDTO> goodsDTOS = JSONObject.parseArray(writeOffSaveVO.getContent(), GoodsDTO.class);
        goodsDTOS.forEach(goodsDTO -> {
            UserPackage userPackage = userPackageMap.get(goodsDTO.getGoodsInfoId());
            if (ObjectUtil.isEmpty(userPackage)) {
                throw new BusinessException("无此项目");
            }
            if (userPackage.getNum() <= 0) {
                throw new BusinessException("无余额");
            }
            if (goodsDTO.getNum() > userPackage.getNum()) {
                throw new BusinessException("核销数量大于套餐数量");
            }
            userPackage.setNum(userPackage.getNum() - goodsDTO.getNum());
            userPackageRepo.save(userPackage);
            goodsDTO.setNum(-goodsDTO.getNum());
        });

        // 保存套餐流水
        String content = JSONObject.toJSONString(goodsDTOS);
        return userPackageFlowRepo.save(
                UserPackageFlow.builder()
                        .userId(writeOffSaveVO.getUserId())
                        .content(content)
                        .type(FlowType.WRITE_OFF)
                        .writeOffUserId(writeOffSaveVO.getWriteOffUserId())
                        .build());
    }

    public WriteOffRecordDTO getDTO(Long id) {
        UserPackageFlow writeOffRecord = userPackageFlowRepo.findById(id).orElseThrow(new BusinessException("无核销记录"));
        // 转vo
        List<GoodsDTO> goodsDTOS = JSONObject.parseArray(writeOffRecord.getContent(), GoodsDTO.class);
        Set<Long> ids = goodsDTOS.stream().map(GoodsDTO::getGoodsInfoId).collect(Collectors.toSet());
        Map<Long, String> goodsMap = goodsInfoRepo.findAllById(ids)
                .stream()
                .collect(Collectors.toMap(GoodsInfo::getId, GoodsInfo::getName));
        List<GoodsVO> goodsVOS = this.toGoodsVO(writeOffRecord.getContent(), goodsMap);

        WriteOffRecordDTO build = WriteOffRecordDTO.builder()
                .id(writeOffRecord.getId())
                .createdAt(writeOffRecord.getCreatedAt())
                .goods(goodsVOS)
                .build();
        if (writeOffRecord.getUser() != null) {
            build.setNickname(writeOffRecord.getUser().getNickname());
        }
        if (writeOffRecord.getWriteOffUser() != null) {
            build.setWriteOffNickname(writeOffRecord.getWriteOffUser().getNickname());
        }
        return build;
    }
}
