package com.github.wxiaoqi.security.admin.biz;

import com.github.wxiaoqi.security.admin.dto.BaseUserMemberExportDTO;
import com.github.wxiaoqi.security.admin.dto.BaseUserMemberExportDataFindDTO;
import com.github.wxiaoqi.security.admin.dto.UserMemberDTO;
import com.github.wxiaoqi.security.admin.dto.UserMemberSaveDTO;
import com.github.wxiaoqi.security.admin.entity.BaseUserMemberExport;
import com.github.wxiaoqi.security.admin.entity.BaseUserMemberLevel;
import com.github.wxiaoqi.security.admin.mapper.BaseUserMemberExportMapper;
import com.github.wxiaoqi.security.admin.vo.BaseUserMemberExportVo;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.vo.PageDataVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.time.Instant;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author libin
 * @version 1.0
 * @description
 * @data 2019/7/8 16:19
 */
@Service
@Transactional
@Slf4j
public class BaseUserMemberExportBiz extends BaseBiz<BaseUserMemberExportMapper, BaseUserMemberExport> {

    @Autowired
    private UserMemberLevelBiz userMemberLevelBiz;

    @Autowired
    private BaseUserMemberBiz baseUserMemberBiz;

    @Autowired
    private AppUserLoginBiz appUserLoginBiz;

    @Autowired
    private  ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private final int BORDER_NUM=250;
    private BaseUserMemberExport baseUserMemberExport;


