package com.gz.service.archive.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.gz.common.DataGlobalVariable;
import com.gz.core.exception.BusinessException;
import com.gz.core.exception.CustomExceptionEnum;
import com.gz.dto.archive.ArchiveDTO;
import com.gz.dto.archive.SecondaryArchiveDTO;
import com.gz.dto.archive.SecondaryArchiveDTO;
import com.gz.dto.system.ArchivesTreeDTO;
import com.gz.dto.system.MenuRoleDTO;
import com.gz.mapper.archive.ArchiveMapper;
import com.gz.mapper.archive.SecondaryArchiveMapper;
import com.gz.mapper.system.MenuRoleMapper;
import com.gz.rvo.archive.ArchiveRVO;
import com.gz.rvo.borrow.ArchiveBorrowListRVO;
import com.gz.service.archive.ArchiveService;
import com.gz.service.system.ArchivesTreeService;
import com.gz.utils.ExcelUtils;
import com.gz.utils.JwtUtils;
import com.gz.utils.ObjectUtils;
import com.gz.vo.PageVO;
import com.gz.vo.archive.AgainSearchVO;
import com.gz.vo.archive.GenerateDhVO;
import com.gz.vo.archive.InsertArchiveVO;
import com.gz.vo.archive.SearchArchiveVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author LiuchangLan
 * @date 2021/2/23 18:12
 */
@Slf4j
@Service
public class ArchiveServiceImpl implements ArchiveService {

    @Resource
    private ArchiveMapper mapper;

    @Resource
    private SecondaryArchiveMapper secondaryArchiveMapper;

    @Resource
    private ArchivesTreeService archivesTreeService;

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private MenuRoleMapper menuRoleMapper;


    @Override
    public Integer insert(InsertArchiveVO vo) throws Exception {
        vo.setCreated(JwtUtils.getCurrentUserJwtPayload().getId());
        return mapper.insertSelective(vo);
    }

    @Override
    public Integer delete(Integer id) {
        return mapper.deleteByPrimaryKey(id);
    }

    @Override
    public Integer update(ArchiveDTO dto) {
        dto.setUpdated(JwtUtils.getCurrentUserJwtPayload().getId());
        return mapper.updateByPrimaryKeySelective(dto);
    }

    @Override
    public List<ArchiveDTO> selectAll() {
        return mapper.selectRoleAll(JwtUtils.getCurrentUserJwtPayload().getRoleId());
    }

    @Override
    public PageInfo<ArchiveRVO> selectByPage(SearchArchiveVO vo) {
        PageHelper.startPage(vo.getPageNum(), vo.getPageSize());
        vo.setRoleId(JwtUtils.getCurrentUserJwtPayload().getRoleId());
        Map<String, Object> params = vo.getParams();
        String sql = "";
        if (params != null) {
            if (StrUtil.isNotEmpty(String.valueOf(params.get("time")))) {
                String[] times = String.valueOf(params.get("time")).split(" - ");
                sql += "AND (a.create_time BETWEEN '" + times[0] + "'" + " AND '" + times[1] + "')";
            }
            for (int i = 1; i <= 10; i++) {
                if (params.get("field_" + i) == null) {
                    continue;
                }
                if (i == 1) {
                    sql += "and ";
                } else {
                    sql += params.get("join_" + i) + " ";
                }
                sql += params.get("field_" + i);
                if ("like".equals(params.get("condition_" + i))) {
                    sql += " like" + " '%" + params.get("variable_" + i) + "%'";
                } else {
                    sql += "" + params.get("condition_" + i) + " '" + params.get("variable_" + i) + "' ";
                }
            }
        }
        HttpSession session = request.getSession();
        vo.setSql(sql);
        PageInfo<ArchiveRVO> result = new PageInfo<>();
        String searchSql = null;
        if (vo.isAgain()) {
            // 读取上一次执行的sql
            String searchArchiveSql = String.valueOf(session.getAttribute(DataGlobalVariable.SEARCH_SQL_KEY));
            // 封装二次搜索条件
            AgainSearchVO againSearchVO = BeanUtil.copyProperties(vo, AgainSearchVO.class);
            // 存入条件
            againSearchVO.setSearchSql(searchArchiveSql);
            result = new PageInfo<>(mapper.againSearchList(againSearchVO));
            // 结果中检索
            searchSql = getSql("com.gz.mapper.archive.ArchiveMapper.againSearchList", againSearchVO);
        } else {
            // 正常检索
            searchSql = getSql("com.gz.mapper.archive.ArchiveMapper.searchList", vo);
            result = new PageInfo<>(mapper.searchList(vo));
        }
        log.debug("本次检索sql:{}", searchSql);
        // 缓存本次执行的sql
        session.setAttribute(DataGlobalVariable.SEARCH_SQL_KEY, searchSql);
        return result;

    }

