package com.izouma.wenlvju.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.izouma.wenlvju.domain.Record;
import com.izouma.wenlvju.domain.RecordExpert;
import com.izouma.wenlvju.domain.RecordSpecialty;
import com.izouma.wenlvju.dto.PageQuery;
import com.izouma.wenlvju.dto.RecordDTO;
import com.izouma.wenlvju.repo.RecordExpertRepo;
import com.izouma.wenlvju.repo.RecordRepo;
import com.izouma.wenlvju.repo.RecordSpecialtyRepo;
import com.izouma.wenlvju.utils.JpaUtils;
import com.izouma.wenlvju.utils.excel.ExcelUtils;
import jodd.util.StringUtil;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import javax.transaction.Transactional;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class RecordService {

    private RecordRepo          recordRepo;
    private RecordSpecialtyRepo recordSpecialtyRepo;
    private RecordExpertRepo    recordExpertRepo;

    public Page<Record> all(PageQuery pageQuery) {
        Map<String, Object> query = pageQuery.getQuery();
        Object status = query.get("status");
        Integer statusNum = null;
        if (status != null) {
            statusNum = Convert.convert(Integer.class, status);
            query.remove("status");
        }
        Integer finalStatusNum = statusNum;
        return recordRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils.toPredicates(pageQuery, Record.class, root, criteriaQuery, criteriaBuilder);
            if (ObjectUtil.isNotNull(finalStatusNum)) {
                LocalDate now = LocalDate.now();
                switch (finalStatusNum) {
                    case 1:
                        // 未开考
                        and.add(criteriaBuilder.greaterThan(root.get("examinationStartTime"), now));
                        break;
                    case 2:
                        // 已开考
                        and.add(criteriaBuilder.lessThan(root.get("examinationEndTime"), now));
                        break;
                    case 3:
                        and.add(criteriaBuilder.lessThanOrEqualTo(root.get("examinationStartTime"), now));
                        and.add(criteriaBuilder.greaterThanOrEqualTo(root.get("examinationEndTime"), now));
                        break;
                }
            }
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }), JpaUtils.toPageRequest(pageQuery));
    }

    public Page<Record> all2(PageQuery pageQuery) {
        Map<String, Object> query = pageQuery.getQuery();

        Object examinationTime = query.get("examinationTime");
        LocalDate date = null;
        if (examinationTime != null) {
            date = Convert.toLocalDateTime(examinationTime).toLocalDate();
            query.remove("examinationTime");
        }

        Object today = query.get("history");
        LocalDate todayDate = null;
        if (today != null) {
            todayDate = Convert.toLocalDateTime(today).toLocalDate();
            query.remove("history");
        }

        LocalDate finalDate = date;
        LocalDate finalTodayDate = todayDate;
        return recordRepo.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = JpaUtils.toPredicates(pageQuery, Record.class, root, criteriaQuery, criteriaBuilder);
            if (ObjectUtil.isNotEmpty(finalDate)) {
                and.add(criteriaBuilder.lessThanOrEqualTo(root.get("examinationStartTime"), finalDate));
                and.add(criteriaBuilder.greaterThanOrEqualTo(root.get("examinationEndTime"), finalDate));
            }
            if (ObjectUtil.isNotEmpty(finalTodayDate)) {
                and.add(criteriaBuilder.lessThan(root.get("examinationEndTime"), finalTodayDate));
            }
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        }, JpaUtils.toPageRequest(pageQuery));
    }


    /*
    展示今天的所有备案
     */
    public List<Record> byToday(String district) {
        LocalDate now = LocalDate.now();
        return recordRepo.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> and = new ArrayList<>();
            if (StringUtil.isNotBlank(district)) {
                and.add(criteriaBuilder.equal(root.get("district"), district));
            }
            and.add(criteriaBuilder.lessThanOrEqualTo(root.get("examinationStartTime"), now));
            and.add(criteriaBuilder.greaterThanOrEqualTo(root.get("examinationEndTime"), now));
            return criteriaBuilder.and(and.toArray(new Predicate[0]));
        });
    }

    /*
    导出
     */
    public List<RecordDTO> excel(List<Record> data) throws IOException {
        List<Long> ids = data.stream().map(Record::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(ids)) {
            return null;
        }
        List<RecordSpecialty> specialties = recordSpecialtyRepo.findAllByRecordIdIn(ids);
        Map<Long, List<RecordSpecialty>> listMap = specialties.stream()
                .collect(Collectors.groupingBy(RecordSpecialty::getRecordId));

        List<RecordDTO> dtos = new ArrayList<>();
        long no = 1;
        for (Record record : data) {
            List<RecordSpecialty> specialtyList = listMap.get(record.getId());
            if (CollUtil.isEmpty(specialtyList)) {
                dtos.add(new RecordDTO(record, null, null, no, null));
            } else {
                long finalNo = no;
                long sno = no * 100;
                specialtyList.forEach(specialty -> {
                    List<RecordExpert> experts = recordExpertRepo.findAllByRecordSpecialtyId(specialty.getId());
                    if (CollUtil.isEmpty(experts)) {
                        dtos.add(new RecordDTO(record, specialty, null, finalNo, null));
                    } else {
                        experts.forEach(expert -> dtos.add(new RecordDTO(record, specialty, expert, finalNo, sno + 1)));
                    }
                });
            }
            no = no + 1;
        }

        return dtos;
    }

    /*
    导入
     */
    @Transactional(rollbackOn = Exception.class)
    public void upload(InputStream is) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer)) > -1) {
            baos.write(buffer, 0, len);
        }
        baos.flush();

        InputStream indicatorStream = new ByteArrayInputStream(baos.toByteArray());
        List<RecordDTO> dtos =
                ExcelUtils.readExcel(indicatorStream, RecordDTO.class, 1, 1);

        List<RecordExpert> experts = new ArrayList<>();
        long no = 0;
        long sno = 0;
        Long recordId = null;
        Long specialtyId = null;
        for (RecordDTO dto : dtos) {
            if (no != dto.getNo()) {
                Record record = new Record();
                BeanUtil.copyProperties(dto, record);
                record = recordRepo.save(record);
                recordId = record.getId();
                no = dto.getNo();
            }

            if (sno != dto.getSno()) {
                RecordSpecialty specialty = new RecordSpecialty();
                BeanUtil.copyProperties(dto, specialty);
                specialty.setRecordId(recordId);
                specialty.setExaminerQuantity(dto.getSpeExaQuantity());
                specialty = recordSpecialtyRepo.save(specialty);
                specialtyId = specialty.getId();
            }


            RecordExpert expert = new RecordExpert();
            BeanUtil.copyProperties(dto, expert);
            expert.setName(dto.getExpertName());
            expert.setRecordSpecialtyId(specialtyId);
            experts.add(expert);
        }
        recordExpertRepo.saveAll(experts);

    }

}
