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

import com.github.wenhao.jpa.PredicateBuilder;
import com.github.wenhao.jpa.Specifications;
import com.tykj.dev.device.library.repository.DeviceLibraryDao;
import com.tykj.dev.device.library.service.DeviceLibraryService;
import com.tykj.dev.device.library.subject.domin.DeviceLibrary;
import com.tykj.dev.device.library.subject.vo.DeviceLibrarySelectVo;
import com.tykj.dev.device.library.subject.vo.DeviceStatisticsVo;
import com.tykj.dev.device.user.subject.service.UserPublicService;
import com.tykj.dev.device.user.util.UserUtils;
import com.tykj.dev.misc.exception.ApiException;
import com.tykj.dev.misc.utils.GetTreeUtils;
import com.tykj.dev.misc.utils.PageUtil;
import com.tykj.dev.misc.utils.ResultUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.Column;
import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author dengdiyi
 */
@Service
public class DeviceLibraryServiceImpl implements DeviceLibraryService {

    @Autowired
    private DeviceLibraryDao deviceLibraryDao;

    @Autowired
    UserUtils userUtils;

    @Autowired
    private UserPublicService userPublicService;

    @Override
    public DeviceLibrary addEntity(DeviceLibrary deviceLibraryEntity) {
        return deviceLibraryDao.save(deviceLibraryEntity);
    }

    @Override
    public Page<DeviceLibrary> getPage(DeviceLibrarySelectVo deviceLibrarySelectVo, Pageable pageable) {
        Specification<DeviceLibrary> selectSpecification = getSelectSpecification(deviceLibrarySelectVo);
        return deviceLibraryDao.findAll(selectSpecification, pageable);
    }

    @Override
    public Page<DeviceLibrary> getTagPage(DeviceLibrarySelectVo deviceLibrarySelectVo, Pageable pageable) {
        return deviceLibraryDao.findAll(getSelectSpecification4(deviceLibrarySelectVo),deviceLibrarySelectVo.getPageable());
    }

    @Override
    public Page<DeviceLibrary> getCoreDevicePage(DeviceLibrarySelectVo deviceLibrarySelectVo, Pageable pageable) {
        Integer selectUnitId = deviceLibrarySelectVo.getUnitId();
        Integer selectAreaId = deviceLibrarySelectVo.getAreaId();
        //areaId为null
        if (selectAreaId==null) {
            List<DeviceLibrary> libraryEntities;
            //unitId为null,按照父子结构查询返回当前单位的所有装备
            if (selectUnitId==null) {
                libraryEntities = getList2(deviceLibrarySelectVo);
            }
            //unitId不为null,按照父子结构查询该单位的所有装备
            else {
                String unitName = userPublicService.findByUnitsToname(selectUnitId);
                libraryEntities = getList3(deviceLibrarySelectVo,unitName);
            }
            //按照子装备跟在父装备后面排列返回
            List<DeviceLibrary> resultList = new ArrayList<>();
            for (DeviceLibrary d:libraryEntities) {
                resultList.add(d);
                if (d.getChilds().size()>0){
                    resultList.addAll(d.getChilds());
                }
            }
            return PageUtil.getPerPage(deviceLibrarySelectVo.getPage(),deviceLibrarySelectVo.getSize(),resultList,pageable);
        }
        //areId不为空，查询某个区域下的所有单位的所有装备
        else {
            //查询区域等级
            Integer selectLevel = userPublicService.getArea(selectAreaId).getType();
            //查询该区域下的所有单位
            List<String> units = userPublicService.findAllUnitNameByAreaId(selectAreaId);
            //市或县筛选出装备所属单位在units列表中的装备
            if (selectLevel==3||selectLevel==2){
                List<DeviceLibrary> libraryEntities = deviceLibraryDao.findAll().stream()
                        .filter(deviceLibraryEntity -> units.contains(deviceLibraryEntity.getOwnUnit()))
                        .collect(Collectors.toList());
                List<DeviceLibrary> deviceLibraries = getList4(libraryEntities);
                List<DeviceLibrary> resultList = new ArrayList<>();
                for (DeviceLibrary d:deviceLibraries) {
                    resultList.add(d);
                    if (d.getChilds().size()>0){
                        resultList.addAll(d.getChilds());
                    }
                }
                return PageUtil.getPerPage(deviceLibrarySelectVo.getPage(),deviceLibrarySelectVo.getSize(),resultList,pageable);
            }
            //省能看到所有装备
            if (selectLevel==1){
                List<DeviceLibrary> libraryEntities = getList4(deviceLibraryDao.findAll());
                List<DeviceLibrary> resultList = new ArrayList<>();
                for (DeviceLibrary d:libraryEntities) {
                    resultList.add(d);
                    if (d.getChilds().size()>0){
                        resultList.addAll(d.getChilds());
                    }
                }
                return PageUtil.getPerPage(deviceLibrarySelectVo.getPage(),deviceLibrarySelectVo.getSize(),resultList,pageable);
            }
            else{
                throw new ApiException(ResultUtil.failed("区域等级只能为1，2，3"));
            }
        }
    }