    private String getSql(String javaMethodAddress, Object param) {
        Map<String, Object> stringObjectMap = ObjectUtils.objToMap(param);
        BoundSql boundSql = sqlSessionFactory.getConfiguration()
                .getMappedStatement(javaMethodAddress).getBoundSql(param);


        String sql = boundSql.getSql();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i = 0; i < parameterMappings.size(); i++) {
            sql = sql.replaceFirst("\\?", String.format("'%s'", stringObjectMap.get(parameterMappings.get(i).getProperty())));
        }
//        log.info("获取运行的sql:{}",sql);
        return sql;
    }

    @Override
    public ArchiveDTO selectByPrimaryKey(Integer id) {
        return mapper.selectByRolePrimaryKey(JwtUtils.getCurrentUserJwtPayload().getRoleId(), id);
    }

    /**
     * @description 生成档号
     * @author LiuChangLan
     * @since 2021/2/24 17:11
     */
    private void generateDh(InsertArchiveVO vo) throws Exception {
        ArchivesTreeDTO archivesTreeDTO = archivesTreeService.selectParentByCode(vo.getMl());
        // 查询件号
        String jh = String.format("%05d", mapper.selectCurrAutoIncrement());
        switch (archivesTreeDTO.getCode()) {
            case "WS":
                vo.setDh(String.format(DataGlobalVariable.WS_DH_FOMAT, vo.getQzh(), vo.getGdnd(), vo.getBgqx(), jh));
                break;
            case "KU":
                vo.setDh(String.format(DataGlobalVariable.KU_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getGdnd(), vo.getHsdwdh(), vo.getBgqx(), jh));
                break;
            case "KJ":
                vo.setDh(String.format(DataGlobalVariable.KJ_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getMl(), vo.getBgqx(), vo.getXmdh(), jh));
                break;
            case "ZY":
                vo.setDh(String.format(DataGlobalVariable.ZY_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getMl(), vo.getBgqx(), jh));
                break;
            case "YX":
                // 段号
                String dh = String.format("%02d", 0);
                vo.setDh(String.format(DataGlobalVariable.YX_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getMl(), vo.getBgqx(), jh, dh));
                break;
            case "SJ":
                vo.setDh(String.format(DataGlobalVariable.SJ_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getBgqx(), jh));
                break;
            case "SW":
                vo.setDh(String.format(DataGlobalVariable.SW_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getMl(), vo.getBgqx(), jh));
                break;
            case "MT":
                vo.setDh(String.format(DataGlobalVariable.MT_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getBgqx(), jh));
                break;
            default:
                throw new BusinessException(CustomExceptionEnum.GENERATE_DH_ERROR);
        }
    }


    @Override
    public PageInfo<ArchiveBorrowListRVO> selectBorrowList(PageVO vo) {
        PageHelper.startPage(vo.getPageNum(), vo.getPageSize());
        return new PageInfo<>(mapper.selectBorrowList(JwtUtils.getCurrentUserJwtPayload().getRoleId()));
    }

    @Override
    public Integer getJH(String mlCode) {
        return mapper.selectMaxJH(mlCode);
    }

    @Override
    public String generateDh(GenerateDhVO vo) {
        String jh = String.format("%05d", vo.getJh());
        ArchivesTreeDTO dto = archivesTreeService.selectParentByCode(vo.getSl());
        if (dto != null) {
            vo.setMl(dto.getCode());
        }

        switch (vo.getMl()) {
            case "WS":
                return String.format(DataGlobalVariable.WS_DH_FOMAT, vo.getQzh(), vo.getGdnd(), vo.getBgqx(), jh);
            case "KU":
                return String.format(DataGlobalVariable.KU_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getGdnd(), vo.getHsdwdh(), vo.getBgqx(), jh);
            case "KJ":
                return String.format(DataGlobalVariable.KJ_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getMl(), vo.getBgqx(), vo.getXmdh(), jh);
            case "ZY":
                return String.format(DataGlobalVariable.ZY_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getMl(), vo.getBgqx(), jh);
            case "YX":
                // 段号
                String dh = String.format("%02d", 0);
                return String.format(DataGlobalVariable.YX_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getMl(), vo.getBgqx(), jh, dh);
            case "SJ":
                return String.format(DataGlobalVariable.SJ_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getBgqx(), jh);
            case "SW":
                return String.format(DataGlobalVariable.SW_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getMl(), vo.getBgqx(), jh);
            case "MT":
                return String.format(DataGlobalVariable.MT_DH_FOMAT, vo.getQzh(), vo.getMlh(), vo.getBgqx(), jh);
            default:
                throw new BusinessException(CustomExceptionEnum.GENERATE_DH_ERROR);
        }
    }

    /**
     * @description 导入档案目录
     * @author LiuChangLan
     * @since 2021/4/9 9:28
     */
    private List<ArchiveDTO> importArchive(InputStream inputStream) {
        ExcelReader reader = ExcelUtil.getReader(inputStream, 0);
        List<List<Object>> rows = reader.read();
        List<ArchiveDTO> result = new ArrayList<>();
        for (int i = 1; i < rows.size(); i++) {
            try {
                List<Object> row = rows.get(i);
                ArchiveDTO dto = new ArchiveDTO();
                dto.setMl(ExcelUtils.getExcelCellStringValue(row, 29));
                dto.setDagdm(ExcelUtils.getExcelCellStringValue(row, 0));
                dto.setDh(ExcelUtils.getExcelCellStringValue(row, 1));
                dto.setQzh(ExcelUtils.getExcelCellStringValue(row, 2));
                dto.setGdnd(ExcelUtils.getExcelCellStringValue(row, 3));
                dto.setMj(ExcelUtils.getExcelCellStringValue(row, 4));
                dto.setBgqx(ExcelUtils.getExcelCellStringValue(row, 5));
                // TODO: 2021/2/25 机构代号 6 馆编号 7
                dto.setJh(ExcelUtils.getExcelCellIntegerValue(row, 7));
                dto.setZtsl(ExcelUtils.getExcelCellIntegerValue(row, 8));
                dto.setWjbh(ExcelUtils.getExcelCellStringValue(row, 9));
                DateTime wjxcsj = null;
                if ("WS".equals(dto.getMl())) {
                    wjxcsj = DateUtil.parse(ExcelUtils.getExcelCellStringValue(row, 10), "yyyyMMdd");
                } else {
                    wjxcsj = DateUtil.parse(ExcelUtils.getExcelCellStringValue(row, 10), "yyyyMM");
                }
                dto.setWjxcsj(DateUtil.formatDate(wjxcsj));
                String wjdqsj = ExcelUtils.getExcelCellStringValue(row, 30);
                if (StrUtil.isEmpty(wjdqsj)) {
                    switch (dto.getBgqx()) {
                        case "005":
                        case "D10":
                            wjdqsj = DateUtil.formatDate(DateUtil.offset(wjxcsj, DateField.YEAR, 10));
                            break;
                        case "004":
                        case "D30":
                            wjdqsj = DateUtil.formatDate(DateUtil.offset(wjxcsj, DateField.YEAR, 30));
                            break;
                    }
                } else {
                    DateTime wjdqsjDate = null;
                    if ("WS".equals(dto.getMl())) {
                        wjdqsjDate = DateUtil.parse(wjdqsj, "yyyyMMdd");
                    } else {
                        wjdqsjDate = DateUtil.parse(wjdqsj, "yyyyMM");
                    }
                    dto.setWjdqsj(DateUtil.formatDate(wjdqsjDate));
                }

                dto.setWjdqsj(wjdqsj);
                dto.setZrz(ExcelUtils.getExcelCellStringValue(row, 11));
                dto.setTm(ExcelUtils.getExcelCellStringValue(row, 12));
                dto.setBz(ExcelUtils.getExcelCellStringValue(row, 13));
                dto.setZbbm(ExcelUtils.getExcelCellStringValue(row, 14));
                dto.setXbbm(ExcelUtils.getExcelCellStringValue(row, 15));
                dto.setKzf(ExcelUtils.getExcelCellStringValue(row, 16));
                dto.setDzwdh(ExcelUtils.getExcelCellStringValue(row, 17));
                dto.setSwh(ExcelUtils.getExcelCellStringValue(row, 18));
                dto.setGb(ExcelUtils.getExcelCellStringValue(row, 19));
                dto.setWz(ExcelUtils.getExcelCellStringValue(row, 20));
                dto.setZtgg(ExcelUtils.getExcelCellStringValue(row, 21));
                dto.setZtlx(ExcelUtils.getExcelCellStringValue(row, 22));
                dto.setZtdw(ExcelUtils.getExcelCellStringValue(row, 23));
                dto.setZzjgdm(ExcelUtils.getExcelCellStringValue(row, 24));
                dto.setFlh(ExcelUtils.getExcelCellStringValue(row, 25));
                dto.setZtc(ExcelUtils.getExcelCellStringValue(row, 26));
                dto.setQwbs(ExcelUtils.getExcelCellStringValue(row, 27));
                dto.setMlh(ExcelUtils.getExcelCellStringValue(row, 28));
                dto.setYs(ExcelUtils.getExcelCellIntegerValue(row, 31));
                dto.setXdjh(ExcelUtils.getExcelCellStringValue(row, 32));
//                dto.setMj("NABU");
                result.add(dto);
            } catch (Exception e) {
                throw new BusinessException(500, String.format("【档案目录】解析出错，错误位置:%s行,错误原因：%s", (i + 1), e.getMessage()));
            }
        }
        String redisKey = String.format(DataGlobalVariable.IMPORT_ARCHIVE_SESSION_KEY_FOMAT, request.getSession().getId());
        stringRedisTemplate.opsForValue().set(redisKey, JSONObject.toJSONString(result), 1L, TimeUnit.HOURS);
        log.info("【档案目录】成功解析，共有{}条档案目录数据", result.size());
        return result;
    }

    /**
     * @description 导入二级目录
     * @author LiuChangLan
     * @since 2021/4/9 9:31
     */
    private List<SecondaryArchiveDTO> importSecondaryArchive(InputStream inputStream) {
        ExcelReader reader = ExcelUtil.getReader(inputStream, 1);
        List<List<Object>> rows = reader.read();
        List<SecondaryArchiveDTO> result = new ArrayList<>();
        for (int i = 1; i < rows.size(); i++) {
            try {
                List<Object> row = rows.get(i);
                SecondaryArchiveDTO dto = new SecondaryArchiveDTO();
                // 档号Str
                dto.setDh(ExcelUtils.getExcelCellStringValue(row, 0));
                // 顺序号
                dto.setSxh(ExcelUtils.getExcelCellIntegerValue(row, 1));
                // 文号
                dto.setWh(ExcelUtils.getExcelCellStringValue(row, 2));
                // 责任者
                dto.setZrz(ExcelUtils.getExcelCellStringValue(row, 3));
                // 题名
                dto.setTm(ExcelUtils.getExcelCellStringValue(row, 4));
                // 日期
                if (StrUtil.isEmpty(ExcelUtils.getExcelCellStringValue(row, 5))) {
                    dto.setRq("");
                } else if (ExcelUtils.getExcelCellStringValue(row, 5).length() == 6) {
                    DateTime rq = DateUtil.parse(ExcelUtils.getExcelCellStringValue(row, 5), "yyyyMM");
                    dto.setRq(DateUtil.formatDate(rq));
                } else if (ExcelUtils.getExcelCellStringValue(row, 5).length() == 8) {
                    DateTime rq = DateUtil.parse(ExcelUtils.getExcelCellStringValue(row, 5), "yyyyMMdd");
                    dto.setRq(DateUtil.formatDate(rq));
                }
                // 页号
                dto.setYh(ExcelUtils.getExcelCellStringValue(row, 6));
                // 备注
                dto.setBz(ExcelUtils.getExcelCellStringValue(row, 7));
                result.add(dto);
            } catch (Exception e) {
                throw new BusinessException(500, String.format("【卷内目录】解析出错，错误位置:%s行,错误原因：%s", (i + 1), e.getMessage()));
            }
        }
        String redisKey = String.format(DataGlobalVariable.IMPORT_SECONDARY_ARCHIVE_SESSION_KEY_FOMAT, request.getSession().getId());
        stringRedisTemplate.opsForValue().set(redisKey, JSONObject.toJSONString(result), 1L, TimeUnit.HOURS);
        log.info("【卷内目录】成功解析，共有{}条档案目录数据", result.size());
        return result;
    }

    @Override
    public List<ArchiveDTO> importExcel(MultipartFile file) throws IOException {
        List<ArchiveDTO> archiveDTOS = importArchive(file.getInputStream());
        importSecondaryArchive(file.getInputStream());
        return archiveDTOS;
    }

    @Override
    public ArchiveRVO selectByPrimaryKeyText(Integer id) {
        MenuRoleDTO param = new MenuRoleDTO();
        param.setRoleId(JwtUtils.getCurrentUserJwtPayload().getRoleId());
        param.setRoleType(DataGlobalVariable.ARCHIVE_SHOW);
        if (menuRoleMapper.select(param).stream().filter(i -> i.getMenuId() == 1).count() <= 0) {
            throw new BusinessException(CustomExceptionEnum.NO_ROLE);
        }

        return mapper.selectByPrimaryKeyText(id);
    }

    @Override
    @Transactional
    public Integer confirmImport() {
        Integer archiveCount = 0;
        Integer secondaryArchiveCount = 0;
        String archiveRedisKey = String.format(DataGlobalVariable.IMPORT_ARCHIVE_SESSION_KEY_FOMAT, request.getSession().getId());
        String secondaryArchiveRedisKey = String.format(DataGlobalVariable.IMPORT_SECONDARY_ARCHIVE_SESSION_KEY_FOMAT, request.getSession().getId());
        // 读取缓存中的档案数据
        String archiveJsonStr = stringRedisTemplate.opsForValue().get(archiveRedisKey);
        // 读取缓存中的卷内目录数据
        String secondaryArchiveJsonStr = stringRedisTemplate.opsForValue().get(secondaryArchiveRedisKey);
        if (StrUtil.isEmpty(archiveJsonStr)) {
            throw new BusinessException(500, "请先上传文件");
        }
        List<ArchiveDTO> archiveDTOS = JSONObject.parseArray(archiveJsonStr, ArchiveDTO.class);
        List<SecondaryArchiveDTO> secondaryArchiveDTOs = JSONObject.parseArray(secondaryArchiveJsonStr, SecondaryArchiveDTO.class);
        for (ArchiveDTO archiveDTO : archiveDTOS) {
            archiveCount += mapper.insertSelective(archiveDTO);
            stringRedisTemplate.opsForValue().set(archiveDTO.getDh(), String.valueOf(archiveDTO.getId()), 1L, TimeUnit.DAYS);
        }
        for (SecondaryArchiveDTO dto : secondaryArchiveDTOs) {
            String dh = dto.getDh();
            String archiveDh = dh.substring(0, dh.lastIndexOf("-"));
            int archiveId = Integer.parseInt(stringRedisTemplate.opsForValue().get(archiveDh));
            dto.setArchiveId(archiveId);
            secondaryArchiveCount += secondaryArchiveMapper.insertSelective(dto);
        }
        log.info("【档案目录】确定导入{}条数据", archiveCount);
        log.info("【卷内目录】确定导入{}条数据", secondaryArchiveCount);
        stringRedisTemplate.delete(archiveRedisKey);
        stringRedisTemplate.delete(secondaryArchiveRedisKey);
        return archiveCount;
    }
}
