package com.tykj.dev.device.train.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.github.wenhao.jpa.PredicateBuilder;
import com.github.wenhao.jpa.Specifications;
import com.tykj.dev.blockcha.subject.entity.BcHash;
import com.tykj.dev.blockcha.subject.service.BlockChainUtil;
import com.tykj.dev.config.TaskBeanConfig;
import com.tykj.dev.device.file.service.FilesUtil;
import com.tykj.dev.device.task.service.TaskService;
import com.tykj.dev.device.task.subject.bto.TaskBto;
import com.tykj.dev.device.train.dao.TrainThemeDao;
import com.tykj.dev.device.train.dao.TrainUnitDao;
import com.tykj.dev.device.train.dao.TrainUserDao;
import com.tykj.dev.device.train.entity.GenerateTestPaper;
import com.tykj.dev.device.train.entity.TrainTheme;
import com.tykj.dev.device.train.entity.TrainUnit;
import com.tykj.dev.device.train.entity.TrainUser;
import com.tykj.dev.device.train.entity.vo.*;
import com.tykj.dev.device.train.service.TrainTaskService;
import com.tykj.dev.device.train.service.TrainThemeService;
import com.tykj.dev.device.user.cache.UnitsCache;
import com.tykj.dev.device.user.subject.entity.Units;
import com.tykj.dev.device.user.subject.entity.User;
import com.tykj.dev.device.user.subject.service.MgrcertService;
import com.tykj.dev.device.user.subject.service.UserService;
import com.tykj.dev.misc.base.BeanHelper;
import com.tykj.dev.misc.base.BusinessEnum;
import com.tykj.dev.misc.base.StatusEnum;
import com.tykj.dev.misc.exception.ApiException;
import com.tykj.dev.misc.utils.JacksonUtil;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zjm
 * @version 1.0.0
 * @ClassName TrainServiceImpl.java
 * @Description 培训逻辑层
 * @createTime 2020年08月13日 15:25:00
 */
@Service
@Slf4j
public class TrainThemeServiceImpl implements TrainThemeService {

    @Autowired
    TrainThemeDao trainThemeDao;

    @Autowired
    MgrcertService mgrcertService;

    @Autowired
    UserService userService;
    @Autowired
    TrainTaskService trainTaskService;

    @Autowired
    TrainUserDao trainUserDao;

    @Autowired
    BlockChainUtil blockChainUtil;

    @Autowired
    TaskService taskService;

    @Autowired
    UnitsCache unitsCache;