    @Override
    public Page<DeviceStatisticsVo> getDeviceStatisticsPage(DeviceLibrarySelectVo deviceLibrarySelectVo, Pageable pageable) {
        //获取存在的所有装备型号
        List<String> list = new ArrayList<>();
        if (deviceLibrarySelectVo.getModel()!=null){
            list.add(deviceLibrarySelectVo.getModel());
        }
        else {
            list = getAllModel();
        }
        //设置最大size的装备查询vo
        List<DeviceStatisticsVo> deviceStatisticsVos = new ArrayList<>();
        DeviceLibrarySelectVo d = new DeviceLibrarySelectVo();
        BeanUtils.copyProperties(deviceLibrarySelectVo,d);
        d.setSize(Integer.MAX_VALUE);
        //获取所有的核心装备
        List<DeviceLibrary> libraryEntities = getCoreDevicePage(d,d.getPageable()).getContent();
        if (libraryEntities.size()>0) {
            //按型号遍历统计各种状态的装备数量
            for (String model : list) {
                DeviceStatisticsVo deviceStatisticsVo = new DeviceStatisticsVo();
                deviceStatisticsVo.setModel(model);
                int num = 0;
                int inLibraryNum = 0;
                int repairNum = 0;
                int destoryNum = 0;
                int scrappedNum = 0;
                int allotNum = 0;
                int retiredNum = 0;
                int useNum = 0;
                //遍历核心装备，按照相同型号的装备生命状态统计
                for (DeviceLibrary d2 : libraryEntities) {
                    if (d2.getModel().equals(model)) {
                        deviceStatisticsVo.setName(d2.getName());
                        num++;
                        switch (d2.getLifeStatus()) {
                            case 2:
                                inLibraryNum++;
                                break;
                            case 3:
                                allotNum++;
                                break;
                            case 4:
                                repairNum++;
                                break;
                            case 5:
                                scrappedNum++;
                                break;
                            case 6:
                                allotNum++;
                                break;
                            case 12:
                                retiredNum++;
                                break;
                            case 9:
                                destoryNum++;
                                break;
                            case 10:
                                destoryNum++;
                                break;
                            case 13:
                                scrappedNum++;
                                break;
                            case 14:
                                useNum++;
                                break;
                            default:
                                break;
                        }
                    }
                }
                //添加统计vo
                if (num > 0) {
                    deviceStatisticsVo.setDeviceNumber(num);
                    deviceStatisticsVo.setAllotNum(allotNum);
                    deviceStatisticsVo.setInLibraryNum(inLibraryNum);
                    deviceStatisticsVo.setRepairNum(repairNum);
                    deviceStatisticsVo.setDestoryNum(destoryNum);
                    deviceStatisticsVo.setScrappedNum(scrappedNum);
                    deviceStatisticsVo.setRetiredNum(retiredNum);
                    deviceStatisticsVo.setUseNum(useNum);
                    deviceStatisticsVos.add(deviceStatisticsVo);
                }
            }
            if (deviceLibrarySelectVo.getName()!=null){
                deviceStatisticsVos = selectByName(deviceLibrarySelectVo.getName(),deviceStatisticsVos);
            }
            return PageUtil.getPerPage(deviceLibrarySelectVo.getPage(),deviceLibrarySelectVo.getSize(),deviceStatisticsVos,pageable);
        }
        return new PageImpl<>(deviceStatisticsVos, pageable, deviceStatisticsVos.size());
    }

