package com.gz.service.statistics.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gz.common.DataGlobalVariable;
import com.gz.core.exception.BusinessException;
import com.gz.dto.borrow.ArchiveBorrowDTO;
import com.gz.dto.system.ArchivesTreeDTO;
import com.gz.mapper.borrow.ArchiveBorrowMapper;
import com.gz.mapper.statistics.SelectStatisticsMapper;
import com.gz.mapper.system.ArchivesTreeMapper;
import com.gz.rvo.statistics.ArchiveStatisticsRVO;
import com.gz.rvo.statistics.ArchiveYearStatisticsRVO;
import com.gz.rvo.statistics.BorrowStatisticsRVO;
import com.gz.service.statistics.SelectStatisticsService;
import com.gz.service.system.ArchivesTreeService;
import com.gz.service.system.impl.AuthServiceImpl;
import com.gz.utils.JwtUtils;
import com.gz.utils.UnitUtils;
import com.gz.vo.statistics.StatisticsVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.Id;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 查询统计Service 实现
 *
 * @author LiuchangLan
 * @date 2021/3/8 14:59
 */
@Slf4j
@Service
public class SelectStatisticsServiceImpl implements SelectStatisticsService {

    @Resource
    private SelectStatisticsMapper selectStatisticsMapper;

    @Resource
    private ArchivesTreeService archivesTreeService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ArchiveBorrowMapper archiveBorrowMapper;

    @Value("${upload-file.archive-file-root-directory}")
    private String baseDir;

