package com.yiboshi.science.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yiboshi.arch.exception.BusinessException;
import com.yiboshi.science.base.BaseServiceImpl;
import com.yiboshi.science.base.Pagination;
import com.yiboshi.science.dao.ComProjectAssignDAO;
import com.yiboshi.science.entity.AssignCount;
import com.yiboshi.science.entity.ComProject;
import com.yiboshi.science.entity.ComProjectAssign;
import com.yiboshi.science.entity.SystemParameter;
import com.yiboshi.science.enumeration.CommonEnum;
import com.yiboshi.science.param.dto.ComExpertSpecDTO;
import com.yiboshi.science.param.dto.ComProjectAssignDTO;
import com.yiboshi.science.param.dto.ComProjectGroupDTO;
import com.yiboshi.science.param.query.ComProjectAssignQueryVO;
import com.yiboshi.science.service.*;
import lombok.AllArgsConstructor;
import net.bytebuddy.asm.Advice;
import org.mockito.internal.matchers.Find;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class ComProjectAssignServiceImpl extends BaseServiceImpl<ComProjectAssignDAO, ComProjectAssignQueryVO, ComProjectAssignDTO, ComProjectAssign> implements ComProjectAssignService {
    @Autowired
    private ComProjectAssignDAO comProjectAssignDAO;
    @Autowired
    private ComBatchService comBatchService;
    @Autowired
    private ComExpertSpecService comExpertSpecService;

    @Autowired
    private ComProjectService comProjectService;

    @Autowired
    private SystemParameterService systemParameterService;

    @Override
    protected void setCriteriaForQuery(ComProjectAssignQueryVO vo, QueryWrapper<ComProjectAssignQueryVO> criteria) {
        if (Objects.nonNull(vo.getExpertId())) {
            criteria.eq("a.expert_id", vo.getExpertId());
        }
        if (Objects.nonNull(vo.getProjId())) {
            criteria.eq("a.proj_id", vo.getProjId());
        }
        if (Objects.nonNull(vo.getGroupId())) {
            criteria.eq("k.id", vo.getGroupId());
        }
        if (Objects.nonNull(vo.getAssignYear())) {
            criteria.eq("assign_year", vo.getAssignYear());
        }
        if (Objects.nonNull(vo.getTotalScore())) {
            criteria.eq("total_score", vo.getTotalScore());
        }
        if (Objects.nonNull(vo.getAuditState())) {
            criteria.eq("a.audit_state", vo.getAuditState());
        }
        if (Objects.nonNull(vo.getCertId())) {
            criteria.like("c.cert_id", vo.getCertId());
        }
        if (Objects.nonNull(vo.getPersonName())) {
            criteria.like("c.person_name", vo.getPersonName());
        }
        if (Objects.nonNull(vo.getSex())) {
            criteria.eq("c.sex", vo.getSex());
        }
        if (Objects.nonNull(vo.getProjName())) {
            criteria.like("f.proj_name", vo.getProjName());
        }
        if (Objects.nonNull(vo.getProjType())) {
            criteria.eq("f.proj_type", vo.getProjType());
        }
        if (Objects.nonNull(vo.getProjNo())) {
            criteria.like("f.proj_no", vo.getProjNo());
        }
        if (Objects.nonNull(vo.getAppUnitName())) {
            criteria.like("i.unit_name", vo.getAppUnitName());
        }

    }

    @Override
    public Pagination<ComProjectAssignDTO> getListByPage(ComProjectAssignQueryVO vo) {
        QueryWrapper criteria = new QueryWrapper();
        setCriteriaForQuery(vo, criteria);
        Page<ComProjectAssignQueryVO> page = new Page<>(vo.getPageIndex(), vo.getPageSize());
        List<ComProjectAssignDTO> dtoList = comProjectAssignDAO.getListByPage(page, criteria).getRecords();
        dtoList.forEach(e -> {
            e.setSpecList(comExpertSpecService.getListByExpertId(e.getExpertId()));
        });
        return new Pagination<>(dtoList, page.getTotal(), vo.getPageSize());
    }

    @Transactional
    public String assignProject(Map<String, Object> map) {
        List<String> projects = null;
        List<String> experts = null;
        if (!map.containsKey("type"))
            throw new BusinessException("参数错误");
        if (!map.containsKey("projects"))
            throw new BusinessException("参数错误");
        if (!map.containsKey("projType"))
            throw new BusinessException("参数错误");
        Integer projType = CommonEnum.projType.num.getCode();
        if (map.containsKey("projType")) {
            projType = (int) map.get("projType");
        }
        experts = (List<String>) map.get("experts");
        projects = (List<String>) map.get("projects");
        Integer reportYear = comBatchService.getReportYear(projType);
        if (null == projects || projects.size() == 0 || null == experts || experts.size() == 0)
            throw new BusinessException("参数错误");
        return this.assignProject(reportYear, projects, experts);
    }

    public String assignProject(Integer year, List<String> projects, List<String> experts) {
        AtomicInteger allCount = new AtomicInteger(0);
        projects.forEach(e -> {
            AtomicInteger assignCount = new AtomicInteger(0);
            experts.forEach(f -> {
                if (!isDistribute(e, f)) {
                    ComProjectAssign assign = new ComProjectAssign();
                    assign.setProjId(e);
                    assign.setExpertId(f);
                    assign.setExpertType(comExpertSpecService.getExpertTypeByExpertId(f));
                    assign.setAssignYear(year);
                    this.insert(assign);
                    assignCount.incrementAndGet();
                    allCount.incrementAndGet();
                }
            });
            if (assignCount.get() > 0)
                updateAssignState(e);
        });
        if (allCount.get() == 0)
            throw new BusinessException("项目分配失败或专家已分配，请检查！");
        return allCount.getAndIncrement() + "";
    }

    public String deleteAssignExpert(String id) {
        ComProjectAssign comProjectAssign = this.getById(id);
        if (null == comProjectAssign)
            throw new BusinessException("分配记录已删除！");
        if (Objects.nonNull(comProjectAssign.getAuditState()) || !comProjectAssign.getAuditState().equals(0))
            throw new BusinessException("专家已评审，不允许删除！");
        this.deleteById(id);
        this.updateAssignState(comProjectAssign.getProjId());
        return id;
    }

    public boolean isAssignByExpertId(String expertId) {
        ComProjectAssign model = new ComProjectAssign();
        model.setExpertId(expertId);
        List<ComProjectAssign> list = this.entityList(model);
        if (null != list && list.size() > 0) {
            return true;
        } else
            return false;
    }

    @Transactional
    public String expertEvaluation(ComProjectAssignDTO dto) {
        ComProjectAssign comProjectAssign = this.getById(dto.getId());
        if (null == comProjectAssign)
            throw new BusinessException("分配记录不存在或已删除！");
        ComProjectAssign entity = convert2Entity(dto);
        this.update(entity);
        if (Objects.nonNull(dto.getAuditState()) && dto.getAuditState().equals(2)) {
            this.updateAssignState(entity.getProjId());
        }
        return entity.getId();
    }

    public ComProjectAssignDTO getAssignExpertById(String id) {
        ComProjectAssignDTO dto = comProjectAssignDAO.getAssignExpertById(id);
        if (null != dto) {
            dto.setSpecList(comExpertSpecService.getListByExpertId(dto.getExpertId()));
        }
        return dto;
    }

    public List<AssignCount> getAssignCount(String expertId) {
        return comProjectAssignDAO.getAssignCount(expertId);
    }

    public List<ComProjectAssignDTO> getAssignExpertList(String projectId) {
        return comProjectAssignDAO.getAssignExpertList(projectId);
    }

    private boolean isDistribute(String projectId, String expertId) {
        ComProjectAssign comProjectAssign = new ComProjectAssign();
        comProjectAssign.setProjId(projectId);
        comProjectAssign.setExpertId(expertId);
        comProjectAssign = this.getEntity(comProjectAssign);
        if (comProjectAssign != null)
            return true;
        else
            return false;
    }

    public void updateAssignState(String projectId) {
        Integer assignState;
        Integer completed;

        BigDecimal totalScore = new BigDecimal(0);
        BigDecimal calculateScore = new BigDecimal(0);

        BigDecimal technologyScore = new BigDecimal(0);
        BigDecimal technologyAverageScore = new BigDecimal(0);
        BigDecimal economyScore = new BigDecimal(0);
        BigDecimal economyAverageScore = new BigDecimal(0);

        List<ComProjectAssignDTO> list = this.getAssignExpertList(projectId);
        if (null == list || list.size() == 0) {
            completed = 0;
            assignState = 0;
        } else {
            int technologyCount = 0;
            int economyCount = 0;
            int personCount = 0;
            for (ComProjectAssignDTO obj : list) {
                if (Objects.nonNull(obj.getAuditState()) && obj.getAuditState().equals(2)) {
                    personCount++;
                    if (Objects.nonNull(obj.getTotalScore())) {
                        if (obj.getExpertType().equals(1)) {
                            technologyCount++;
                            technologyScore = technologyScore.add(obj.getTotalScore());
                        } else {
                            economyCount++;
                            economyScore = economyScore.add(obj.getTotalScore());
                        }
                    }
                }
            }
            DecimalFormat df1 = new DecimalFormat("0.00");
            if (technologyCount > 0) {
                technologyAverageScore = technologyScore.divide(new BigDecimal(technologyCount), 2, BigDecimal.ROUND_HALF_UP);
                technologyScore = new BigDecimal(df1.format(technologyScore));
            }
            if (economyCount > 0) {

                economyAverageScore = economyScore.divide(new BigDecimal(economyCount), 2, BigDecimal.ROUND_HALF_UP);
                economyScore = new BigDecimal(df1.format(economyScore));
            }
            totalScore = technologyScore.add(economyScore);
            calculateScore = technologyAverageScore.multiply(new BigDecimal(0.8)).add(economyAverageScore.multiply(new BigDecimal(0.2)));
            calculateScore = new BigDecimal(df1.format(calculateScore));
            if (list.size() > personCount)
                completed = 0;
            else
                completed = 1;
            assignState = 1;
        }
        this.updateAssignState(projectId, assignState, completed, totalScore, technologyScore, technologyAverageScore, economyScore, economyAverageScore, calculateScore);
    }

    public void updateAssignState(String projectId, Integer assignState, Integer completed, BigDecimal totalScore, BigDecimal technologyScore, BigDecimal technologyAverageScore, BigDecimal economyScore, BigDecimal economyAverageScore, BigDecimal calculateScore) {
        ComProject comProject = new ComProject();
        comProject.setId(projectId);
        comProject.setAssignState(assignState);
        comProject.setCompleted(completed);
        comProject.setTotalScore(totalScore);
        comProject.setTechnologyScore(technologyScore);
        comProject.setTechnologyAverageScore(technologyAverageScore);
        comProject.setEconomyScore(economyScore);
        comProject.setEconomyAverageScore(economyAverageScore);
        comProject.setCalculateScore(calculateScore);
        comProject.setUpdated(new Date());
        comProjectService.update(comProject);
    }

    public List<ComProjectGroupDTO> getProjectGroupAssignById(String id, String expertId) {
        return comProjectAssignDAO.getProjectGroupAssignById(id, expertId);
    }

}