    @Override
    public List<String> getAllName() {
        List<DeviceLibrary> list = deviceLibraryDao.findAll();
        //用Set存装备名称，排除重复名称
        Set<String> s = new HashSet<>();
        for (DeviceLibrary d:list) {
            s.add(d.getName());
        }
        return new ArrayList<>(s);
    }

    @Override
    public List<DeviceLibrary> getList(DeviceLibrarySelectVo deviceLibrarySelectVo) {
        List<DeviceLibrary> deviceLibraryEntities = deviceLibraryDao.findAll(getSelectSpecification(deviceLibrarySelectVo));
        //返回父子结构
        Map<Integer, DeviceLibrary> nodeCollect =
                deviceLibraryEntities.stream().collect(Collectors.toMap(DeviceLibrary::getId, deviceLibraryEntity -> deviceLibraryEntity));
        return GetTreeUtils.parseTreeFromDown(
                deviceLibraryEntities,
                DeviceLibrary::getId,
                deviceLibraryEntity -> Optional.ofNullable(nodeCollect.get(deviceLibraryEntity.getPartParentId())),
                DeviceLibrary::addChildNode
        );
    }

    public List<DeviceLibrary> getList2(DeviceLibrarySelectVo deviceLibrarySelectVo) {
        List<DeviceLibrary> deviceLibraryEntities = deviceLibraryDao.findAll(getSelectSpecification6(deviceLibrarySelectVo));
        //返回父子结构
        Map<Integer, DeviceLibrary> nodeCollect =
                deviceLibraryEntities.stream().collect(Collectors.toMap(DeviceLibrary::getId, deviceLibraryEntity -> deviceLibraryEntity));
        return GetTreeUtils.parseTreeFromDown(
                deviceLibraryEntities,
                DeviceLibrary::getId,
                deviceLibraryEntity -> Optional.ofNullable(nodeCollect.get(deviceLibraryEntity.getPartParentId())),
                DeviceLibrary::addChildNode
        );
    }

    public List<DeviceLibrary> getList3(DeviceLibrarySelectVo deviceLibrarySelectVo,String name) {
        List<DeviceLibrary> deviceLibraryEntities = deviceLibraryDao.findAll(getSelectSpecification2(deviceLibrarySelectVo,name));
        //返回父子结构
        Map<Integer, DeviceLibrary> nodeCollect =
                deviceLibraryEntities.stream().collect(Collectors.toMap(DeviceLibrary::getId, deviceLibraryEntity -> deviceLibraryEntity));
        return GetTreeUtils.parseTreeFromDown(
                deviceLibraryEntities,
                DeviceLibrary::getId,
                deviceLibraryEntity -> Optional.ofNullable(nodeCollect.get(deviceLibraryEntity.getPartParentId())),
                DeviceLibrary::addChildNode
        );
    }

    public List<DeviceLibrary> getList4(List<DeviceLibrary> deviceLibraryEntities) {
        //以父子结构返回
        Map<Integer, DeviceLibrary> nodeCollect =
                deviceLibraryEntities.stream().collect(Collectors.toMap(DeviceLibrary::getId, deviceLibraryEntity -> deviceLibraryEntity));
        return GetTreeUtils.parseTreeFromDown(
                deviceLibraryEntities,
                DeviceLibrary::getId,
                deviceLibraryEntity -> Optional.ofNullable(nodeCollect.get(deviceLibraryEntity.getPartParentId())),
                DeviceLibrary::addChildNode
        );
    }

    @Override
    public List<DeviceLibrary> getListWithoutParent(DeviceLibrarySelectVo deviceLibrarySelectVo) {
        return deviceLibraryDao.findAll(getSelectSpecification(deviceLibrarySelectVo));
    }

    @Override
    public List<String> getAllUnit() {
        List<DeviceLibrary> list = deviceLibraryDao.findAll();
        //用set存储所属单位，排除重复单位
        Set<String> s = new HashSet<>();
        for (DeviceLibrary d:list) {
            s.add(d.getOwnUnit());
        }
        return new ArrayList<>(s);
    }

