package com.izouma.wenlvju.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.github.kevinsawicki.http.HttpRequest;
import com.izouma.wenlvju.config.DateConfig;
import com.izouma.wenlvju.domain.*;
import com.izouma.wenlvju.dto.PageQuery;
import com.izouma.wenlvju.dto.ReviewTime;
import com.izouma.wenlvju.enums.AuthorityName;
import com.izouma.wenlvju.enums.RateStatus;
import com.izouma.wenlvju.exception.BusinessException;
import com.izouma.wenlvju.repo.*;
import com.izouma.wenlvju.security.Authority;
import com.izouma.wenlvju.service.sms.NjwlSmsService;
import com.izouma.wenlvju.utils.JpaUtils;
import com.izouma.wenlvju.utils.ObjUtils;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.pdf.PdfWriter;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.Version;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;


@Service
@Slf4j
@AllArgsConstructor
public class RateService {

    private final RateRepo                rateRepo;
    private final CollaborateRepo         collaborateRepo;
    private final GradingOrganizationRepo gradingOrganizationRepo;
    private final RateAuditRepo           rateAuditRepo;
    private final UserRepo                userRepo;
    private final NjwlSmsService          njwlSmsService;
    private final OrganizationRepo        organizationRepo;

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

    public Rate save(Rate record) {
        LocalDateTime now = LocalDateTime.now();
        String district = record.getDistrict();
        if (record.getId() != null) {
            Rate orig = rateRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
            boolean examination = orig.isUndertakeExamination();
            ObjUtils.merge(orig, record);

            switch (record.getStatus()) {
                case FIRST_REVIEW_PENDING: // 初审中 1
                    orig.setApplyTime(now);
                    //发送短信给地区考级管理人员
                    Map<String, String> applyMap = this.applyMessage(district, now);
                    njwlSmsService.sendSms(applyMap.get("phone"), applyMap.get("message"));
                    break;
                case REVIEW_PENDING: // 分配专家组 3
                    // 发送给专家组长和组员
                    if (ObjectUtil.isEmpty(orig.getExpertUserId()) || CollUtil.isEmpty(orig.getExpertMemberUserId())) {
                        throw new BusinessException("专家组长或组员不能为空");
                    }
                    Map<String, String> map = this.reviewTimesMessage(orig);
                    njwlSmsService.sendSms(map.get("phone"), map.get("message"));
                    njwlSmsService.sendSms(map.get("phone1"), map.get("message1"));
                    break;
                case SUBMIT_GRADE:// 专家组提交成绩 4
                    orig.setUndertakeExamination(examination);
                    RateAudit rateAudit = RateAudit.builder()
                            .userId(orig.getExpertUserId())
                            .rateId(orig.getId())
                            .remark("专家组已线下考察完毕")
                            .status(RateStatus.SUBMIT_GRADE)
                            .build();
                    rateAuditRepo.save(rateAudit);
                    Map<String, String> gradeMessage = this.gradeMessage(now, orig.getExpertUserId(), orig
                            .getName());
                    njwlSmsService.sendSms(gradeMessage.get("phone"), gradeMessage.get("message"));
                    break;
                default:
                    break;
            }
            orig.setSort(orig.getStatus().getSort());
            return rateRepo.save(orig);

        }

        String year = String.valueOf(LocalDate.now().getYear());
        if (rateRepo.countAllByOrganizationIdAndYearAndStatusNot(record.getOrganizationId(), year, RateStatus.CANCEL) > 0) {
            throw new BusinessException("已申请");
        }
        record.setYear(year);
        if (record.isSubmit() && RateStatus.FIRST_REVIEW_PENDING.equals(record.getStatus())) {
            record.setApplyTime(now);
            //发送短信给地区考级管理人员
            Map<String, String> map = this.applyMessage(district, now);
            njwlSmsService.sendSms(map.get("phone"), map.get("message"));
        }
        record.setSort(record.getStatus().getSort());
        return rateRepo.save(record);
    }

    /*
    线下退回
     */
    public void offline(Long id, Long userId, RateStatus status) {
        Rate rate = rateRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        rate.setStatus(status);
        rate.setSort(rate.getStatus().getSort());
        rateRepo.save(rate);
        RateAudit rateAudit = RateAudit.builder()
                .userId(userId)
                .rateId(id)
                .remark(RateStatus.REVIEW_PENDING.equals(status) ? "线下考察材料提交不全，被退回！" : "已取消专家组分配！")
                .status(status)
                .build();
        rateAuditRepo.save(rateAudit);
    }

