package com.izouma.wenlvju.service.performance;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.izouma.wenlvju.domain.ArtType;
import com.izouma.wenlvju.domain.Award;
import com.izouma.wenlvju.domain.performance.Programme;
import com.izouma.wenlvju.domain.performance.ProgrammeAward;
import com.izouma.wenlvju.dto.AwardDTO;
import com.izouma.wenlvju.dto.PageQuery;
import com.izouma.wenlvju.dto.ProgrammeAwardDTO;
import com.izouma.wenlvju.enums.ProgrammeStatus;
import com.izouma.wenlvju.repo.ArtTypeRepo;
import com.izouma.wenlvju.repo.AwardRepo;
import com.izouma.wenlvju.repo.performance.ProgrammeAwardRepo;
import com.izouma.wenlvju.repo.performance.ProgrammeRepo;
import com.izouma.wenlvju.utils.JpaUtils;
import com.izouma.wenlvju.utils.ObjUtils;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class ProgrammeAwardService {

    private ProgrammeAwardRepo programmeAwardRepo;
    private ArtTypeRepo        artTypeRepo;
    private ProgrammeRepo      programmeRepo;
    private AwardRepo          awardRepo;

    public Page<ProgrammeAward> all(PageQuery pageQuery) {
        return programmeAwardRepo.findAll(JpaUtils.toSpecification(pageQuery, ProgrammeAward.class), JpaUtils.toPageRequest(pageQuery));
    }

    public List<ProgrammeAwardDTO> getAll(Long performanceId) {
        //找出所有节目奖项设置
        PageQuery pageQuery = new PageQuery();
        pageQuery.setSize(100);
        pageQuery.setSort("specialtyId,asc");
        pageQuery.getQuery().put("performanceId", performanceId);
        List<ProgrammeAward> programmeAwards = this.all(pageQuery).getContent();

        // 奖项
        List<Award> awardList = awardRepo.findAllByPerformanceId(performanceId);

        //专业
        List<ArtType> all = artTypeRepo.findAll();
        Map<Long, String> artTypeMap = all
                .stream()
                .collect(Collectors.toMap(ArtType::getId, ArtType::getName));

        //已经设置完成
        if (CollUtil.isNotEmpty(programmeAwards)) {
            Map<Long, String> awardMap = awardList.stream().collect(Collectors.toMap(Award::getId, Award::getName));
            return programmeAwards.stream()
                    .map(award -> {
                        ProgrammeAwardDTO dto = new ProgrammeAwardDTO(award);
                        dto.setSpecialty(artTypeMap.get(award.getSpecialtyId()));
                        dto.setAward(awardMap.get(award.getAwardId()));
                        return dto;
                    })
                    .collect(Collectors.toList());
        }

        //总节目
        List<Programme> programmeList = programmeRepo.findAllByPerformanceIdAndProgrammeStatus(performanceId, ProgrammeStatus.SUBMIT);
        List<ArtType> artTypes = all.stream()
                .filter(artType -> ObjectUtil.isNull(artType.getParent()))
                .collect(Collectors.toList());

        List<ProgrammeAwardDTO> dtos = new ArrayList<>();

        artTypes.forEach(artType -> {
            List<Long> artTypeIds = all.stream()
                    .filter(at -> at.getCode().startsWith(artType.getCode()))
                    .map(ArtType::getId)
                    .collect(Collectors.toList());
            long count = programmeList.stream()
                    .filter(programme -> artTypeIds.contains(programme.getSpecialtyId()))
                    .count();

            awardList.forEach(award -> {
                ProgrammeAwardDTO dto = new ProgrammeAwardDTO();
                dto.setSpecialtyId(artType.getId());
                dto.setSpecialty(artType.getName());
                dto.setAwardId(award.getId());
                dto.setAward(award.getName());
                dto.setPerformanceId(performanceId);
                dto.setProgrammeNum(count);
                dtos.add(dto);
            });
        });
        return dtos;
    }

    public List<ProgrammeAwardDTO> getAll1(Long performanceId) {
        //找出所有节目奖项设置
        PageQuery pageQuery = new PageQuery();
        pageQuery.setSize(100);
        pageQuery.setSort("specialtyId,asc");
        pageQuery.getQuery().put("performanceId", performanceId);
        List<ProgrammeAward> programmeAwards = this.all(pageQuery).getContent();

        // 奖项
        List<Award> awardList = awardRepo.findAll();

        //专业
        List<ArtType> all = artTypeRepo.findAll();
        Map<Long, String> artTypeMap = all
                .stream()
                .collect(Collectors.toMap(ArtType::getId, ArtType::getName));

        //已经设置完成
        if (CollUtil.isNotEmpty(programmeAwards)) {
            Map<Long, String> awardMap = awardList.stream().collect(Collectors.toMap(Award::getId, Award::getName));
            return programmeAwards.stream()
                    .map(award -> {
                        ProgrammeAwardDTO dto = new ProgrammeAwardDTO(award);
                        dto.setSpecialty(artTypeMap.get(award.getSpecialtyId()));
                        dto.setAward(awardMap.get(award.getAwardId()));
                        return dto;
                    })
                    .collect(Collectors.toList());
        }

        //总节目
        List<Programme> programmeList = programmeRepo.findAllByPerformanceIdAndProgrammeStatus(performanceId, ProgrammeStatus.SUBMIT);
        List<ArtType> artTypes = all.stream()
                .filter(artType -> ObjectUtil.isNull(artType.getParent()))
                .collect(Collectors.toList());

        List<ProgrammeAwardDTO> dtos = new ArrayList<>();

        // 节目数量
        Map<Long, Long> countProgramme = programmeList.stream()
                .collect(Collectors.groupingBy(Programme::getParentSpecialtyId, Collectors.counting()));
        artTypes.forEach(artType -> {
            Long count = countProgramme.get(artType.getId());

            awardList.forEach(award -> {
                ProgrammeAwardDTO dto = new ProgrammeAwardDTO();
                dto.setSpecialtyId(artType.getId());
                dto.setSpecialty(artType.getName());
                dto.setAwardId(award.getId());
                dto.setAward(award.getName());
                dto.setPerformanceId(performanceId);
                dto.setProgrammeNum(ObjectUtil.isNotNull(count) ? count : 0);
                dtos.add(dto);
            });
        });
        return dtos;
    }


    public List<ProgrammeAwardDTO> getAll2(Long performanceId) {
        PageQuery pageQuery = new PageQuery();
        pageQuery.setSize(100);
        pageQuery.setSort("specialtyId,asc");
        pageQuery.getQuery().put("performanceId", performanceId);
        List<ProgrammeAward> programmeAwards = this.all(pageQuery).getContent();

        List<Award> awardList = awardRepo.findAll();

        List<ArtType> artTypes = artTypeRepo.findAll();
        Map<Long, String> artTypeMap = artTypes
                .stream()
                .collect(Collectors.toMap(ArtType::getId, ArtType::getName));

        List<ProgrammeAwardDTO> dtos = new ArrayList<>();
        if (CollUtil.isNotEmpty(programmeAwards)) {
            Map<Long, List<ProgrammeAward>> longListMap = programmeAwards.stream()
                    .collect(Collectors.groupingBy(ProgrammeAward::getSpecialtyId));
            Map<Long, String> awardMap = awardList.stream().collect(Collectors.toMap(Award::getId, Award::getName));

            longListMap.forEach((k, v) -> {
                ProgrammeAwardDTO dto = new ProgrammeAwardDTO(v.get(0));
                dto.setSpecialty(artTypeMap.get(k));
                dto.setAwardDTOS(v.stream().map(award -> {
                    AwardDTO awardDTO = new AwardDTO(award);
                    awardDTO.setAward(awardMap.get(award.getAwardId()));
                    return awardDTO;
                }).collect(Collectors.toList()));
                dtos.add(dto);
            });
            return dtos;
        }

        List<Programme> programmeList = programmeRepo.findAllByPerformanceIdAndProgrammeStatus(performanceId, ProgrammeStatus.SUBMIT);
        List<ArtType> artParents = artTypes.stream()
                .filter(artType -> ObjectUtil.isNull(artType.getParent()))
                .collect(Collectors.toList());


        artParents.forEach(artType -> {

            List<Long> artTypeIds = artParents.stream()
                    .filter(at -> at.getCode().startsWith(artType.getCode()))
                    .map(ArtType::getId)
                    .collect(Collectors.toList());
            long count = programmeList.stream()
                    .filter(programme -> artTypeIds.contains(programme.getSpecialtyId()))
                    .count();
            ProgrammeAwardDTO dto = ProgrammeAwardDTO.builder()
                    .programmeNum(count)
                    .specialty(artType.getName())
                    .performanceId(performanceId)
                    .build();

            List<AwardDTO> awardDTOS = new ArrayList<>();
            awardList.forEach(award -> {
                AwardDTO awardDTO = new AwardDTO();
                awardDTO.setAward(award.getName());
                awardDTOS.add(awardDTO);
            });
            dto.setAwardDTOS(awardDTOS);
            dtos.add(dto);
        });
        return dtos;
    }

    public void saveProgramme(List<ProgrammeAwardDTO> dtos) {
        List<Programme> programmeList = programmeRepo.findAllByPerformanceIdAndProgrammeStatus(dtos.get(0)
                .getPerformanceId(), ProgrammeStatus.SUBMIT);

        List<ArtType> artTypes = artTypeRepo.findAll();
        List<ArtType> artParents = artTypes.stream()
                .filter(artType -> ObjectUtil.isNull(artType.getParent()))
                .collect(Collectors.toList());

        Map<Long, List<Programme>> map = new HashMap<>();
        //按专业分组
        artParents.forEach(artType -> {
            List<Long> artTypeIds = artTypes.stream()
                    .filter(at -> at.getCode().startsWith(artType.getCode()))
                    .map(ArtType::getId)
                    .collect(Collectors.toList());
            List<Programme> programmes = programmeList.stream()
                    .filter(programme -> artTypeIds.contains(programme.getSpecialtyId()))
                    .sorted(Comparator.comparingDouble(Programme::getScore))
                    .collect(Collectors.toList());

            map.put(artType.getId(), programmes);
        });


        //获得奖项优先级
        List<Award> awards = awardRepo.findAll();
        awards.sort(Comparator.comparingInt(Award::getLevel));

        //按专业区分再按奖项区分
        Map<Long, Map<Long, List<ProgrammeAwardDTO>>> mapMap = dtos.stream()
                .collect(Collectors.groupingBy(ProgrammeAwardDTO::getSpecialtyId, Collectors.groupingBy(ProgrammeAwardDTO::getAwardId)));


        mapMap.forEach((k, v) -> {
            int start = 0;
            for (Award award : awards) {
                ProgrammeAwardDTO pa = v.get(award.getId()).get(0);
                if (pa.getNum() > 0) {
                    List<Programme> programmes = map.get(k);
                    if (CollUtil.isNotEmpty(programmes)) {
                        List<Programme> result = programmes.subList(start, start + pa.getNum().intValue());
                        result.forEach(programme -> programme.setAwardId(award.getId()));
                        start += pa.getNum().intValue();
                    }
                }
            }
        });
    }

    public void saveProgramme1(List<ProgrammeAwardDTO> dtos) {
        List<Programme> programmeList = programmeRepo.findAllByPerformanceIdAndProgrammeStatus(dtos.get(0)
                .getPerformanceId(), ProgrammeStatus.SUBMIT);

//        long noScore = programmeList.stream().filter(programme -> ObjectUtil.isNull(programme.getScore())).count();
//        if (noScore > 0) {
//            throw new BusinessException("有节目未评分");
//        }

        Map<Long, List<Programme>> programmeMap = programmeList.stream()
                .peek(programme -> {
                    if (ObjectUtil.isNull(programme.getScore())) {
                        programme.setScore(0.0);
                    }
                })
                .sorted((a, b) -> b.getScore().compareTo(a.getScore()))
                .collect(Collectors.groupingBy(Programme::getParentSpecialtyId));

        //获得奖项优先级
        List<Award> awards = awardRepo.findAll();
        awards.sort(Comparator.comparingInt(Award::getLevel));

        //按专业区分再按奖项区分
        Map<Long, Map<Long, List<ProgrammeAwardDTO>>> mapMap = dtos.stream()
                .collect(Collectors.groupingBy(ProgrammeAwardDTO::getSpecialtyId, Collectors.groupingBy(ProgrammeAwardDTO::getAwardId)));


        mapMap.forEach((k, v) -> {
            int start = 0;
            for (Award award : awards) {
                ProgrammeAwardDTO pa = v.get(award.getId()).get(0);
                if (pa.getNum() > 0) {
                    List<Programme> programmes = programmeMap.get(k);
                    if (CollUtil.isNotEmpty(programmes)) {
                        List<Programme> result = programmes.subList(start, start + pa.getNum().intValue());
                        result.forEach(programme -> programme.setAwardId(award.getId()));
                        programmeRepo.saveAll(result);
                        start += pa.getNum().intValue();
                    }

                }
            }
        });
    }

    public void batchSave(List<ProgrammeAwardDTO> dtos) {
        List<Long> ids = dtos.stream().map(ProgrammeAwardDTO::getId).collect(Collectors.toList());
        Map<Long, ProgrammeAward> proAwardMap = programmeAwardRepo.findAllById(ids)
                .stream()
                .collect(Collectors.toMap(ProgrammeAward::getId, programmeAward -> programmeAward));
        List<ProgrammeAward> programmeAwards = dtos.stream().map(ProgrammeAward::new).collect(Collectors.toList());
        programmeAwards.forEach(programmeAward -> {
            if (ObjectUtil.isNotNull(programmeAward.getId())) {
                ProgrammeAward orig = proAwardMap.get(programmeAward.getId());
                ObjUtils.merge(orig, programmeAward);
                programmeAwardRepo.save(orig);
            } else {
                programmeAwardRepo.save(programmeAward);
            }
        });
//        programmeAwardRepo.saveAll(programmeAwards);
    }
}