    @Override
    public List<DeviceLibrary> getAllList(DeviceLibrarySelectVo deviceLibrarySelectVo) {
        return deviceLibraryDao.findAll(getSelectSpecification4(deviceLibrarySelectVo));
    }

    @Override
    public List<DeviceLibrary> getCheckList() {
        PredicateBuilder<DeviceLibrary> predicateBuilder = Specifications.and();
        predicateBuilder.eq("ownUnit",userUtils.getCurrentUserUnitName());
        predicateBuilder.eq("lifeStatus",2);
        return deviceLibraryDao.findAll(predicateBuilder.build());
    }

    @Override
    public List<DeviceLibrary> getAllotList(DeviceLibrarySelectVo deviceLibrarySelectVo) {
        List<DeviceLibrary> deviceLibraryEntities = deviceLibraryDao.findAll(getAllotSelectSpecification(deviceLibrarySelectVo));
        Map<Integer, DeviceLibrary> nodeCollect =
                deviceLibraryEntities.stream().collect(Collectors.toMap(DeviceLibrary::getId, deviceLibraryEntity -> deviceLibraryEntity));
        return GetTreeUtils.parseTreeFromDown(
                deviceLibraryEntities,
                DeviceLibrary::getId,
                deviceLibraryEntity -> Optional.ofNullable(nodeCollect.get(deviceLibraryEntity.getPartParentId())),
                DeviceLibrary::addChildNode
        );
    }

    @Override
    public List<DeviceLibrary> getListByBillId(Integer id) {
        PredicateBuilder<DeviceLibrary> predicateBuilder = Specifications.and();
        predicateBuilder.eq("storageBillId",id);
        return deviceLibraryDao.findAll(predicateBuilder.build());
    }


    @Override
    public DeviceLibrary update(DeviceLibrary deviceLibraryEntity) {
        return deviceLibraryDao.save(deviceLibraryEntity);
    }

    @Override
    public DeviceLibrary getOne(Integer id) {
        Optional<DeviceLibrary> deviceLibraryEntity = deviceLibraryDao.findById(id);
        return deviceLibraryEntity.orElse(null);
    }

    @Override
    public void delete(Integer id) {
        deviceLibraryDao.deleteById(id);
    }

    private Specification<DeviceLibrary> getSelectSpecification(DeviceLibrarySelectVo deviceLibrarySelectVo){
        PredicateBuilder<DeviceLibrary> predicateBuilder = getPredicateBuilder(deviceLibrarySelectVo);
        //unitId为空，默认查询当前单位
        if (deviceLibrarySelectVo.getUnitId()==null){
            String unit = userUtils.getCurrentUserUnitName();
            predicateBuilder.eq("ownUnit",unit);
        }
        else {
            String unit = userPublicService.findByUnitsToname(deviceLibrarySelectVo.getUnitId());
            predicateBuilder.eq("ownUnit",unit);
        }
        return predicateBuilder.build();
    }

    private Specification<DeviceLibrary> getSelectSpecification2(DeviceLibrarySelectVo deviceLibrarySelectVo,String name){
        PredicateBuilder<DeviceLibrary> predicateBuilder = getPredicateBuilder(deviceLibrarySelectVo);
        predicateBuilder.eq("ownUnit",name);
        return predicateBuilder.build();
    }

    private Specification<DeviceLibrary> getSelectSpecification4(DeviceLibrarySelectVo deviceLibrarySelectVo){
        PredicateBuilder<DeviceLibrary> predicateBuilder = getPredicateBuilder(deviceLibrarySelectVo);
        return predicateBuilder.build();
    }

    private Specification<DeviceLibrary> getSelectSpecification6(DeviceLibrarySelectVo deviceLibrarySelectVo){
        PredicateBuilder<DeviceLibrary> predicateBuilder = getPredicateBuilder(deviceLibrarySelectVo);
        if (deviceLibrarySelectVo.getUnitId()==null){
            String unit = userUtils.getCurrentUserUnitName();
            predicateBuilder.eq("ownUnit",unit);
        }
        return predicateBuilder.build();
    }

