package com.izouma.wenlvju.service.performance;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.izouma.wenlvju.domain.ArtType;
import com.izouma.wenlvju.domain.User;
import com.izouma.wenlvju.domain.performance.Arrange;
import com.izouma.wenlvju.domain.performance.ArrangeJudge;
import com.izouma.wenlvju.domain.performance.Programme;
import com.izouma.wenlvju.dto.ArrangeDTO;
import com.izouma.wenlvju.dto.PageQuery;
import com.izouma.wenlvju.exception.BusinessException;
import com.izouma.wenlvju.repo.ArtTypeRepo;
import com.izouma.wenlvju.repo.UserRepo;
import com.izouma.wenlvju.repo.performance.ArrangeJudgeRepo;
import com.izouma.wenlvju.repo.performance.ArrangeRepo;
import com.izouma.wenlvju.repo.performance.ProgrammeRepo;
import com.izouma.wenlvju.utils.JpaUtils;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class ArrangeService {

    private ArrangeRepo      arrangeRepo;
    private ProgrammeService programmeService;
    private ArtTypeRepo      artTypeRepo;
    private ProgrammeRepo    programmeRepo;
    private ArrangeJudgeRepo arrangeJudgeRepo;
    private UserRepo         userRepo;

    public Page<Arrange> all(PageQuery pageQuery) {
        Map<Long, String> artMap = artTypeRepo.findAll()
                .stream()
                .collect(Collectors.toMap(ArtType::getId, ArtType::getName));
        return arrangeRepo.findAll(JpaUtils.toSpecification(pageQuery, Arrange.class), JpaUtils.toPageRequest(pageQuery))
                .map(arrange -> {
                    arrange.setSpecialtyName(arrange.getSpecialtyId()
                            .stream()
                            .map(artMap::get)
                            .collect(Collectors.toList()));
                    return arrange;
                });
    }

    public Page<Arrange> all2(PageQuery pageQuery) {
        Map<Long, String> artMap = artTypeRepo.findAll()
                .stream()
                .collect(Collectors.toMap(ArtType::getId, ArtType::getName));
        List<ArrangeJudge> ajs = arrangeJudgeRepo.findAll();
        Set<Long> userIds = ajs.stream().map(ArrangeJudge::getExpertId).collect(Collectors.toSet());
        Map<Long, List<ArrangeJudge>> judgeMap = ajs
                .stream()
                .collect(Collectors.groupingBy(ArrangeJudge::getArrangeId));
        Map<Long, String> userMap = userRepo.findAllById(userIds)
                .stream()
                .collect(Collectors.toMap(User::getId, User::getNickname));
        return arrangeRepo.findAll(JpaUtils.toSpecification(pageQuery, Arrange.class), JpaUtils.toPageRequest(pageQuery))
                .map(arrange -> {
                    arrange.setSpecialtyName(arrange.getSpecialtyId()
                            .stream()
                            .map(artMap::get)
                            .collect(Collectors.toList()));
                    List<ArrangeJudge> arrangeJudges = judgeMap.get(arrange.getId());
                    String experts = arrangeJudges.stream()
                            .map(arrangeJudge -> userMap.get(arrangeJudge.getExpertId()))
                            .collect(Collectors.joining(","));
                    arrange.setExperts(experts);
                    return arrange;
                });
    }

    /*
    分组
     */
    public void group(ArrangeDTO dto) {
        Arrange first = arrangeRepo.findFirstByPerformanceId(dto.getPerformanceId());
        if (ObjectUtil.isNotNull(first) && first.isPublish()) {
            throw new BusinessException("活动分组已发布");
        }

        long total = 0;
        int days = Period.between(dto.getStartDate(), dto.getEndDate()).getDays();
        for (int i = 0; i <= days; i++) {
            if (dto.isMorning()) {
                total += Duration.between(dto.getMorningStartTime(), dto.getMorningEndTime()).toMinutes();
            }
            if (dto.isAfternoon()) {
                total += Duration.between(dto.getAfternoonStartTime(), dto.getAfternoonEndTime()).toMinutes();
            }
        }

        int time = dto.getQuantity() * dto.getDuration();
        // 分组数量 = 总时长/节目数量*单个节目时长+10
        int group = (int) Math.ceil((double) total / (time + 10));

        LocalDate date = dto.getStartDate();
        DateTimeFormatter format = DateTimeFormatter.ofPattern("MM月dd日");
        String[] arr = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十"};
        String format1 = format.format(date);

        if (dto.isMorning() && dto.isAfternoon()) {
            this.saveArrange(dto, group, time, format1, arr);
        } else {
            LocalTime startAt;
            LocalTime endAt;
            LocalTime startTime;
            boolean morning = true;
            String amPm = "上午";

            if (dto.isMorning()) {
                startTime = dto.getMorningStartTime();
                startAt = dto.getMorningStartTime();
                endAt = dto.getMorningEndTime();
            } else if (dto.isAfternoon()) {
                startTime = dto.getAfternoonStartTime();
                morning = false;
                startAt = dto.getAfternoonStartTime();
                endAt = dto.getAfternoonEndTime();
                amPm = "下午";
            } else {
                throw new BusinessException("时间安排不能为空");
            }
            int j = 0;
            for (int i = 0; i < group; i++) {
                LocalTime endTime = startTime.plusMinutes(time);

                String name = format1 + amPm + "第" + arr[j] + "场";
                Arrange arrange = Arrange.builder()
                        .address(dto.getAddress())
                        .duration(dto.getDuration())
                        .performanceId(dto.getPerformanceId())
                        .quantity(dto.getQuantity())
                        .date(date)
                        .startTime(startTime)
                        .endTime(endTime)
                        .duration(dto.getDuration())
                        .specialtyId(dto.getSpecialtyId())
                        .name(name)
                        .morning(morning)
                        .build();

                Map<String, String> programmes = programmeService.group(arrange);
                List<Programme> programmeList = JSONObject.parseArray(programmes.get("programmes"), Programme.class);
                List<Long> ids = programmeList.stream()
                        .map(Programme::getSpecialtyId)
                        .distinct()
                        .collect(Collectors.toList());
                arrange.setSpecialtyId(ids);
                arrange.setQuantity(programmeList.size());
                arrange = arrangeRepo.save(arrange);

                programmeService.saveAll(programmeList, arrange.getId());

                // 插入节目
                if (!Boolean.parseBoolean(programmes.get("flag"))) {
                    //不需要这么多分组
                    break;
                }

                j++;
                startTime = endTime.plusMinutes(10);
                if (startTime.isAfter(endAt)) {
                    date = date.plusDays(1);
                    j = 0;
                    startTime = startAt;
                }
            }

        }

    }

    public void saveArrange(ArrangeDTO dto, int group, int time, String format1, String[] arr) {
        LocalDate date = dto.getStartDate();
        LocalTime startTime = dto.getMorningStartTime();
        boolean morning = true;
        int j = 0;
        for (int i = 0; i < group; i++) {
            LocalTime endTime = startTime.plusMinutes(time);
//            if (!endTime.isBefore(dto.getMorningEndTime())) {
//                if (Duration.between(dto.getMorningEndTime(), endTime).toMinutes() > dto.getDuration()) {
//
//                }
//            }
            String amPm = morning ? "上午" : "下午";
            String name = format1 + amPm + "第" + arr[j] + "场";
            Arrange arrange = Arrange.builder()
                    .address(dto.getAddress())
                    .duration(dto.getDuration())
                    .performanceId(dto.getPerformanceId())
                    .quantity(dto.getQuantity())
                    .date(date)
                    .startTime(startTime)
                    .endTime(endTime)
                    .duration(dto.getDuration())
                    .specialtyId(dto.getSpecialtyId())
                    .name(name)
                    .morning(morning)
                    .build();

            Map<String, String> programmes = programmeService.group(arrange);
            List<Programme> programmeList = JSONObject.parseArray(programmes.get("programmes"), Programme.class);
            List<Long> ids = programmeList.stream()
                    .map(Programme::getSpecialtyId)
                    .distinct()
                    .collect(Collectors.toList());
            arrange.setSpecialtyId(ids);
            arrange.setQuantity(programmeList.size());
            arrange = arrangeRepo.save(arrange);

            programmeService.saveAll(programmeList, arrange.getId());

            // 插入节目
            if (!Boolean.parseBoolean(programmes.get("flag"))) {
                //不需要这么多分组
                break;
            }
            j++;
            startTime = endTime.plusMinutes(10);
            if (startTime.isAfter(dto.getMorningEndTime()) && startTime.isBefore(dto.getAfternoonStartTime())) {
                startTime = dto.getAfternoonStartTime();
                j = 0;
                morning = false;
            } else if (startTime.isAfter(dto.getAfternoonEndTime())) {
                date = date.plusDays(1);
                startTime = dto.getMorningStartTime();
                j = 0;
                morning = true;
            }
        }
    }

    public void publish(Long performanceId) {
        // 分布发布
        arrangeRepo.publish(performanceId);
        Map<Long, Arrange> arranges = arrangeRepo.findAllByPerformanceId(performanceId)
                .stream()
                .collect(Collectors.toMap(Arrange::getId, arrange -> arrange));
        List<Programme> programmeList = programmeRepo.findAllByPerformanceIdAndStatus(performanceId, 1);
        Map<Long, List<Programme>> programmes = programmeList
                .stream()
                .collect(Collectors.groupingBy(Programme::getArrangeId));
        // 节目改状态，加上表演时间
        programmes.forEach((k, v) -> {
            Arrange arrange = arranges.get(k);
            LocalDate date = arrange.getDate();
            LocalTime start = arrange.getStartTime();
            int duration = arrange.getDuration();
            programmeService.sort(v);
            final LocalDateTime[] beginAt = {LocalDateTime.of(date, start)};
            v.forEach(p -> {
                LocalDateTime endAt = beginAt[0].plusMinutes(duration);
                p.setShowBegin(beginAt[0]);
                p.setShowEnd(endAt);
                p.setAddress(arrange.getAddress());
                p.setStatus(2);
                beginAt[0] = endAt;
            });
        });
        programmeRepo.saveAll(programmeList);
    }

    public Page<Arrange> byExpert(PageQuery pageQuery, Long userId) {
        List<Long> arrangeId = arrangeJudgeRepo.findArrangeIdByExpertId(userId);
        if (CollUtil.isEmpty(arrangeId)) {
            return new PageImpl<>(new ArrayList<>(), JpaUtils.toPageRequest(pageQuery), 0);
        }
        pageQuery.setSort("date,asc;startTime,asc;");
        return arrangeRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils.toPredicates(pageQuery, Arrange.class, root, criteriaQuery, criteriaBuilder);
            and.add(root.get("id").in(arrangeId));
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }), JpaUtils.toPageRequest(pageQuery));
    }

}
