package com.neusoft.smart.pos.ota.service;

import com.neusoft.smart.pos.dto.ota.request.UpgradeStrategyGroupRequest;
import com.neusoft.smart.pos.dto.ota.request.UpgradeStrategyQueryRequest;
import com.neusoft.smart.pos.dto.ota.response.UpgradeStrategyGroupResponse;
import com.neusoft.smart.pos.dto.ota.response.UpgradeStrategyResponse;
import com.neusoft.smart.pos.framework.task.TaskExecutor;
import com.neusoft.smart.pos.framework.utils.BeanUtil;
import com.neusoft.smart.pos.ota.persistent.domain.UpgradePackage;
import com.neusoft.smart.pos.ota.persistent.domain.UpgradeStrategy;
import com.neusoft.smart.pos.ota.persistent.domain.UpgradeStrategyGroup;
import com.neusoft.smart.pos.ota.persistent.ext.domain.UpgradePackageQuery;
import com.neusoft.smart.pos.ota.persistent.ext.domain.UpgradeStrategyQuery;
import com.neusoft.smart.pos.ota.persistent.repository.UpgradePackageRepository;
import com.neusoft.smart.pos.ota.persistent.repository.UpgradeStrategyRepository;
import com.neusoft.smart.pos.ota.task.DeviceInitStrategyTask;
import com.neusoft.smart.pos.ota.task.DeviceStrategyTask;
import com.neusoft.smart.pos.ota.task.StrategyCancelTask;
import com.neusoft.smart.pos.redis.bean.DeviceInfo;
import com.neusoft.smart.pos.redis.bean.DeviceVersion;
import com.neusoft.smart.pos.redis.bean.DeviceVersionList;
import com.neusoft.smart.pos.utils.RequestHeaderUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class UpgradeStrategyService {
    
    @Autowired
    private UpgradeStrategyRepository upgradeStrategyRepository;
    
    @Autowired
    private UpgradePackageRepository upgradePackageRepository;
    
    @Autowired
    private TaskExecutor taskExecutor;
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    
    /**
     * @param upgradeStrategyGroupRequest
     */
    @Transactional
    public void create(UpgradeStrategyGroupRequest upgradeStrategyGroupRequest) {
        UpgradeStrategyGroup strategyGroup = new UpgradeStrategyGroup();
        BeanUtils.copyProperties(upgradeStrategyGroupRequest, strategyGroup);
        strategyGroup.setCreatedBy(RequestHeaderUtil.getUserId());
        strategyGroup.setCreatedDate(new Date());
        strategyGroup.setOrganizationId(RequestHeaderUtil.getOrgId());
        upgradeStrategyRepository.createGroup(strategyGroup);
        
        //为了适配熙康PDA的增量包需求,现在需要改造成策略选择哪个更新包,放到设备的更新集合中,不需要补全升级序列
        UpgradePackage curUpgradePackage = upgradePackageRepository.get(upgradeStrategyGroupRequest.getPackageId());
        List<UpgradePackage> prevVersionList = new ArrayList<>();
        
        if (!curUpgradePackage.getFileType().equals(2)) {
            //把此组织的所有更新包都查询出来
            UpgradePackageQuery packageQuery = new UpgradePackageQuery();
            packageQuery.setOrganizationId(RequestHeaderUtil.getOrgId());
            packageQuery.setStatusArr(new Integer[]{0});
            packageQuery.setIsPage(false);
            packageQuery.setFileTypes(Arrays.asList(0, 1));
            List<UpgradePackage> upgradePackageList = upgradePackageRepository.queryList(packageQuery);
            Integer curVersion = Integer.parseInt(curUpgradePackage.getVersionCode().substring(curUpgradePackage.getVersionCode().lastIndexOf(".") + 1));
            //把小于当前更新包版本的数据过滤出来
            prevVersionList = upgradePackageList.stream().filter(upgradePackage -> {
                Integer version = Integer.parseInt(upgradePackage.getVersionCode().substring(upgradePackage.getVersionCode().lastIndexOf(".") + 1));
                return version <= curVersion;
            }).collect(Collectors.toList());
        }
        
        List<UpgradePackage> finalPrevVersionList = prevVersionList;
        upgradeStrategyGroupRequest.getDevices().forEach(device -> {
            //查询出所有设备的策略
            UpgradeStrategyQuery strategyQuery = new UpgradeStrategyQuery();
            strategyQuery.setDeviceId(device.getDeviceId());
            strategyQuery.setStatus(0);
            strategyQuery.setIsPage(false);
            List<UpgradeStrategy> allStrategy = upgradeStrategyRepository.queryList(strategyQuery);
            //跟所有的以前的版本对比,过滤出差异版本
            List<UpgradePackage> differVersion = new ArrayList<>();
            //非熙康PDA过滤差异版本
            if (!curUpgradePackage.getFileType().equals(2)) {
                differVersion = finalPrevVersionList.stream().filter(prevVersionPackage ->
                        //所有策略中查询不到的就放到差异集合中
                        allStrategy.stream().noneMatch(aStrategy -> prevVersionPackage.getId().equals(aStrategy.getPackageId()))
                ).sorted(Comparator.comparingInt(x -> //重新排序一下
                        Integer.parseInt(x.getVersionCode().substring(x.getVersionCode().lastIndexOf(".") + 1)))).collect(Collectors.toList());
            } else {
                //熙康PDA只需要把选择的版本放进去
                differVersion.add(curUpgradePackage);
            }
            
            //处理缓存数据的任务,此处定义在上,共用下边的differ循环
            DeviceStrategyTask strategyTask = new DeviceStrategyTask();
            strategyTask.setMacAddr(device.getMacAddr());
            strategyTask.setSerialNo(device.getSerialNo());
            strategyTask.setCurVersion(curUpgradePackage.getVersionCode());
            
            //把差异集合插入数据库中,包含此次创建的策略
            differVersion.forEach(differ -> {
                UpgradeStrategy strategy = new UpgradeStrategy();
                strategy.setGroupId(strategyGroup.getId());
                strategy.setDeviceId(device.getDeviceId());
                strategy.setPackageId(differ.getId());
                upgradeStrategyRepository.create(strategy);
                
                //给task赋值
                DeviceVersion deviceVersion = new DeviceVersion();
                BeanUtils.copyProperties(differ, deviceVersion);
                deviceVersion.setFileSize(differ.getFileSize().longValue());
                deviceVersion.setUpdateBeginTime(upgradeStrategyGroupRequest.getUpgradeStartTime().getTime());
                strategyTask.getVersionList().addLast(deviceVersion);
                taskExecutor.getExecutorService().submit(strategyTask);
            });
            
        });
    }
    
    /**
     * @param upgradeStrategyQueryRequest
     * @return
     */
    public List<UpgradeStrategyGroupResponse> getGroupList(UpgradeStrategyQueryRequest upgradeStrategyQueryRequest) {
        List<UpgradeStrategyGroupResponse> result = new ArrayList<>();
        List<UpgradeStrategyGroup> poList = upgradeStrategyRepository.queryGroupList(BeanUtil.parse(upgradeStrategyQueryRequest, UpgradeStrategyQuery.class));
        if (!poList.isEmpty()) {
            UpgradeStrategyQuery upgradeStrategyQuery = new UpgradeStrategyQuery();
            upgradeStrategyQuery.setGroupIds(poList.stream().map(UpgradeStrategyGroup::getId).collect(Collectors.toList()));
            upgradeStrategyQuery.setIsPage(false);
            List<UpgradeStrategy> list = upgradeStrategyRepository.queryList(upgradeStrategyQuery);
            Map<Integer, List<UpgradeStrategy>> countMap = list.stream().collect(Collectors.groupingBy(UpgradeStrategy::getGroupId, Collectors.toList()));
            poList.forEach(po -> {
                UpgradeStrategyGroupResponse response = new UpgradeStrategyGroupResponse();
                BeanUtils.copyProperties(po, response);
                List<UpgradeStrategy> strategies = countMap.get(po.getId());
                Long count = 0l;
                if (null != strategies) {
                    count = strategies.stream().map(UpgradeStrategy::getDeviceId).count();
                }
                response.setStrategyCount(count);
                result.add(response);
            });
        }
        return result;
    }
    
    /**
     * @param upgradeStrategyQuery
     * @return
     */
    public Integer queryGroupCount(UpgradeStrategyQueryRequest upgradeStrategyQuery) {
        return upgradeStrategyRepository.queryGroupCount(BeanUtil.parse(upgradeStrategyQuery, UpgradeStrategyQuery.class));
    }
    
    /**
     * @param groupId
     * @return
     */
    public List<UpgradeStrategyResponse> queryList(Integer groupId) {
        List<UpgradeStrategyResponse> strategyResponseList = new ArrayList<>();
        UpgradeStrategyQuery upgradeStrategyQuery = new UpgradeStrategyQuery();
        upgradeStrategyQuery.setGroupId(groupId);
        upgradeStrategyQuery.setIsPage(false);
        List<UpgradeStrategy> result = upgradeStrategyRepository.queryList(upgradeStrategyQuery);
        result.forEach(po -> {
            UpgradeStrategyResponse upgradeStrategyResponse = new UpgradeStrategyResponse();
            BeanUtils.copyProperties(po, upgradeStrategyResponse);
            strategyResponseList.add(upgradeStrategyResponse);
        });
        return strategyResponseList;
    }
    
    
    /**
     * 策略取消
     *
     * @param groupId
     */
    @Transactional
    public void cancel(Integer groupId) {
        //更新数据库
        UpgradeStrategyGroup strategyGroup = new UpgradeStrategyGroup();
        strategyGroup.setId(groupId);
        strategyGroup.setStatus(1);
        upgradeStrategyRepository.updateGroup(strategyGroup);
        UpgradeStrategy upgradeStrategy = new UpgradeStrategy();
        upgradeStrategy.setStatus(1);
        upgradeStrategyRepository.updateByExampleAndId(groupId, upgradeStrategy);
        //查询此策略全部机具版本关系
        UpgradeStrategyQuery upgradeStrategyQuery = new UpgradeStrategyQuery();
        upgradeStrategyQuery.setGroupId(groupId);
        upgradeStrategyQuery.setIsPage(false);
        List<UpgradeStrategy> upgradeStrategyList = upgradeStrategyRepository.queryList(upgradeStrategyQuery);
        //执行异步任务
        StrategyCancelTask strategyCancelTask = new StrategyCancelTask();
        strategyCancelTask.setUpgradeStrategyList(upgradeStrategyList);
        taskExecutor.getExecutorService().submit(strategyCancelTask);
    }
    
    @Transactional
    public void setUpgradeStatus(List<Pair<Integer, Integer>> ids) {
        ids.forEach(pair ->
                upgradeStrategyRepository.updateStatus(pair.getFirst(), pair.getSecond())
        );
    }
    
    
    /**
     *
     */
    public void loadData2Redis_old() {
        BoundHashOperations<String, String, DeviceVersionList> deviceHash = redisTemplate.boundHashOps("deviceVersionsMap");
        List<DeviceInfo> list = deviceHash.entries().values().stream().map(DeviceVersionList::getDeviceInfo).collect(Collectors.toList());
        UpgradePackageQuery packageQuery = new UpgradePackageQuery();
//					packageQuery.setOrderBySubversion(true);
        packageQuery.setStatusArr(new Integer[]{0});
        packageQuery.setIsPage(false);
        List<UpgradePackage> upgradePackageList = upgradePackageRepository.queryList(packageQuery);
        list.forEach(aDevice -> {
                    UpgradeStrategyQuery strategyQuery = new UpgradeStrategyQuery();
                    strategyQuery.setDeviceId(aDevice.getId());
                    strategyQuery.setStatus(0);
                    strategyQuery.setIsPage(false);
                    List<UpgradeStrategy> allStrategy = upgradeStrategyRepository.queryList(strategyQuery);
                    
                    DeviceStrategyTask deviceStrategyTask = new DeviceStrategyTask();
                    deviceStrategyTask.setMacAddr(aDevice.getMacAddr());
                    deviceStrategyTask.setSerialNo(aDevice.getSerialNo());
                    
                    LinkedList<DeviceVersion> deviceVersionList = new LinkedList<>();
                    if (!upgradePackageList.isEmpty()) {
                        allStrategy.forEach(a -> {
                            UpgradePackage temp = upgradePackageList.stream().filter(up -> up.getId().equals(a.getPackageId())).findAny().orElse(null);
                            if (null != temp) {
                                DeviceVersion deviceVersion = new DeviceVersion();
                                BeanUtils.copyProperties(temp, deviceVersion);
                                deviceVersion.setFileSize(temp.getFileSize().longValue());
                                deviceVersion.setUpdateBeginTime(upgradeStrategyRepository.getGroup(a.getGroupId()).getUpgradeStartTime().getTime());
                                deviceVersionList.add(deviceVersion);
                            }
                            
                        });
                        if (!deviceVersionList.isEmpty()) {
                            deviceVersionList.stream().sorted((x, y) ->
                                    Integer.parseInt(y.getVersionCode().substring(y.getVersionCode().lastIndexOf(".") + 1)) -
                                            Integer.parseInt(x.getVersionCode().substring(x.getVersionCode().lastIndexOf(".") + 1))
                            );
                            deviceStrategyTask.setVersionList(deviceVersionList);
                            taskExecutor.getExecutorService().submit(deviceStrategyTask);
                        }
                    }
                }
        );
    }

    /**
     *
     */
    public void loadData2Redis() {
        System.out.println("loadData2Redis in");
        BoundHashOperations<String, String, DeviceVersionList> deviceHash = redisTemplate.boundHashOps("deviceVersionsMap");
        System.out.println("loadData2Redis in 2");
        List<DeviceInfo> deviceList = deviceHash.entries().values().stream().map(DeviceVersionList::getDeviceInfo).collect(Collectors.toList());

        List<UpgradePackage> upgradePackageList = queryUpdatePackageList();

        DeviceInitStrategyTask initTask = new DeviceInitStrategyTask();
        Map<String, DeviceVersionList> devicesInitMap = initTask.getDeviceHashMap();
        devicesInitMap.putAll(deviceHash.entries());

        deviceList.forEach(deviceInfo -> {
                    List<UpgradeStrategy> allStrategy = queryUpgradeStrategyList(deviceInfo.getId());
                    DeviceVersionList versionList = devicesInitMap.get(deviceInfo.getMacAddr());
                    if(versionList == null) {
                        versionList = new DeviceVersionList();
                        devicesInitMap.put(deviceInfo.getMacAddr(), versionList);
                    }

                    LinkedHashMap<String, DeviceVersion> versionsMap = versionList.getVersionsMap();

                    if (!upgradePackageList.isEmpty()) {
                        allStrategy.forEach(a -> {
                            UpgradePackage temp = upgradePackageList.stream().filter(up -> up.getId().equals(a.getPackageId())).findAny().orElse(null);
                            if (null != temp) {
                                DeviceVersion deviceVersion = new DeviceVersion();
                                BeanUtils.copyProperties(temp, deviceVersion);
                                deviceVersion.setFileSize(temp.getFileSize().longValue());
                                deviceVersion.setUpdateBeginTime(upgradeStrategyRepository.getGroup(a.getGroupId()).getUpgradeStartTime().getTime());
                                versionsMap.put(temp.getVersionCode(), deviceVersion);
                            }
                        });
                    }
                });
        taskExecutor.getExecutorService().submit(initTask);
        System.out.println("loadData2Redis out");
    }

    private List<UpgradePackage> queryUpdatePackageList() {
        UpgradePackageQuery packageQuery = new UpgradePackageQuery();

        packageQuery.setStatusArr(new Integer[]{0});
        packageQuery.setIsPage(false);
        return upgradePackageRepository.queryList(packageQuery);
    }

    private List<UpgradeStrategy> queryUpgradeStrategyList(Integer deviceId) {
        UpgradeStrategyQuery strategyQuery = new UpgradeStrategyQuery();
        strategyQuery.setDeviceId(deviceId);
        strategyQuery.setStatus(0);
        strategyQuery.setIsPage(false);
        return upgradeStrategyRepository.queryList(strategyQuery);
    }

}