    private Specification<DeviceLibrary> getAllotSelectSpecification(DeviceLibrarySelectVo deviceLibrarySelectVo){
        PredicateBuilder<DeviceLibrary> predicateBuilder = getPredicateBuilder(deviceLibrarySelectVo);
        String unit = userUtils.getCurrentUserUnitName();
        predicateBuilder.eq("ownUnit",unit);
        predicateBuilder.eq("lifeStatus",2);
        return predicateBuilder.build();
    }

    private PredicateBuilder<DeviceLibrary> getPredicateBuilder(DeviceLibrarySelectVo deviceLibrarySelectVo){
        PredicateBuilder<DeviceLibrary> predicateBuilder = Specifications.and();
        if (deviceLibrarySelectVo!=null) {
            if (deviceLibrarySelectVo.getModel()!=null){
                predicateBuilder.eq("model",deviceLibrarySelectVo.getModel());
            }
            if (deviceLibrarySelectVo.getName()!=null){
                predicateBuilder.eq("name",deviceLibrarySelectVo.getName());
            }
            if (deviceLibrarySelectVo.getSecretLevel()!=null){
                predicateBuilder.eq("secretLevel",deviceLibrarySelectVo.getSecretLevel());
            }
            if (deviceLibrarySelectVo.getMatchingRange()!=null){
                predicateBuilder.eq("matchingRange",deviceLibrarySelectVo.getMatchingRange());
            }
            if (deviceLibrarySelectVo.getStorageType()!=null){
                predicateBuilder.eq("storageType",deviceLibrarySelectVo.getStorageType());
            }
            if (deviceLibrarySelectVo.getManageStatus()!=null){
                predicateBuilder.eq("manageStatus",deviceLibrarySelectVo.getManageStatus());
            }
            if (deviceLibrarySelectVo.getLifeStatus()!=null){
                predicateBuilder.in("lifeStatus",deviceLibrarySelectVo.getLifeStatus().toArray(new Integer[]{}));
            }
            if (deviceLibrarySelectVo.getType()!=null){
                predicateBuilder.eq("type",deviceLibrarySelectVo.getType());
            }
            if (deviceLibrarySelectVo.getInvisibleRange()!=null){
                predicateBuilder.eq("invisibleRange",deviceLibrarySelectVo.getInvisibleRange());
            }
            if (deviceLibrarySelectVo.getInvisibleRange()!=null){
                predicateBuilder.eq("locationUnit",deviceLibrarySelectVo.getLocationUnit());
            }
            if (deviceLibrarySelectVo.getRfidCardId()!=null){
                predicateBuilder.eq("rfidCardId",deviceLibrarySelectVo.getRfidCardId());
            }

            if (deviceLibrarySelectVo.getContent() != null) {
                Class<DeviceLibrary> deviceLibraryEntity = DeviceLibrary.class;
                Field[] declaredFields = deviceLibraryEntity.getDeclaredFields();
                PredicateBuilder<DeviceLibrary> p = Specifications.or();
                for (Field field : declaredFields) {
                    if (field.getType().equals(String.class)&&field.getAnnotation(Transient.class)==null) {
                        p.like(field.getName(), "%" + deviceLibrarySelectVo.getContent() + "%");
                    }
                }
                predicateBuilder.predicate(p.build());
            }
            if (deviceLibrarySelectVo.getStartTime() != null) {
                predicateBuilder.gt("createTime", deviceLibrarySelectVo.getStartTime());
            }
            if (deviceLibrarySelectVo.getEndTime() != null) {
                predicateBuilder.lt("updateTime", deviceLibrarySelectVo.getEndTime());
            }
        }
        return predicateBuilder;
    }

    private List<String> getAllModel() {
        List<DeviceLibrary> list = deviceLibraryDao.findAll();
        Set<String> s = new HashSet<>();
        for (DeviceLibrary d:list) {
            s.add(d.getModel());
        }
        return new ArrayList<>(s);
    }

    private List<DeviceStatisticsVo> selectByName(String name,List<DeviceStatisticsVo> deviceStatisticsVos){
        return deviceStatisticsVos.stream().filter(deviceStatisticsVo -> deviceStatisticsVo.getName().equals(name)).collect(Collectors.toList());
    }
}