    public void audit(Long id, RateStatus status, String remark, Long userId) {
        Rate rate = rateRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        rate.setStatus(status);
        if (RateStatus.REVIEW_DENY.equals(status)) {
            // 市里退回
            rate.setRejected(true);
            rate.setRejectedAt(LocalDateTime.now());
        }

        RateAudit rateAudit = RateAudit.builder()
                .userId(userId)
                .rateId(id)
                .remark(remark)
                .status(status)
                .build();
        rateAuditRepo.save(rateAudit);
        rate.setSort(rate.getStatus().getSort());
        rateRepo.save(rate);
        switch (status) {
            case FIRST_REVIEW_DENY:
                // 发送短信给承办单位
                njwlSmsService.sendSms(rate.getOwnerPhone(), "你单位等级评定申请被" + rate.getDistrict() + "文化和旅游局退回，请根据反馈意见及时处理！");
                break;
            case ASSIGN_EXPERT:
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT);
                String phone1 = userRepo.findAllByAuthoritiesContainsAndDelFalse(Authority.get(AuthorityName.ROLE_ADMIN))
                        .stream()
                        .map(User::getPhone)
                        .collect(Collectors.joining(","));
                String message1 = "您于" + dtf.format(LocalDateTime.now()) + "，收到" + rate.getDistrict() + "文化和旅游局上报的承办单位等级评定申请。";
                njwlSmsService.sendSms(phone1, message1);
                break;
            case REVIEW_DENY:
                String phone = userRepo.findAllByDistrictAndAuthoritiesContainsAndDelFalse(rate.getDistrict(), Authority
                                .get(AuthorityName.ROLE_DISTRICT_STAFF))
                        .stream()
                        .map(User::getPhone)
                        .collect(Collectors.joining(","));
                String message = "你区上报的承办单位等级评定申请被市文化和旅游局等级评定委员会退回，请根据反馈意见及时处理！";
                njwlSmsService.sendSms(phone, message);
                break;
            default:
                break;
        }
    }

    public Rate saveReviewTime(Long id, LocalDateTime time, Long userId) {
        Rate rate = rateRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        rate.setReviewTime(time);
        rate.getReviewTimes().add(ReviewTime.builder()
                .reviewTime(time)
                .operatingTime(LocalDateTime.now())
                .build());

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT);
        RateAudit rateAudit = RateAudit.builder()
                .userId(userId)
                .rateId(id)
                .remark("专家组长设置线下审查时间为：" + formatter.format(time))
                .status(RateStatus.REVIEW_PENDING)
                .build();
        rateAuditRepo.save(rateAudit);
        rate.setSort(RateStatus.REVIEW_PENDING.getSort());
        rate = rateRepo.save(rate);

        // 发送短信 确定审查时间
        if (ObjectUtil.isNull(rate.getReviewTime())) {
            throw new BusinessException("未设置具体审查时间");
        }
        Map<String, String> body = this.reviewTimeMessage(rate);
        njwlSmsService.sendSms(body.get("phone"), body.get("message"));
        njwlSmsService.sendSms(body.get("phone1"), body.get("message1"));
        return rate;
    }

    /*
    提交纸质材料
    */
    public void paperMaterial(Long id, RateStatus status, String remark, Long userId) {
        Rate rate = rateRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        rate.setStatus(status);
        rate.setSort(status.getSort());
        rateRepo.save(rate);
        RateAudit rateAudit = RateAudit.builder()
                .userId(userId)
                .rateId(id)
                .remark(remark)
                .status(status)
                .build();
        rateAuditRepo.save(rateAudit);
        if (RateStatus.COMPLETE.equals(status)) {
            njwlSmsService.sendSms(rate.getOwnerPhone(), "等级评定结果公告已发布，请在南京市文化和旅游局官方网站（http://wlj.nanjing.gov.cn/）上查询。");
        }
    }

    public String export(Rate rate) {
        Map<String, Object> dataMap = new HashMap<>();
        try {
            //单位
            dataMap.put("name", rate.getName());
            //日期
            dataMap.put("date", DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_FORMAT)
                    .format(rate.getCreatedAt()));
            //负责人
            dataMap.put("owner", rate.getOwner());
            //电话
            dataMap.put("phone", rate.getOwnerPhone());
            //邮箱
            dataMap.put("email", rate.getOwnerEmail());
            //承办过
            dataMap.put("undertake", rate.isUndertakeExamination() ? "是" : "否");
            //考级机构名称
            Set<Long> ids = collaborateRepo.findAllByRateId(rate.getId())
                    .stream()
                    .map(Collaborate::getGradingOrganizationId)
                    .collect(Collectors.toSet());
            if (CollUtil.isNotEmpty(ids)) {
                String str = gradingOrganizationRepo.findAllById(ids)
                        .stream()
                        .map(GradingOrganization::getName)
                        .collect(Collectors.joining(","));
                dataMap.put("examination", str);
            }

            //单位概况
            dataMap.put("introduction", rate.getIntroduction());
            //Configuration 用于读取ftl文件
            Configuration configuration = new Configuration(new Version("2.3.0"));

            configuration.setDefaultEncoding("utf-8");
            configuration.setClassForTemplateLoading(this.getClass(), "/templates");//指定ftl所在目录,根据自己的改
            StringWriter writer = new StringWriter();
            Template template = configuration.getTemplate("RateTemplate.ftl", "utf-8");//以utf-8的编码读取ftl文件
            template.process(dataMap, writer);
            return writer.toString();
        } catch (Exception e) {
            log.error("生成word错误", e);
            throw new BusinessException(e.getMessage());
        }
    }

    public void imgToPdf(List<InputStream> files, Rate rate) {
        List<String> imageUrllist = new ArrayList<>();

        this.addList(imageUrllist, rate.getIntroductionAnnex());
        this.addList(imageUrllist, rate.getPrivacyPolicy());
        this.addList(imageUrllist, rate.getBusiness());
        this.addList(imageUrllist, rate.getCredits());
        this.addList(imageUrllist, rate.getFire());
        this.addList(imageUrllist, rate.getHygiene());
        this.addList(imageUrllist, rate.getFinance());
        this.addList(imageUrllist, rate.getProperty());

        String filename = "img.pdf";

        Document doc = new Document(PageSize.A4, 20, 20, 20, 20);
        try {
            FileOutputStream os = new FileOutputStream(filename);
            PdfWriter.getInstance(doc, os);

            doc.open();
            for (String img : imageUrllist) {
                doc.newPage();
                Image png1 = Image.getInstance(img);
//                float heigth = png1.getHeight();
                float width = png1.getWidth();
                int percent = this.getPercent(width);

                png1.setAlignment(Image.MIDDLE);
                png1.scalePercent(percent + 3);// 表示是原来图像的比例;
                doc.add(png1);
            }
//            b = os.toByteArray();

            File file = new File(filename);
            InputStream is = new FileInputStream(file);
            files.add(is);

        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        } finally {
            doc.close();
        }
    }

    public void addList(List<String> list, List<String> imgs) {
        imgs.forEach(img -> {
            if ("png".equalsIgnoreCase(this.getSuffix(img)) || "jpg".equalsIgnoreCase(this.getSuffix(img))
                    || "jpeg".equalsIgnoreCase(this.getSuffix(img))) {
                list.add(img);
            }
        });
    }

    /**
     * 等比压缩，获取压缩百分比
     *
     * @param weight 图片的宽度
     * @return 压缩百分比
     */
    public int getPercent(float weight) {
        int p;
        float p2;
        p2 = 530 / weight * 100;
        p = Math.round(p2);
        return p;
    }

    public List<InputStream> upLoad1(Rate rate) throws UnsupportedEncodingException {
        List<InputStream> files = new ArrayList<>();
        String uri = "http://convert.izouma.com/word2pdf";

        // 表格
        String export = this.export(rate);
        InputStream is = new ByteArrayInputStream(export.getBytes());
        InputStream stream = HttpRequest.post(uri)
                .accept("*/*")
                .part("file", "审核材料.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", is)
                .stream();
        files.add(stream);
        this.downloadFile1(files, rate.getIntroductionAnnex());
        this.downloadFile1(files, rate.getPrivacyPolicy());
        this.downloadFile1(files, rate.getBusiness());
        this.downloadFile1(files, rate.getCredits());
        this.downloadFile1(files, rate.getFire());
        this.downloadFile1(files, rate.getHygiene());
        this.downloadFile1(files, rate.getFinance());
        this.downloadFile1(files, rate.getProperty());

        this.imgToPdf(files, rate);

        return files;
    }

    public void downloadFile1(List<InputStream> files, List<String> urls) {
        AtomicInteger num = new AtomicInteger(1);
        urls.forEach(privacy -> {
            num.getAndIncrement();
            if ("doc".equals(getSuffix(privacy)) || "docx".equals(getSuffix(privacy))) {
                InputStream is1 = HttpRequest.get(privacy)
                        .stream();
                InputStream stream = HttpRequest.post("http://convert.izouma.com/word2pdf")
                        .accept("*/*")
                        .part("file", "审核材料.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", is1)
                        .stream();
                files.add(stream);
            } else if ("pdf".equals(getSuffix(privacy))) {
                InputStream stream = HttpRequest.get(privacy).stream();
                files.add(stream);
            }
        });
    }

    public List<File> upLoad(Rate rate) {
        List<File> files = new ArrayList<>();
        String uri = "http://convert.izouma.com/word2pdf";
        // 表格
        String export = this.export(rate);
        InputStream is = new ByteArrayInputStream(export.getBytes());
        File file = new File("/Users/qiufangchao/Desktop/" + "申请表" + ".pdf");
        HttpRequest.post(uri)
                .accept("*/*")
                .part("file", "审核材料.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", is)
                .receive(file);
        files.add(file);
        this.downloadFile(files, rate.getPrivacyPolicy(), "法人资格");
        return files;
    }

    public void downloadFile(List<File> files, List<String> urls, String filename) {
        AtomicInteger num = new AtomicInteger(1);
        urls.forEach(privacy -> {
            num.getAndIncrement();
            File file1 = new File("/Users/qiufangchao/Desktop/" + filename + num + ".pdf");
            if ("doc".equals(getSuffix(privacy)) || "docx".equals(getSuffix(privacy))) {
                InputStream is1 = HttpRequest.get(privacy).stream();
                HttpRequest.post("http://convert.izouma.com/word2pdf")
                        .accept("*/*")
                        .part("file", "审核材料.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", is1)
                        .receive(file1);
                files.add(file1);
            } else if ("pdf".equals(getSuffix(privacy))) {
                HttpRequest.get(privacy).receive(file1);
                files.add(file1);
            }
        });
    }

    public String getSuffix(String url) {
        int index = url.lastIndexOf(".");
        if (index < 0) {
            return "";
        }
        return url.substring(index + 1);
    }

    public void exportPdf(Long id, HttpServletResponse response) throws IOException {
        Rate rate = rateRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        List<InputStream> files = this.upLoad1(rate);


        // pdf合并工具类
        PDFMergerUtility mergePdf = new PDFMergerUtility();
        mergePdf.addSources(files);

        // 指定目标文件输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        mergePdf.setDestinationStream(outputStream);

        mergePdf.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());

        // 设置合并生成pdf文件名称