    public void saveUserMember(UserMemberSaveDTO userMemberSaveDTO, Integer userId, String name) {
        if (userMemberSaveDTO != null) {
            UserMemberDTO userMemberDTO = new UserMemberDTO();
            BaseUserMemberLevel level = userMemberLevelBiz.getLevel(userMemberSaveDTO.getMemberLevel());
            Map<String, Integer> phoneAndUserIdMap = appUserLoginBiz.findPhoneAndUserIdMapByPhones(Arrays.asList(userMemberSaveDTO.getPhone()));

            if (phoneAndUserIdMap != null) {
                userMemberDTO.setUserId(phoneAndUserIdMap.get(userMemberSaveDTO.getPhone()));
                userMemberDTO.setMemberLevel(userMemberSaveDTO.getMemberLevel());
                userMemberDTO.setDiscount(level == null ? 0 : (level.getDiscount() == null ? 0 : level.getDiscount()));
                userMemberDTO.setTotalNumber(userMemberSaveDTO.getTotalNumber());
                userMemberDTO.setRentFreeDays(userMemberSaveDTO.getRentFreeDays());
                userMemberDTO.setMemberName(userMemberSaveDTO.getMemberName());
                userMemberDTO.setIsBind(1);
                userMemberDTO.setBuyCount(0);
                try {
                    baseUserMemberBiz.updUserMemberByUserId(userMemberDTO);
                } catch (Exception e) {
                    log.error("会员更新或新增错误：【{}】", e.getMessage());
                    throw new BaseException(e);
                }
            }
            boolean hasUsed = phoneAndUserIdMap == null ? false : (phoneAndUserIdMap.get(userMemberSaveDTO.getPhone()) == null ? false : true);
            BaseUserMemberExport memberExport = BaseUserMemberExport
                    .builder()
                    .username(userMemberSaveDTO.getPhone())
                    .memberLevel(userMemberSaveDTO.getMemberLevel())
                    .totalNumber(userMemberSaveDTO.getTotalNumber())
                    .rentFreeDays(userMemberSaveDTO.getRentFreeDays())
                    .memberName(userMemberSaveDTO.getMemberName())
                    .discount(level == null ? 0 : level.getDiscount() == null ? 0 : level.getDiscount())
                    .status(hasUsed?1:0)
                    .userId(hasUsed?phoneAndUserIdMap.get(userMemberSaveDTO.getPhone()):null)
                    .source(0)
                    .crtId(userId)
                    .crtName(name)
                    .crtTime(Instant.now().toEpochMilli())
                    .verificationTime(hasUsed?Instant.now().toEpochMilli():0)
                    .isDel(0)
                    .build();
            if (log.isDebugEnabled()) {
                log.debug("当前组装的数据：【{}】", memberExport);
            }
            mapper.insertSelective(memberExport);
        }
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int importUserMember(List<String[]> userMemberData, Integer userId, String userName) {
        List<BaseUserMemberExport> baseUserMemberExports = new ArrayList<>();

        List<BaseUserMemberLevel> levesls = userMemberLevelBiz.getLevesls();
        Map<Integer, BaseUserMemberLevel> levelAndbaseUserMemberMap = levesls.parallelStream().collect(Collectors.toMap(BaseUserMemberLevel::getLevel, Function.identity()));
        Map<String,Integer> leavelNameAndLeaveMap = levelAndbaseUserMemberMap.values().parallelStream().collect(Collectors.toMap(BaseUserMemberLevel::getName, BaseUserMemberLevel::getLevel));

        List<String> phones = userMemberData.stream().map(x -> x[0]).distinct().collect(Collectors.toList());
        Map<String, Integer> phoneAndUserIdMap = appUserLoginBiz.findPhoneAndUserIdMapByPhones(phones);
        Set<Map.Entry<String, Integer>> leaveNameAndLeaveEntry = leavelNameAndLeaveMap.entrySet();
        Integer memberLevel =1;
        Integer discount = 0;
        AtomicInteger counter = new AtomicInteger(0);
        int threadNums= userMemberData.size()/BORDER_NUM==0?1:userMemberData.size()/BORDER_NUM;
        CountDownLatch latch = new CountDownLatch(threadNums);
        Map<Integer,UserMemberDTO> numAndUserMemberMap = new HashMap<>(userMemberData.size());
        for (int i=0;i<threadNums;i++){
            int startIndex = i*BORDER_NUM;
            int endIndex=i==(threadNums-1)?userMemberData.size():(i+1)*BORDER_NUM;
            List<String[]> subResultDate = userMemberData.subList(startIndex,endIndex);
            threadPoolTaskExecutor.execute(()->{
                wrapperData(subResultDate, userId, userName, baseUserMemberExports, levelAndbaseUserMemberMap, phoneAndUserIdMap, leaveNameAndLeaveEntry, memberLevel, discount, counter, numAndUserMemberMap);
                 latch.countDown();
                });
         }
        try {
            latch.await();
        } catch (InterruptedException e) {
            throw new BaseException("导入数据失败");
        }
        InsertBatch(baseUserMemberExports);

        Set<Map.Entry<Integer, UserMemberDTO>> entries = numAndUserMemberMap.entrySet();
        for (Map.Entry<Integer, UserMemberDTO> memberDTOEntry : entries) {
            try {
                baseUserMemberBiz.updUserMemberByUserId(memberDTOEntry.getValue());
            }catch (Exception ex){
                log.error("当前行数：【{}】",memberDTOEntry.getKey());
                log.error("会员更新错误：【{}】", ex.getMessage());
                throw new BaseException("");
            }
        }
        return baseUserMemberExports.size();
    }

    private void wrapperData(List<String[]> userMemberData, Integer userId, String userName,
                             List<BaseUserMemberExport> baseUserMemberExports, Map<Integer, BaseUserMemberLevel> levelAndbaseUserMemberMap,
                             Map<String, Integer> phoneAndUserIdMap, Set<Map.Entry<String, Integer>> leaveNameAndLeaveEntry,
                             Integer memberLevel, Integer discount, AtomicInteger counter,Map<Integer,UserMemberDTO> numAndUserMemberMap) {
        for (String[] data : userMemberData) {
            String phone = data[0];
            phone = StringUtils.hasText(phone)?phone.trim():"";
            String memberLevelName = data[1];
            String memberName  = data[2];
            String totalNumber = data[3];
            String rentFreeDays = data[4];

            for (Map.Entry<String, Integer> entry : leaveNameAndLeaveEntry) {
                if (entry.getKey().contains(memberLevelName.substring(0,1))){
                    memberLevel = entry.getValue();
                    discount = levelAndbaseUserMemberMap.get(memberLevel).getDiscount();
                    break;
                }
            }

            if (phoneAndUserIdMap != null && phoneAndUserIdMap.get(phone) != null) {
                UserMemberDTO userMemberDTO = new UserMemberDTO();
                userMemberDTO.setUserId(phoneAndUserIdMap.get(phone));
                userMemberDTO.setMemberLevel(memberLevel);
                userMemberDTO.setDiscount(discount);
                userMemberDTO.setTotalNumber(Integer.valueOf(totalNumber));
                userMemberDTO.setRentFreeDays(Integer.valueOf(rentFreeDays));
                userMemberDTO.setIsBind(1);
                userMemberDTO.setMemberName(memberName);
                userMemberDTO.setBuyCount(0);
                numAndUserMemberMap.put(counter.incrementAndGet(),userMemberDTO);
               /* try {
                    baseUserMemberBiz.updUserMemberByUserId(userMemberDTO);
                } catch (Exception e) {
                    log.error("当前行数：【{}】",counter.get());
                    log.error("会员更新错误：【{}】", e.getMessage());
                    throw new BaseException(e);
                }*/
            }
            boolean hasUsed = phoneAndUserIdMap == null ? false : (phoneAndUserIdMap.get(phone) == null ? false : true);
            BaseUserMemberExport memberExport = BaseUserMemberExport
                    .builder()
                    .username(phone.trim())
                    .memberLevel(memberLevel)
                    .totalNumber(Integer.valueOf(totalNumber))
                    .rentFreeDays(Integer.valueOf(rentFreeDays))
                    .discount(discount)
                    .status(hasUsed?1:0)
                    .userId(hasUsed?phoneAndUserIdMap.get(phone):null)
                    .crtId(userId)
                    .memberName(memberName)
                    .source(0)
                    .crtName(userName)
                    .crtTime(Instant.now().toEpochMilli())
                    .verificationTime(hasUsed?Instant.now().toEpochMilli():null)
                    .isDel(0)
                    .build();
            if (log.isDebugEnabled()) {
                log.debug("当前组装的数据：【{}】", memberExport);
            }
            baseUserMemberExports.add(memberExport);
        }
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void InsertBatch(List<BaseUserMemberExport> baseUserMemberExports) {
        for (BaseUserMemberExport userMemberExport : baseUserMemberExports) {
            mapper.insertSelective(userMemberExport);
        }
    }

    public List<BaseUserMemberExportDTO> findUserMemberExportDataByPhoneAndHashEffective(String phone) {
        List<BaseUserMemberExportDTO> baseUserMemberExportDTOS = new ArrayList<>();

        Example example = new Example(BaseUserMemberExport.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("username", phone);
        criteria.andEqualTo("status", 0);
        criteria.andEqualTo("isDel", 0);
        List<BaseUserMemberExport> baseUserMemberExports = mapper.selectByExample(example);

        if (CollectionUtils.isNotEmpty(baseUserMemberExports)) {
            BaseUserMemberExportDTO baseUserMemberExportDTO;
            for (BaseUserMemberExport userMemberExport : baseUserMemberExports) {
                baseUserMemberExportDTO = new BaseUserMemberExportDTO();
                BeanUtils.copyProperties(userMemberExport, baseUserMemberExportDTO);
                baseUserMemberExportDTOS.add(baseUserMemberExportDTO);
            }
        }
        return baseUserMemberExportDTOS;
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void updateUserMemberExportDataToLoseEfficacyByPhone(String phone,Integer userId) {
        mapper.updateUserMemberExportDataToLoseEfficacyByPhone(phone,userId,Instant.now().toEpochMilli());
    }

    public PageDataVO<BaseUserMemberExportVo> findUserMemberExportDataPage(BaseUserMemberExportDataFindDTO exportDataFindDTO) {

        PageDataVO<BaseUserMemberExportVo> baseUserMemberExportVoPageDataVO = new PageDataVO<>();
        PageDataVO<BaseUserMemberExportDTO> baseUserMemberExportDataDTOS = PageDataVO.pageInfo(exportDataFindDTO.getPage(), exportDataFindDTO.getLimit(), () -> mapper.findExportDataPage(exportDataFindDTO));
        List<BaseUserMemberExportDTO> userMemberExportDTOS = baseUserMemberExportDataDTOS.getData();
        if (CollectionUtils.isEmpty(userMemberExportDTOS)) {
            return baseUserMemberExportVoPageDataVO;
        }
        List<BaseUserMemberExportVo> baseUserMemberExportVos = new ArrayList<>();
        BaseUserMemberExportVo baseUserMemberExportVo;
        for (BaseUserMemberExportDTO userMemberExportDTO : userMemberExportDTOS) {
            baseUserMemberExportVo = new BaseUserMemberExportVo();
            BeanUtils.copyProperties(userMemberExportDTO, baseUserMemberExportVo);
            baseUserMemberExportVos.add(baseUserMemberExportVo);
        }
        baseUserMemberExportVos.sort(Comparator.comparing(BaseUserMemberExportVo::getCrtTime).reversed());
        baseUserMemberExportVoPageDataVO.setData(baseUserMemberExportVos);
        baseUserMemberExportVoPageDataVO.setPageNum(baseUserMemberExportDataDTOS.getPageNum());
        baseUserMemberExportVoPageDataVO.setPageSize(baseUserMemberExportDataDTOS.getPageSize());
        baseUserMemberExportVoPageDataVO.setTotalCount(baseUserMemberExportDataDTOS.getTotalCount());
        baseUserMemberExportVoPageDataVO.setTotalPage(baseUserMemberExportDataDTOS.getTotalPage());
        return baseUserMemberExportVoPageDataVO;
    }

    public void updateUserMemberExportDataStatus(Integer id) {
        mapper.updateUserMemberExportDataStatusById(id, 1);
    }

    @Override
    public void insertSelective(BaseUserMemberExport baseUserMemberExport){
        mapper.insertSelective(baseUserMemberExport);
    }
}