    @Override
    public List<ArchiveStatisticsRVO> selectStatisticsByType(StatisticsVO vo) {
        String key = String.format(DataGlobalVariable.CACHE_KEY_FOMAT, this.getClass().getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), JSONObject.toJSONString(vo));
        if (stringRedisTemplate.hasKey(key)) {
            return JSONArray.parseArray(stringRedisTemplate.opsForValue().get(key), ArchiveStatisticsRVO.class);
        } else {
            if (vo.getMl() != null) {
                vo.setMlCodes(getMl(vo.getMl()));
            }
            if (!StringUtils.isEmpty(vo.getTime())) {
                vo.setStartTime(vo.getTime().split(" - ")[0]);
                vo.setEndTime(vo.getTime().split(" - ")[1]);
            }
            // 统计结果
            List<ArchiveStatisticsRVO> tree = selectStatisticsMapper.selectArchiveStatistics(vo);
//            //二级分类集合
//            List<String> list = new ArrayList<>();
//            List<ArchivesTreeDTO> archivesTreeDTOS = archivesTreeService.selectByOrder();
//            archivesTreeDTOS.forEach(dto -> {
//                if (dto.getParentId() != -1) {
//                    for (ArchivesTreeDTO dt : archivesTreeDTOS) {
//                        if (dto.getParentId() == dt.getId()) {
//                            list.add(dt.getCode());
//                        }
//                    }
//                }
//            });
            //占用磁盘大小临时存储到map
            Map<String, Long> map = new HashMap<>();
            List<ArchivesTreeDTO> archiveTrees = archivesTreeService.selectAll();
            archiveTrees.forEach(i -> {
                map.put(i.getCode(), 0L);
            });
            // 档案文件根目录
            File baseDir = FileUtil.file(this.baseDir);
            // 统计总数的一级档案数
            List<String> calcSumType = new ArrayList<String>(){{
                add("WS");
                add("SJ");
            }};
            //占用磁盘的总大小
            Long hj = 0L;
            if (FileUtil.exist(baseDir) && FileUtil.isDirectory(baseDir)) {
                // 全宗号
                for (File qzh : baseDir.listFiles()) {
                    if (FileUtil.isDirectory(qzh)) {
                        // 年份
                        for (File year : qzh.listFiles()) {
                            if (ObjectUtil.isNotEmpty(year)) {
                                //分类
                                for (File category : year.listFiles()) {
                                    if (ObjectUtil.isNotEmpty(category)) {
                                        // 一级档案数分类
                                        if (map.keySet().stream().anyMatch(i -> i.equals(category.getName()))){
                                            long histryFileSize = Long.parseLong(map.get(category.getName()).toString());
                                            Long currFileSize = this.getLinuxDirectorySize(category);
                                            histryFileSize += currFileSize;
                                            map.put(category.getName(), histryFileSize );
                                            if (calcSumType.contains(category.getName())){
                                                hj +=currFileSize;
                                            }
                                        }
                                        // 二级档案数
                                        for (File category2 : category.listFiles()) {
                                            if (map.keySet().stream().anyMatch(i -> i.equals(category2.getName()))){
                                                long histryFileSize = Long.parseLong(map.get(category2.getName()).toString());
                                                Long currFileSize = this.getLinuxDirectorySize(category2);
                                                histryFileSize += currFileSize;
                                                map.put(category2.getName(), histryFileSize );
                                                hj +=currFileSize;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Map<String, String> map1 = new HashMap<>();
            //内存大小赋值
            for (Map.Entry<String, Long> entry : map.entrySet()) {
                map1.put(entry.getKey(), UnitUtils.storageUnitConvertStr(entry.getValue()));
            }
            for (Map.Entry<String, String> entry : map1.entrySet()) {
                for (ArchiveStatisticsRVO li : tree) {
                    if (li.getCode().equals(entry.getKey())) {
                        li.setSpace(entry.getValue());
                    }
                }
            }
            //计算总量
            tree.forEach(e -> {
                if (e.getParentId() == -1) {
                    for (ArchiveStatisticsRVO node : tree) {
                        Integer archiveCount = 0;
                        Integer fileCount = 0;
                        if (e.getId().equals(node.getParentId())) {
                            archiveCount += node.getArchiveCount();
                            fileCount += node.getFileCount();
                        }
                        e.setArchiveCount(e.getArchiveCount() + archiveCount);
                        e.setFileCount(e.getFileCount() + fileCount);
                    }
                }
            });

            tree.get(tree.size() - 1).setSumSpace(UnitUtils.storageUnitConvertStr(hj));
            stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(tree), DataGlobalVariable.CACHE_EXPIRES, DataGlobalVariable.TIME_UNIT);
            return tree;
        }
    }

    @Override
    public List<ArchiveYearStatisticsRVO> selectArchiveYearStatistics(StatisticsVO vo) {
        String key = String.format(DataGlobalVariable.CACHE_KEY_FOMAT, this.getClass().getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), JSONObject.toJSONString(vo));
        if (stringRedisTemplate.hasKey(key)) {
            return JSONArray.parseArray(stringRedisTemplate.opsForValue().get(key), ArchiveYearStatisticsRVO.class);
        } else {
            if (vo.getMl() != null) {
                vo.setMlCodes(getMl(vo.getMl()));
            }
            if (!StringUtils.isEmpty(vo.getTime())) {
                vo.setStartTime(vo.getTime().split(" - ")[0]);
                vo.setEndTime(vo.getTime().split(" - ")[1]);
            }
            List<ArchiveYearStatisticsRVO> archiveYearStatisticsRVOS = selectStatisticsMapper.selectArchiveYearStatistics(vo);
            // 档案文件根目录
            File baseDir = FileUtil.file(this.baseDir);
            Long hj = 0l;
            log.info("{}{}", baseDir, baseDir.isDirectory());
            if (FileUtil.exist(baseDir) && FileUtil.isDirectory(baseDir)) {
                // 全宗号
                for (File qzh : baseDir.listFiles()) {
                    if (FileUtil.isDirectory(qzh)) {
                        // 年份
                        for (File year : qzh.listFiles()) {
                            if (ObjectUtil.isNotEmpty(year)) {
                                Long linuxDirectorySize = this.getLinuxDirectorySize(year);
                                hj += linuxDirectorySize;
                                archiveYearStatisticsRVOS.forEach(statisticsRVO -> {
                                    if (statisticsRVO.getGdnd().equals(year.getName())) {
                                        String s = UnitUtils.storageUnitConvertStr(linuxDirectorySize);
                                        statisticsRVO.setSpace(s);
                                    }
                                });
                            }
                        }
                    }
                }
            }
            if(archiveYearStatisticsRVOS.size()>0){
                archiveYearStatisticsRVOS.get(archiveYearStatisticsRVOS.size() - 1).setSumSpace(UnitUtils.storageUnitConvertStr(hj));
            }
            stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(archiveYearStatisticsRVOS), DataGlobalVariable.CACHE_EXPIRES, DataGlobalVariable.TIME_UNIT);
            return archiveYearStatisticsRVOS;
        }
    }

    /**
     * @return 文件夹占用空间大小 单位：字节（K） 1GB = 1024MB = 1024 * 1024 KB = 1024 * 1024 * 1024 B
     * @description 获取Linux文件夹大小
     * @author ZhangHai
     * @since 2021/9/15 11:33
     */
    private Long getLinuxDirectorySize(File file) {
        if (!file.exists()) {
            throw new BusinessException(500, "文件见不存在");
        }
        if (!file.isDirectory()) {
            throw new BusinessException(500, "参数非文件见");
        }
        // 执行命令Format
        final String commandFomat = "du -s -b {}";

        // 需要执行的命令
        final String command = StrUtil.format(commandFomat, file.getAbsoluteFile());
        log.info("文件夹大小扫描命令执行：{}", command);
        // 执行命令的结果
        String commandResult = RuntimeUtil.execForStr(command);
        // 占用空间大小 String类型
        String sizeStr = commandResult.substring(0, commandResult.indexOf("\t"));
        Long size = 0L;
        if (StrUtil.isNotEmpty(sizeStr)) {
            size = Long.parseLong(sizeStr);
        }
        return size;
    }

    @Override
    public List<BorrowStatisticsRVO> selectBorrowStatistics(StatisticsVO vo) {
        String key = String.format(DataGlobalVariable.CACHE_KEY_FOMAT, this.getClass().getName(), Thread.currentThread().getStackTrace()[1].getMethodName(), JSONObject.toJSONString(vo));
        if (stringRedisTemplate.hasKey(key)) {
            return JSONArray.parseArray(stringRedisTemplate.opsForValue().get(key), BorrowStatisticsRVO.class);
        } else {
            if (vo.getMl() != null) {
                vo.setMlCodes(getMl(vo.getMl()));
            }
            if (!StringUtils.isEmpty(vo.getTime())) {
                vo.setStartTime(vo.getTime().split(" - ")[0]);
                vo.setEndTime(vo.getTime().split(" - ")[1]);
            }
            List<BorrowStatisticsRVO> borrowStatisticsRVOS = selectStatisticsMapper.selectBorrowStatistics(vo);
            stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(borrowStatisticsRVOS), DataGlobalVariable.CACHE_EXPIRES, DataGlobalVariable.TIME_UNIT);
            return borrowStatisticsRVOS;
        }
    }

    private List<String> getMl(Integer id) {
        String key = String.format(DataGlobalVariable.STATISTICS_ML_CODE_KEY_FOMAT, id);
        String listString = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isEmpty(listString)) {
            ArchivesTreeDTO dto = archivesTreeService.selectByPrimaryKey(id);
            List<String> collect = archivesTreeService.selectChildById(id).stream().map(ArchivesTreeDTO::getCode).collect(Collectors.toList());
            if (dto.getParentId() == -1) {
                collect.add(archivesTreeService.selectByPrimaryKey(id).getCode());
            } else {
                collect.add(archivesTreeService.selectParentByCode(dto.getCode()).getCode());
            }
            stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(collect), 1L, TimeUnit.DAYS);
            return collect;
        } else {
            return JSONObject.parseObject(listString, List.class);
        }
    }


    @Override
    public Map<String, Integer> selectUserBorrowCountStatistics() {
        ArchiveBorrowDTO param = new ArchiveBorrowDTO();
        param.setAdminId(JwtUtils.getCurrentUserJwtPayload().getId());
        List<ArchiveBorrowDTO> selectResult = archiveBorrowMapper.select(param);
        Integer dqwgh = 0, // 到期未归还
                jjdq = 0, // 即将到期
                sqwcl = 0, // 申请未处理
                lsjyw = selectResult.size(); // 历史借阅数

        for (ArchiveBorrowDTO item : selectResult) {
            // 当前时间
            DateTime now = DateUtil.date();
            // 预计归还时间
            DateTime estimateReturnTime = DateUtil.parseDate(item.getEstimateReturnTime());
            // 已经过期 未归还 原件借阅 借阅审核已通过
            if (now.before(estimateReturnTime) && item.getReturnStatus() == 0 && item.getBorrowType() == 1 && item.getAuditStatus() == 2) {
                dqwgh++;
            }
            // 剩余3天为即将到期
            if (DateUtil.between(now, estimateReturnTime, DateUnit.DAY) == 3) {
                jjdq++;
            }
            if (item.getBorrowType() == 1 && item.getAuditStatus() == 0) {
                sqwcl++;
            }
        }
        Map<String, Integer> result = new HashMap<>();
        result.put("dqwgh", dqwgh);
        result.put("jjdq", jjdq);
        result.put("sqwcl", sqwcl);
        result.put("lsjyw", lsjyw);
        return result;
    }
}