    @Autowired
    TrainUnitDao trainUnitDao;
    @Override
    public TrainTheme save(TrainTheme trainTheme) {
        TrainTheme trainTheme1 = trainThemeDao.save(trainTheme);
        CompletableFuture.runAsync(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            TrainTheme trainTheme2 = findById(trainTheme1.getTrainId());
            BcHash bcText = blockChainUtil.sendHash(1000, JacksonUtil.toJSon(trainTheme2));
            String recordId = bcText.getData().getRecordID();
            trainTheme2.setRecordId(recordId);
            trainThemeDao.save(trainTheme2);
        }, TaskBeanConfig.getThreadPoolTaskScheduler());
        return trainTheme1;
    }

    /**
     * @param trainThemes 异步上链
     */
    @Override
    @Async
    public void sendHash(List<TrainTheme> trainThemes) {
        trainThemes.forEach(trainTheme1 -> {
            BcHash bcHash=blockChainUtil.sendHash(1000,JacksonUtil.toJSon(trainTheme1));
            trainTheme1.setRecordId(bcHash.getData().getRecordID());
            trainThemeDao.save(trainTheme1);
        });
    }

    @Override
    public TrainTheme findById(Integer trainThemeId) {
        Optional<TrainTheme> train = trainThemeDao.findById(trainThemeId);
        if (train.isPresent()){
            TrainTheme trainTheme=train.get();
            trainTheme.setTrainDataFileList(FilesUtil.stringFileToList(trainTheme.getTrainDataFiles()));
            trainTheme.setTrainPapersFileList(FilesUtil.stringFileToList(trainTheme.getTrainPapersFiles()));
            return trainTheme;
        }else {
          throw new ApiException(ResponseEntity.status(500).body("[培训] 查询详情id："+trainThemeId+"没有找到"));
        }

    }

    @Override
    public List<TrainTheme> findAllStatus(Integer status) {
        return trainThemeDao.findAllByTrainStatus(status);
    }

    @Override
    public ByTrainingPeople findByTrainingPeople(Integer trainThemeId, Integer userId) {
        TrainTheme trainTheme = trainThemeDao.findById(trainThemeId).get();
        ByTrainingPeople byTrainingPeople = trainTheme.toByTrainingPeople();
        TrainUser trainUser=trainUserDao.findByUserIdAndTrainId(userId,trainThemeId);
        List<TrainUser> trainUsers=trainUserDao.findAllByUnitsIdAndTrainId(trainUser.getUnitsId(),trainThemeId);
        Units units= unitsCache.findById(trainUser.getUnitsId());
        Map<Integer,User> map = userService.findAllByUnite(units.getUnitId()).stream().collect(Collectors.toMap(User::getUserId,Function.identity()));
        Set<Integer> ids=map.keySet();
        ids.removeAll(trainUsers.stream().map(TrainUser::getUserId).collect(Collectors.toList()));
        if (ids.size()>0){
            ids.forEach(i->{
                TrainUser trainUser1= userToTrainUser(map.get(i));
                trainUser1.setIsSignUp(2);
                trainUser1.setIsNotJoinUserId(1);
                trainUser1.setTrainId(trainTheme.getTrainId());
                trainUser1= saveTrainUser(trainUser1);
                trainUsers.add(trainUser1);
            });

        }
        trainUsers.forEach(
                trainUser1 -> trainUser1.setMgrcert(mgrcertService.findByUserId(trainUser1.getUserId()))
        );

        byTrainingPeople.setCoursewareFileList(JacksonUtil.readValue(trainTheme.getCoursewareFiles(), new TypeReference<List<CoursewareFile>>() {}));
        byTrainingPeople.setGenerateTestPaper(JacksonUtil.readValue(trainTheme.getGenerateTestPaperString(), GenerateTestPaper.class));
        byTrainingPeople.setTrainDataFileList(FilesUtil.stringFileToList(trainTheme.getTrainDataFiles()));
        byTrainingPeople.setTrainUserList(trainUsers);
        return byTrainingPeople;
    }

    @Override
    public ByTrainingPeopleCheck findByTrainingPeopleCheck(Integer trainThemeId, Integer userId) {
        TrainTheme trainTheme = trainThemeDao.findById(trainThemeId).get();
        ByTrainingPeopleCheck byTrainingPeople = trainTheme.toByTrainingPeopleCheck();
        TrainUser trainUser=trainUserDao.findByUserIdAndTrainId(userId,trainThemeId);
        byTrainingPeople.setCoursewareFileList(JacksonUtil.readValue(trainTheme.getCoursewareFiles(), new TypeReference<List<CoursewareFile>>() {}));
        byTrainingPeople.setGenerateTestPaper(JacksonUtil.readValue(trainTheme.getGenerateTestPaperString(), GenerateTestPaper.class));
        byTrainingPeople.setTrainDataFileList(FilesUtil.stringFileToList(trainTheme.getTrainDataFiles()));
        byTrainingPeople.setTrainUser(trainUser);
        return byTrainingPeople;
    }


    @Override
    public TrainRegistrationVo findRegistrationVo(Integer trainThemeId) {
        TrainTheme trainTheme = trainThemeDao.findById(trainThemeId).get();
        TaskBto taskBto=trainTaskService.selectFatherIsNullAndBillidAndBillType(trainThemeId,BusinessEnum.TRAIN.id);
        TrainRegistrationVo trainRegistrationVo = trainTheme.toTrainRegistrationVo();
        List<TrainUser> trainUsers= trainUserDao.findAllByTrainId(trainThemeId);
        List<TrainUserToUnitVo> signUpTrainUserToUnitVo=new ArrayList<>();
        List<TrainUserToUnitVo> notSignUpTrainUserToUnitVo=new ArrayList<>();
        Map<Integer, TrainUnit> trainUnitMap=trainUnitDao.findAllByTrainId(trainThemeId).stream().collect(Collectors.toMap(TrainUnit::getUnitId, Function.identity()));
        AtomicBoolean flag= new AtomicBoolean(true);
        List<TaskBto> taskBtoList=trainTaskService.findAllByTypeAndBillId(taskBto.getId());
        taskBtoList.forEach(
                    taskBto1 -> {
                        TrainUnit trainUnit=trainUnitMap.get(taskBto1.getOwnUnit());
                        List<TrainUser> trainUsersNotSignUp = trainUsers.stream().filter(trainUser -> trainUser.getUnitsId().equals(taskBto1.getOwnUnit()) && trainUser.getIsSignUp() == 2).collect(Collectors.toList());
                        if (taskBto1.getBillStatus().equals(StatusEnum.TRAIN1002.id) && trainUnit.getSubmitStatus()==1){
                           flag.set(false);
                           List<TrainUser> trainUserList = trainUsers.stream().filter(trainUser -> trainUser.getUnitsId().equals(taskBto1.getOwnUnit())).collect(Collectors.toList());
                           notSignUpTrainUserToUnitVo.add(new TrainUserToUnitVo(taskBto1.getOwnUnit(), unitsCache.findById(taskBto1.getOwnUnit()).getName(), trainUserList, taskBto1.getId(),trainUnit.getSubmitStatus()));
                       }else {
                           List<TrainUser> trainUsersSignUp = trainUsers.stream().filter(trainUser -> trainUser.getUnitsId().equals(taskBto1.getOwnUnit()) && trainUser.getIsSignUp() == 1).collect(Collectors.toList());
                           if (trainUsersSignUp.size() != 0) {
                               signUpTrainUserToUnitVo.add(new TrainUserToUnitVo(taskBto1.getOwnUnit(),unitsCache.findById(taskBto1.getOwnUnit()).getName(), trainUsersSignUp, taskBto1.getId(),trainUnit.getSubmitStatus()));
                           }
                           notSignUpTrainUserToUnitVo.add(new TrainUserToUnitVo(taskBto1.getOwnUnit(), unitsCache.findById(taskBto1.getOwnUnit()).getName(), trainUsersNotSignUp, taskBto1.getId(),trainUnit.getSubmitStatus()));
                       }
                    }
            );
            trainRegistrationVo.setGenerateTestPaper(JacksonUtil.readValue(trainTheme.getGenerateTestPaperString(),GenerateTestPaper.class));
            trainRegistrationVo.setCoursewareFileList(JacksonUtil.readValue(trainTheme.getCoursewareFiles(), new TypeReference<List<CoursewareFile>>() {}));
            trainRegistrationVo.setIsEnd(flag.get());
            trainRegistrationVo.setNoSignUpTrainUserToUnitVo(notSignUpTrainUserToUnitVo);
            trainRegistrationVo.setSignUpTrainUserToUnitVo(signUpTrainUserToUnitVo);
            trainRegistrationVo.setTrainDataFileList(FilesUtil.stringFileToList(trainTheme.getTrainDataFiles()));
            return trainRegistrationVo;
    }

    @Override
    public TrainTrainingVo findTrainTrainingVo(Integer trainThemeId) {
        TrainTheme trainTheme = trainThemeDao.findById(trainThemeId).get();
        TrainTrainingVo trainTrainingVo = trainTheme.toTrainTrainingVo();
        trainTrainingVo.setTrainDataFileList(FilesUtil.stringFileToList(trainTheme.getTrainDataFiles()));
        trainTrainingVo.setTrainUsers(trainUserDao.findAllByIsSignUpAndTrainId(1,trainThemeId));
        trainTrainingVo.setCoursewareFileList(JacksonUtil.readValue(trainTheme.getCoursewareFiles(), new TypeReference<List<CoursewareFile>>() {
        }));
        trainTrainingVo.setGenerateTestPaper(JacksonUtil.readValue(trainTheme.getGenerateTestPaperString(), GenerateTestPaper.class));
        return trainTrainingVo;
    }

    @Override
    public TrainAuditVo findTrainAuditVo(Integer trainThemeId) {
        TrainTheme trainTheme = trainThemeDao.findById(trainThemeId).get();
        trainTheme.setTrainDataFileList(FilesUtil.stringFileToList(trainTheme.getTrainDataFiles()));
        TrainAuditVo trainAuditVo = trainTheme.toTrainAuditVo();
        List<TrainUser> trainUsers= trainUserDao.findAllByIsSignUpAndTrainId(1,trainThemeId);
        trainUsers.forEach(trainUser -> {
            trainUser.setMgrcert(mgrcertService.findByUserId(trainUser.getUserId()));
        });
        trainAuditVo.setTrainUser(trainUsers);
        return trainAuditVo;
    }

    @Override
    public TrainTheme updateTrain(TrainUpdateVo trainUpdateVo) {
        TrainTheme trainTheme = trainThemeDao.findById(trainUpdateVo.getTrainId()).get();
        if (trainUpdateVo.getEndTime() != null) {
            trainTheme.setEndTime(trainUpdateVo.getEndTime());
        }
        if (trainUpdateVo.getStartTime() != null) {
            trainTheme.setStartTime(trainUpdateVo.getStartTime());
        }
//        if (trainUpdateVo.getTrainDataFileList()!=null&&trainUpdateVo.getTrainDataFileList().size()!=0){
            trainTheme.setTrainDataFiles(FilesUtil.stringFileToList(trainUpdateVo.getTrainDataFileList()));
//        }
        if (trainUpdateVo.getTrainLocation() != null) {
            trainTheme.setTrainLocation(trainUpdateVo.getTrainLocation());
        }
        if (trainUpdateVo.getTrainSynopsis() != null) {
            trainTheme.setTrainSynopsis(trainUpdateVo.getTrainSynopsis());
        }
        TrainTheme trainTheme1 = trainThemeDao.save(trainTheme);
        CompletableFuture.runAsync(()->blockChainUtil.appendHash(JacksonUtil.toJSon(trainTheme1),trainTheme1.getRecordId()),TaskBeanConfig.getThreadPoolTaskScheduler());
        return trainTheme1;
    }

    @Override
    public TrainThemePage findTrainPageVo(ConditionsTrainVo conditionsTrainVo) {
        Pageable pageable = PageRequest.of(conditionsTrainVo.getPage(), conditionsTrainVo.getSize(), Sort.Direction.ASC, "trainTime");
        Page<TrainTheme> all = getContacts(conditionsTrainVo,pageable);

        List<TrainThemeVo> list = new ArrayList<>();
        all.getContent().forEach(
                trainTheme -> {
                    if (trainTheme!=null) {
                        TaskBto taskBto = trainTaskService.selectFatherIsNullAndBillidAndBillType(trainTheme.getTrainId(), BusinessEnum.TRAIN.id);
                        TrainThemeVo trainThemeVo = trainTheme.toTrainThemeVo();
                        trainThemeVo.setTaskId(taskBto.getId());
                        list.add(trainThemeVo);
                    }
                }
        );
        return TrainThemePage.builder()
                .data(list)
                .page(conditionsTrainVo.getPage())
                .size(conditionsTrainVo.getSize())
                .total((int) all.getTotalElements()).build();
    }

    @Override
    public TrainDetailsVo findSelectDetailsVo(Integer trainId) {
        Optional<TrainTheme> trainThemeOptional= trainThemeDao.findById(trainId);

        if (trainThemeOptional.isPresent()){
            TrainTheme trainTheme=trainThemeOptional.get();
            TrainDetailsVo trainDetailsVo=trainTheme.toTrainDetailsVo();
            List<TrainUser> trainUsers= trainUserDao.findAllByIsSignUpAndTrainId(1,trainId);
            trainUsers.forEach(trainUser -> {
                trainUser.setMgrcert(mgrcertService.findByUserId(trainUser.getUserId()));
            });
            trainDetailsVo.setCoursewareFileList(JacksonUtil.readValue(trainTheme.getCoursewareFiles(), new TypeReference<List<CoursewareFile>>() {
            }));
            trainDetailsVo.setGenerateTestPaper(JacksonUtil.readValue(trainTheme.getGenerateTestPaperString(), GenerateTestPaper.class));
            trainDetailsVo.setTrainDataFileList(FilesUtil.stringFileToList(trainTheme.getTrainDataFiles()));
            trainDetailsVo.setTrainPapersFileList(FilesUtil.stringFileToList(trainTheme.getTrainPapersFiles()));
            trainDetailsVo.setTrainUsers(trainUsers);
        return trainDetailsVo;
        }else {
          throw new ApiException(ResponseEntity.ok("培训id没有查询到，请检查"));
        }

    }

    @Override
    public TrainTheme findBytaskId(Integer taskId) {
        TaskBto taskBto = taskService.get(taskId);

        return findById(taskBto.getBillId());
    }

    @Override
    public List<TrainUser> findAllTrainUserByTrainId(Integer trainId) {

        return trainUserDao.findAllByTrainId(trainId).stream().filter(trainUser -> trainUser.getIsSignUp()==1).collect(Collectors.toList());
    }

    @Override
    public List<TrainTheme> findByUnitIdAndisNotExamines(Integer isNotExamines, Integer unitId) {
        return trainThemeDao.findAllByUnitsIdAndIsNotExamines(unitId,isNotExamines);
    }

    @Override
    public List<TrainTheme> findByUnitId(Integer unitId) {
        return trainThemeDao.findAllByUnitsId(unitId);
    }

    @Override
    public WaitExaminesVo findWaitExaminesVo(Integer trainId) {
        TrainTheme trainTheme = trainThemeDao.findById(trainId).get();
        WaitExaminesVo trainTrainingVo = trainTheme.toWaitExaminesVo();
        trainTrainingVo.setCoursewareFileList(JacksonUtil.readValue(trainTheme.getCoursewareFiles(), new TypeReference<List<CoursewareFile>>() {
        }));
        trainTrainingVo.setGenerateTestPaper(JacksonUtil.readValue(trainTheme.getGenerateTestPaperString(), GenerateTestPaper.class));
        return trainTrainingVo;
    }

    private Page<TrainTheme> getContacts(ConditionsTrainVo conditionsTrainVo, Pageable pageable) {
        PredicateBuilder<TrainTheme> predicateBuilder = Specifications.and();
        if (conditionsTrainVo.getEndTime() != null && conditionsTrainVo.getStartTime() != null) {
            predicateBuilder.gt("createTime", conditionsTrainVo.getStartTime());
            predicateBuilder.lt("createTime", conditionsTrainVo.getEndTime());
        }
        Units units= unitsCache.findById(conditionsTrainVo.getUnitId());
        if (units.getLevel()!=1){
            predicateBuilder.eq("unitsId",conditionsTrainVo.getUnitId());
        }
        if (conditionsTrainVo.getDimName() != null) {
            Class<TrainTheme> trainThemeClass = TrainTheme.class;
            Field[] declaredFields = trainThemeClass.getDeclaredFields();
            PredicateBuilder<TrainTheme> p = Specifications.or();
            for (Field field : declaredFields) {
                if (field.getType().equals(String.class)) {
                    p.like(field.getName(), "%" + conditionsTrainVo.getDimName() + "%");
                }
            }
            predicateBuilder.predicate(p.build());
        }
        return trainThemeDao.findAll(predicateBuilder.build(), pageable);
    }
    private TrainUser userToTrainUser(User user){
        ModelMapper mapper = BeanHelper.getUserMapper();
        return mapper.map(user,TrainUser.class);
    }

    private TrainUser saveTrainUser(TrainUser trainUser){
        TrainUser trainUser1 = trainUserDao.save(trainUser);
        //异步线程
        CompletableFuture.runAsync(()-> {
            BcHash bcHash = blockChainUtil.sendHash(1000, JacksonUtil.toJSon(trainUser1));
            trainUserDao.upDate(bcHash.getData().getRecordID(),trainUser1.getId());
        });
        return trainUser1;
    }
}