//        String targetPath = "/Users/qiufangchao/Desktop/result.pdf";
//        mergePdf.setDestinationFileName(targetPath);

        for (InputStream is : files) {
            is.close();
        }

        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=" + "result.pdf");

        ServletOutputStream sos = response.getOutputStream();
        sos.write(outputStream.toByteArray());

        sos.flush();
        outputStream.close();
    }

    public Map<String, String> reviewTimesMessage(Rate rate) {
        List<Long> ids = new ArrayList<>(rate.getExpertMemberUserId());
        ids.add(rate.getExpertUserId());
        Map<Long, User> userMap = userRepo.findAllById(ids)
                .stream()
                .collect(Collectors.toMap(User::getId, user -> user));
        if (!rate.isChange()) {
            Organization organization = organizationRepo.findById(rate.getId()).orElseThrow(new BusinessException("无记录"));
            rate.setAddress(organization.getAddress());
        }

        String message = "现场检查计划已分配：\n" +
                "审核单位：%s\n" +
                "单位地址：%s\n" +
                "单位负责人：%s\n" +
                "电话：%s\n" +
                "检查日期：%s\n" +
                "组长：%s-%s\n" +
                "组员：%s\n" +
                "宁艺通入口地址：http://wlj.izouma.com/h5/login\n" +
                "请在指定日期内完成现场检查任务，谢谢！";
        String message1 = "现场检查计划已分配：\n" +
                "审核单位：%s\n" +
                "单位地址：%s\n" +
                "检查日期：%s\n" +
                "组长：%s-%s";

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT);

        User user = userMap.get(rate.getExpertUserId());
        StringBuilder sb = new StringBuilder();
        rate.getExpertMemberUserId().forEach(userId -> {
            User user1 = userMap.get(userId);
            sb.append(user1.getNickname()).append("-").append(user1.getPhone()).append("、");
            System.out.println(sb);
        });
        String str = "";
        if (StrUtil.isNotEmpty(sb)) {
            str = sb.substring(0, sb.length() - 1);
        }

        String date = dtf.format(rate.getReviewStartTime()) + " 至 " + dtf.format(rate.getReviewEndTime());

        String body = String.format(message, rate.getName(), rate.getDetailAddress(), rate.getOwner(), rate.getOwnerPhone(), date, user
                .getNickname(), user.getPhone(), str);

        String body1 = String.format(message1, rate.getName(), rate.getDetailAddress(), date, user
                .getNickname(), user.getPhone());

        String phone = rate.getExpertMemberUserId()
                .stream()
                .map(userId -> userMap.get(userId).getPhone())
                .collect(Collectors.joining(","));

        Map<String, String> map = new HashMap<>();
        map.put("message", body);
        map.put("phone", user.getPhone());
        map.put("message1", body1);
        map.put("phone1", phone);
        return map;
    }

    public Map<String, String> reviewTimeMessage(Rate rate) {
//        Rate rate = rateRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        List<Long> ids = new ArrayList<>(rate.getExpertMemberUserId());
        ids.add(rate.getExpertUserId());
        Map<Long, User> userMap = userRepo.findAllById(ids)
                .stream()
                .collect(Collectors.toMap(User::getId, user -> user));

        String message = "现场检查时间已确定：\n" +
                "审核单位：%s\n" +
                "单位地址：%s\n" +
                "检查时间：%s\n" +
                "单位负责人：%s\n" +
                "电话：%s\n" +
                "请在确定的时间内完成现场检查任务，谢谢！";
        String message1 = "现场检查时间已确定：\n" +
                "组长：%s-%s\n" +
                "检查时间：%s\n" +
                "请主动与检查组联系，配合做好承办单位等级评定工作！";

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT);

        String body = String.format(message, rate.getName(), rate.getDetailAddress(), dtf.format(rate.getReviewTime()), rate
                .getOwner(), rate.getOwnerPhone());

        User user = userMap.get(rate.getExpertUserId());
        String body1 = String.format(message1, user.getNickname(), user.getPhone(), dtf.format(rate.getReviewTime()));


        String phone = rate.getExpertMemberUserId()
                .stream()
                .map(userId -> userMap.get(userId).getPhone())
                .collect(Collectors.joining(","));
        phone += "," + user.getPhone();

        Map<String, String> map = new HashMap<>();
        map.put("message", body);
        map.put("phone", phone);
        map.put("message1", body1);
        map.put("phone1", rate.getOwnerPhone());
        return map;
    }

    public Map<String, String> gradeMessage(LocalDateTime time, Long expertId, String name) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT);
        String phone = userRepo.findAllByAuthoritiesContainsAndDelFalse(Authority.get(AuthorityName.ROLE_ADMIN))
                .stream()
                .map(User::getPhone)
                .collect(Collectors.joining(","));

        User expert = userRepo.findById(expertId).orElseThrow(new BusinessException("无专家"));

        String body = "现场检查已完成：\n" +
                "单位名称：%s\n" +
                "完成时间：%s \n" +
                "组长 ： %s-%s\n";
        String message = String.format(body, name, dtf.format(time), expert.getNickname(), expert.getPhone());
        Map<String, String> map = new HashMap<>();
        map.put("message", message);
        map.put("phone", phone);
        return map;
    }

    public Map<String, String> applyMessage(String district, LocalDateTime time) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT);
        String phone = userRepo.findAllByDistrictAndAuthoritiesContainsAndDelFalse(district, Authority.get(AuthorityName.ROLE_DISTRICT_STAFF))
                .stream()
                .map(User::getPhone)
                .collect(Collectors.joining(","));

        String message = "您于" + dtf.format(time) + "，收到新的承办单位等级评定申请，请在15天内办结。";

        Map<String, String> map = new HashMap<>();
        map.put("message", message);
        map.put("phone", phone);
        return map;
    }
}
