package com.ruoyi.web.service;

import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.syzy.domain.BBusiWishUniversitySubmitMarjors;
import com.ruoyi.syzy.domain.BBusiWishUniversitySubmitRecruitPlan;
import com.ruoyi.syzy.mapper.BBusiWishUniversitySubmitMarjorsMapper;
import com.ruoyi.syzy.mapper.BBusiWishUniversitySubmitMarjorsUpdateMapper;
import com.ruoyi.syzy.mapper.BBusiWishUniversitySubmitRecruitPlanMapper;
import com.ruoyi.voluntary.domain.PdDirectionManual;
import com.ruoyi.voluntary.domain.PdMarjorMatchRecord;
import com.ruoyi.voluntary.mapper.PdDirectionManualMapper;
import com.ruoyi.voluntary.mapper.PdMarjorMatchDataMapper;
import com.ruoyi.web.domain.ManualBindingMajorDto;
import com.ruoyi.web.domain.ManualBindingQueryDto;
import com.ruoyi.web.domain.ManualBindingRecruitPlanDto;
import com.ruoyi.web.util.BigDataParseExcelUtil;
import com.ruoyi.web.util.MarjorMatchResultWriter;
import com.ruoyi.web.util.MarjorMatchStatus;
import com.ruoyi.web.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class MatchService {
    private final BBusiWishUniversitySubmitRecruitPlanMapper busiWishUniversitySubmitRecruitPlanMapper;
    private final BBusiWishUniversitySubmitMarjorsMapper busiWishUniversitySubmitMarjorsMapper;
    private final PdDirectionManualMapper pdDirectionManualMapper;
    private final PdMarjorMatchDataMapper pdMarjorMatchDataMapper;
    private final BBusiWishUniversitySubmitMarjorsUpdateMapper busiWishUniversitySubmitMarjorsUpdateMapper;

    private final BackChangesService backChangesService;

    public MatchService(BBusiWishUniversitySubmitRecruitPlanMapper busiWishUniversitySubmitRecruitPlanMapper, BBusiWishUniversitySubmitMarjorsMapper busiWishUniversitySubmitMarjorsMapper, PdDirectionManualMapper pdDirectionManualMapper, PdMarjorMatchDataMapper pdMarjorMatchDataMapper, BBusiWishUniversitySubmitMarjorsUpdateMapper busiWishUniversitySubmitMarjorsUpdateMapper, BackChangesService backChangesService) {
        this.busiWishUniversitySubmitRecruitPlanMapper = busiWishUniversitySubmitRecruitPlanMapper;
        this.busiWishUniversitySubmitMarjorsMapper = busiWishUniversitySubmitMarjorsMapper;
        this.pdDirectionManualMapper = pdDirectionManualMapper;
        this.pdMarjorMatchDataMapper = pdMarjorMatchDataMapper;
        this.busiWishUniversitySubmitMarjorsUpdateMapper = busiWishUniversitySubmitMarjorsUpdateMapper;
        this.backChangesService = backChangesService;
    }

    public void exportManualBindingList(ManualBindingQueryDto queryDto, HttpServletResponse response) throws Exception {
        String[] years = queryDto.getYear().split(",");
        String[] matchTypes = (StringUtils.isNotBlank(queryDto.getMatchType()) ? queryDto.getMatchType() : "O,M").split(",");
        if (StringUtils.isBlank(queryDto.getPlanYear())) {
            queryDto.setPlanYear(String.valueOf(Calendar.getInstance().get(Calendar.YEAR)));
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String realFileName = queryDto.getLocation() + "_" + queryDto.getPlanYear() + "_" + queryDto.getMatchType() + "_" + queryDto.getYear() + "_" + sdf.format(new Date()) + ".xlsx";
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        FileUtils.setAttachmentResponseHeader(response, realFileName);

        Map<String, PdDirectionManual> directionManualMap = buildDirectionManualMap(); // Maps.newHashMap(); //
        MarjorMatchResultWriter writer = new MarjorMatchResultWriter();
        for(String matchType : matchTypes) {
            for(String year : years) {
                exportManualBindingSheet(queryDto, matchType, year, directionManualMap, writer);
            }
        }
        writer.write(response.getOutputStream());
    }

    public List<ManualBindingRecruitPlanDto> findManualBindingRecruitPlanList(ManualBindingQueryDto queryDto, Integer pageNum, Integer pageSize) {
        Map<String, PdDirectionManual> directionManualMap = buildDirectionManualMap(); // Maps.newHashMap(); //

        if (!"M".equals(queryDto.getMatchType())) {
            queryDto.setMatchType("O");
        }
        if (StringUtils.isBlank(queryDto.getYear())) {
            queryDto.setYear(String.valueOf(Calendar.getInstance().get(Calendar.YEAR) - 1));
        }
        if (StringUtils.isBlank(queryDto.getPlanYear())) {
            queryDto.setPlanYear(String.valueOf(Calendar.getInstance().get(Calendar.YEAR)));
        }
        PageHelper.startPage(pageNum, pageSize);
        List<BBusiWishUniversitySubmitRecruitPlan> recruitPlanList = busiWishUniversitySubmitRecruitPlanMapper.selectWishUniversitySubmitPlanForMatch(queryDto.toMap());

        Set<Long> submitIdSet = Sets.newHashSet();
        for(BBusiWishUniversitySubmitRecruitPlan rp : recruitPlanList) {
            for(String gid : rp.getGroups().split(",")) {
                submitIdSet.add(NumberUtils.toLong(gid, 0L));
            }
        }
        Map<Long, BBusiWishUniversitySubmitMarjors> submitMarjorsMap;
        if(submitIdSet.size() > 0) {
            submitMarjorsMap = busiWishUniversitySubmitMarjorsMapper.selectBBusiWishUniversitySubmitMarjorsByIds(new ArrayList<>(submitIdSet)).stream().collect(Collectors.toMap(BBusiWishUniversitySubmitMarjors::getId, Function.identity()));
        } else {
            submitMarjorsMap = Maps.newHashMap();
        }

        List<ManualBindingRecruitPlanDto> dtoList = Lists.newArrayList();
        for (BBusiWishUniversitySubmitRecruitPlan rp : recruitPlanList) {
            Integer groupId = rp.getMajorCount();
            Long manualId = null != rp.getLiberalScience() ? rp.getLiberalScience().longValue() : null;
            Long suggestId = null != rp.getBatch() ? rp.getBatch().longValue() : null;

            ManualBindingRecruitPlanDto dto = new ManualBindingRecruitPlanDto();
            dto.setDirectionManual(getPdDirectionManual(directionManualMap, rp.getMarjorDirection()));
            dto.setId(rp.getId());
            dto.setGroupId(groupId);
            dto.setYear(rp.getYear());
            dto.setLevel(rp.getLevel());
            dto.setType(rp.getType());
            dto.setManualId(manualId);
            dto.setStatus(rp.getStatus());
            dto.setSuggestId(suggestId);
            dto.setSpecialProject(rp.getSpecialProject());
            dto.setUniversityName(rp.getUniversityName());
            dto.setMarjorName(rp.getMarjorName());
            dto.setXuezhi(rp.getXuezhi());
            dto.setMarjorDirection(rp.getMarjorDirection());
            dto.setCollegeCode(rp.getCollegeCode());

            List<ManualBindingMajorDto> bindingMajorList = Lists.newArrayList();
            for (String sid : rp.getGroups().split(",")) {
                BBusiWishUniversitySubmitMarjors sm = submitMarjorsMap.get(NumberUtils.toLong(sid, 0L));
                if (null != sm) {
                    ManualBindingMajorDto majorDto = new ManualBindingMajorDto();
                    majorDto.setDirectionManual(getPdDirectionManual(directionManualMap, sm.getMarjorDirection()));
                    majorDto.setIsAlgorithmRecommended(null != suggestId && suggestId.equals(sm.getId()));
                    majorDto.setIsManualBound(null != manualId && manualId > 0 && manualId.equals(sm.getId()));

                    majorDto.setId(sm.getId());
                    majorDto.setYear(sm.getYear());
                    majorDto.setLevel(sm.getLevel());
                    majorDto.setType(sm.getType());
                    majorDto.setSpecialProject(sm.getSpecialProject());
                    majorDto.setUniversityName(sm.getUniversityName());
                    majorDto.setMarjorName(sm.getMarjorName());
                    majorDto.setXuezhi(sm.getXuezhi());
                    majorDto.setMarjorDirection(sm.getMarjorDirection());
                    bindingMajorList.add(majorDto);
                }
            }
            dto.setBindingMajorList(bindingMajorList);
            dtoList.add(dto);
        }
        return PageUtil.getDtoListWithPageable(recruitPlanList, dtoList);
    }


    public void importManualBindingList(String location, Integer planYear, InputStream is) throws Exception {
        final MarjorMatchStatus status = new MarjorMatchStatus();
        final List<PdMarjorMatchRecord> recordList = com.google.common.collect.Lists.newArrayList();
        final Set<Long> planIdSet = Sets.newHashSet();
        final Boolean newGaokao = pdMarjorMatchDataMapper.selectNewGaokao(location, planYear) > 0;
        BigDataParseExcelUtil xlx = new BigDataParseExcelUtil() {
            @Override
            public void optRows(int sheetIndex, int curRow, List<String> rowlist, List excelList) throws Exception {
                PdMarjorMatchRecord record = status.groupRecord;
                if(curRow == 0) {
                    saveMarjorMatchRecord(newGaokao, planYear, location, recordList, planIdSet, record, false);
                    status.groupId = -1;
                    status.groupRecord = null;

                    Map<String, Integer> headerIndexMap = status.headerIndexMap;
                    headerIndexMap.clear();
                    for (int i = 0; i < rowlist.size(); i++) {
                        headerIndexMap.put(rowlist.get(i), i);
                    }
                    if (CollectionUtils.intersection(headerIndexMap.keySet(), Sets.newHashSet("计ID", "组id", "年度", "备选", "选择", "修改", "状态")).size() == 7) {
                        status.sheetIndex = sheetIndex;
                        status.planIdIdx = headerIndexMap.get("计ID");
                        status.groupIdx = headerIndexMap.get("组id");  // *
                        status.yearIdx = headerIndexMap.get("年度");   // *
                        status.optionIdx = headerIndexMap.get("备选");
                        status.chooseIdx = headerIndexMap.get("选择"); // *
                        status.manualIdx = headerIndexMap.get("修改"); // *
                        status.statusIdx = headerIndexMap.get("状态");
                    } else {
                        log.warn("No data at sheet {}", sheetIndex + 1);
                    }
                    return;
                } else if(status.sheetIndex != sheetIndex) {
                    return;
                }
                Integer group = NumberUtils.toInt(rowlist.get(status.groupIdx), 0);
                if (group == 0) {
                    return;
                }
                if(!group.equals(status.groupId)) {
                    saveMarjorMatchRecord(newGaokao, planYear, location, recordList, planIdSet, record, false);
                    Integer year = NumberUtils.toInt(rowlist.get(status.yearIdx), 0);
                    // 生成新的一组
                    record = new PdMarjorMatchRecord();
                    record.setLocation(location);
                    record.setId(group);
                    record.setYear(year);
                    record.setManualId(null);
                    record.setSuggestId(0);
                    record.setMatchedId(null);
                    status.groupId = group;
                    status.groupRecord = record;
                    planIdSet.add(NumberUtils.toLong(rowlist.get(status.planIdIdx), 0L));
                }
                Integer suggestId = NumberUtils.toInt(rowlist.get(status.chooseIdx), 0);
                if (suggestId > 0) {
                    record.setSuggestId(suggestId);
                }
                Integer manualId = NumberUtils.toInt(rowlist.get(status.manualIdx), -1);
                if (manualId.equals(0)) { // 去掉默认推荐
                    record.setMatchedId(0);
                } else if(manualId > 0) { // 设置手动匹配
                    record.setManualId(manualId);
                }
            }
        };
        xlx.process(is);
        saveMarjorMatchRecord(newGaokao, planYear, location, recordList, planIdSet, status.groupRecord, true);
    }

    private void saveMarjorMatchRecord(Boolean newGaoKao, Integer planYear, String location, List<PdMarjorMatchRecord> recordList, Set<Long> planIdSet, PdMarjorMatchRecord record, boolean done) {
        if (null != record) {
            if (null != record.getManualId() && record.getManualId() > 0) {
                record.setMatchedId(record.getManualId()); // 指定
            } else if (null == record.getMatchedId()) {
                record.setMatchedId(record.getSuggestId()); // 建议
            }
            recordList.add(record);
        }
        if (done || recordList.size() >= 500) {
            pdMarjorMatchDataMapper.updateGroupSubmitBatch(recordList);
            // 计算 planId 和 marjorCollegeId
            if (planIdSet.size() > 0) {
                Set<Integer> collegeIdSet = pdMarjorMatchDataMapper.selectPlanCollegeIdList(planIdSet).stream().map(t -> t.getGroupId()).collect(Collectors.toSet());
                if (collegeIdSet.size() > 0) {
                    busiWishUniversitySubmitMarjorsUpdateMapper.insertUpdatePlanSubmit(planIdSet);
                    backChangesService.processUpdateStats(newGaoKao, planYear, location, planIdSet, collegeIdSet);
                }
            }
            recordList.clear();
            planIdSet.clear();
        }
    }

    private void exportManualBindingSheetData(List<BBusiWishUniversitySubmitRecruitPlan> recruitPlanList, String matchType, String year, Map<String, PdDirectionManual> directionManualMap, MarjorMatchResultWriter writer) {
        Set<Long> submitIdSet = Sets.newHashSet();
        for(BBusiWishUniversitySubmitRecruitPlan rp : recruitPlanList) {
            for(String gid : rp.getGroups().split(",")) {
                submitIdSet.add(NumberUtils.toLong(gid, 0L));
            }
        }
        Map<Long, BBusiWishUniversitySubmitMarjors> submitMarjorsMap;
        if(submitIdSet.size() > 0) {
            submitMarjorsMap = busiWishUniversitySubmitMarjorsMapper.selectBBusiWishUniversitySubmitMarjorsByIds(new ArrayList<>(submitIdSet)).stream().collect(Collectors.toMap(BBusiWishUniversitySubmitMarjors::getId, Function.identity()));
        } else {
            submitMarjorsMap = Maps.newHashMap();
        }

        for (BBusiWishUniversitySubmitRecruitPlan rp : recruitPlanList) {
            Integer groupId = rp.getMajorCount();
            Long manualId = null != rp.getLiberalScience() ? rp.getLiberalScience().longValue() : null;
            Long suggestId = null != rp.getBatch() ? rp.getBatch().longValue() : null;

            // 匹配,计ID,组id,年度,级别, 类型,备选,选择,编辑,状态
            String[] datas = new String[writer.getHeaders().length];
            datas[0] = rp.getPlanType();
            datas[1] = String.valueOf(rp.getId());
            datas[2] = String.valueOf(groupId);
            datas[3] = year;
            datas[4] = rp.getLevel();

            datas[5] = rp.getType();
            datas[9] = getStatusName(rp.getStatus());

            // 计专项,专专项,计院校,专院校,计专业, 专专业,计学制,专学制,计专业备注,专专业备注
            datas[10] = rp.getSpecialProject();
            datas[12] = rp.getUniversityName();
            datas[14] = rp.getMarjorName();
            datas[16] = rp.getXuezhi();
            datas[18] = rp.getMarjorDirection();
            // 计无效,专无效,计备注1,专备注1,计备注2,专备注2,计包含,专包含,计类别,专类别
            PdDirectionManual pd = getPdDirectionManual(directionManualMap, rp.getMarjorDirection());
            if (null != pd) {
                datas[20] = pd.getInvalidRemark();
                datas[22] = pd.getMemo1();
                datas[24] = pd.getMemo2();
                datas[26] = pd.getHasDirection();
                datas[28] = pd.getSpecialType();
            } else {
                datas[20] = "";
                datas[22] = "";
                datas[24] = "";
                datas[26] = "";
                datas[28] = "";
            }
            for (String sid : rp.getGroups().split(",")) {
                BBusiWishUniversitySubmitMarjors sm = submitMarjorsMap.get(NumberUtils.toLong(sid, 0L));
                if (null == sm) {
                    continue;
                }
                datas[6] = String.valueOf(sm.getId()); // 备选id
                boolean isSuggest = null != suggestId && suggestId.equals(sm.getId());
                datas[7] = isSuggest ? String.valueOf(suggestId) : "0"; // 备选是推荐,且只有一个，其他都是0
                datas[8] = null != manualId ? (manualId.equals(sm.getId()) ? String.valueOf(manualId) : (isSuggest ? "0" : "")) : ""; // 人工选择，如果备选不一样就否定

                datas[11] = sm.getSpecialProject();
                datas[13] = sm.getUniversityName();
                datas[15] = sm.getMarjorName();
                datas[17] = sm.getXuezhi();
                datas[19] = sm.getMarjorDirection();

                PdDirectionManual sd = getPdDirectionManual(directionManualMap, rp.getMarjorDirection());
                if(null != sd) {
                    datas[21] = sd.getInvalidRemark();
                    datas[23] = sd.getMemo1();
                    datas[25] = sd.getMemo2();
                    datas[27] = sd.getHasDirection();
                    datas[29] = sd.getSpecialType();
                } else {
                    datas[21] = "";
                    datas[23] = "";
                    datas[25] = "";
                    datas[27] = "";
                    datas[29] = "";
                }
                writer.appendRow(datas);
            }
        }
    }

    private void exportManualBindingSheet(ManualBindingQueryDto queryDto, String matchType, String year, Map<String, PdDirectionManual> directionManualMap, MarjorMatchResultWriter writer) {
        queryDto.setMatchType(matchType);
        queryDto.setYear(year);
        List<BBusiWishUniversitySubmitRecruitPlan> recruitPlanList = busiWishUniversitySubmitRecruitPlanMapper.selectWishUniversitySubmitPlanForMatch(queryDto.toMap());
        writer.appendSheet(year + "_" + matchType);
        Integer start = 0;
        while (start < recruitPlanList.size()) {
            Integer end = Math.min(start + 1000, recruitPlanList.size());
            exportManualBindingSheetData(recruitPlanList.subList(start, end), matchType, year, directionManualMap, writer);
            start = end;
        }
    }

    private static String[] STATUS_NAMES = new String[] {"0","1一对","2多对","3","4","5","6零对","7零多选","8多对一","9预否","10预置","11一错","12多错"};
    private String getStatusName(Integer status) {
        return status > 0 && status < STATUS_NAMES.length ? STATUS_NAMES[status] : "";
    }

    // 提取数据
    private PdDirectionManual getPdDirectionManual(Map<String, PdDirectionManual> directionManualMap, String direction) {
        PdDirectionManual pd = directionManualMap.get(normalDirectionKey(direction));
        return pd;
    }

    private String normalDirectionKey(String text) {
        String normalText = text.toLowerCase().replaceAll("\n|\t", "");
        return normalText;
    }

    private String normalKey(String text) {
        String normalText = text.toLowerCase().replaceAll("师|类|含|方向|招生|考生|专业|招收|\\(|\\)|\n|\t", "");
        return normalText;
    }

    private Map<String, PdDirectionManual> buildDirectionManualMap() {
        Map<String, PdDirectionManual> directionManualMap = Maps.newHashMap();
        log.info("begin load pdDirectionManualMap");
        for (PdDirectionManual pd : pdDirectionManualMapper.selectPdDirectionManualList(new PdDirectionManual())) {
            int nullCount = 0;
            // schoolLocation 暂忽略
            if (pd.getHasDirection() == null) {
                pd.setHasDirection("");
                nullCount++;
            }
            if (pd.getMemo1() == null) {
                pd.setMemo1("");
                nullCount++;
            } else {
                pd.setMemo1(normalKey(pd.getMemo1()));
            }
            if (pd.getMemo2() == null) {
                pd.setMemo2("");
                nullCount++;
            } else {
                pd.setMemo2(normalKey(pd.getMemo2()));
            }
            if (pd.getSpecialType() == null) {
                pd.setSpecialType("");
                nullCount++;
            }
            String key = pd.getDirection();
            if (nullCount == 4) {
                pd.setInvalidRemark("无效");
            }
            directionManualMap.put(normalDirectionKey(key), pd);
        }
        log.info("Finished buildDirectionManualMap, size: {}", directionManualMap.size());
        return directionManualMap;
    }
}
