package com.ningdatech.pmapi.meeting.manage;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.common.model.FreemarkerBatchExportDTO;
import com.ningdatech.pmapi.common.util.FreemarkerWordUtil;
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import com.ningdatech.pmapi.expert.manage.ExpertReviewManage;
import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingInnerProject;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingOuterProject;
import com.ningdatech.pmapi.meeting.entity.dto.*;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingInnerProjectService;
import com.ningdatech.pmapi.meeting.service.IMeetingOuterProjectService;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
import com.ningdatech.pmapi.projectlib.model.entity.Project;
import com.ningdatech.pmapi.projectlib.service.IProjectService;
import com.ningdatech.pmapi.sms.utils.DateUtil;
import freemarker.template.TemplateException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * <p>
 * ExpertExportManage
 * </p>
 *
 * @author WendyYang
 * @since 2023/7/24
 **/
@Slf4j
@Component
@RequiredArgsConstructor
public class ExpertExportManage {

    private final IMeetingService meetingService;
    private final IMeetingExpertService meetingExpertService;
    private final IMeetingInnerProjectService meetingInnerProjectService;
    private final IMeetingOuterProjectService meetingOuterProjectService;
    private final IExpertUserFullInfoService expertUserInfoService;
    private final IProjectService projectService;
    private final ExpertReviewManage expertReviewManage;

    //==================================================================================================================

    private static final String EXPERT_REVIEW = "meeting/专家评审单.ftl";

    private static final String EXPERT_FEE = "meeting/专家费.ftl";

    private static final String LABOR_FEE = "meeting/劳务费发放.ftl";

    private static final String EXPERT_INVITE = "meeting/专家抽取表.ftl";

    //==================================================================================================================
    static Function<Integer, String> meetingUsageTime = (type) -> {
        if (1 == type) {
            return "半天";
        }
        if (2 == type) {
            return "一天";
        }
        return StrUtil.EMPTY;
    };

    static Supplier<String> wordName = () -> UUID.randomUUID().toString(true) + ".doc";

    private List<MeetingExpert> listExpertsByAgreeOrLeave(Long meetingId) {
        Page<MeetingExpert> page = Page.of(1, 20);
        meetingExpertService.pageExpertByStatusAndMeetingId(page, meetingId, null, null);
        if (page.getTotal() == 0) {
            return Collections.emptyList();
        }
        page.getRecords().removeIf(w -> !(ExpertAttendStatusEnum.AGREED.eq(w.getStatus()) || ExpertAttendStatusEnum.ON_LEAVE.eq(w.getStatus())));
        return page.getRecords();
    }

    //==================================================================================================================

    public void exportFeeForExpert(Long meetingId, HttpServletResponse response) {
        try {
            Meeting meeting = meetingService.getById(meetingId);
            ExpertFeeExportDTO data = new ExpertFeeExportDTO();
            data.setMeetingTime(DateUtil.localDateTimeFormat(meeting.getStartTime(), "yyyy-MM-dd HH:mm"));
            data.setMeetingAddress(meeting.getMeetingAddress());
            List<String> projectNames = new ArrayList<>();
            if (meeting.getIsInnerProject()) {
                List<MeetingInnerProject> inners = meetingInnerProjectService.listByMeetingId(meetingId);
                List<Long> projectIds = CollUtils.fieldList(inners, MeetingInnerProject::getProjectId);
                List<Project> projects = projectService.listByIds(projectIds);
                projects.forEach(w -> projectNames.add(w.getProjectName()));
            } else {
                List<MeetingOuterProject> inners = meetingOuterProjectService.listByMeetingId(meetingId);
                inners.forEach(w -> projectNames.add(w.getProjectName()));
            }
            data.setProjectName(StrUtil.join("、", projectNames));
            data.setExperts(new ArrayList<>());
            // 设置专家信息
            List<MeetingExpert> experts = meetingExpertService.listAgreedExperts(meetingId);
            List<Long> expertIds = CollUtils.fieldList(experts, MeetingExpert::getExpertId);
            List<ExpertUserFullInfo> expertInfos = expertUserInfoService.listByUserId(expertIds);
            Map<Long, ExpertUserFullInfo> expertMap = CollUtils.listToMap(expertInfos, ExpertUserFullInfo::getUserId);
            AtomicInteger integer = new AtomicInteger(0);
            experts.forEach(w -> {
                ExpertInfoDTO expert = new ExpertInfoDTO();
                expert.setNo(integer.incrementAndGet());
                expert.setFee(BigDecimal.valueOf(500));
                expert.setName(w.getExpertName());
                expert.setMobile(w.getMobile());
                ExpertUserFullInfo expertUser = expertMap.get(w.getExpertId());
                if (expertUser != null) {
                    expert.setIdcard(expertUser.getIdCard());
                    expert.setBankNo(expertUser.getBankNo());
                    expert.setBank(expertUser.getBank());
                    expert.setCompany(expertUser.getCompany());
                    expert.setSex(expertUser.getGender());
                }
                data.getExperts().add(expert);
            });
            FreemarkerWordUtil.export(wordName.get(), data, response, EXPERT_FEE);
        } catch (TemplateException | IOException e) {
            log.error("专家费用单导出异常：{}", meetingId, e);
            throw BizException.wrap("专家费用单导出失败");
        }
    }

    public void expertReviewTable(Long meetingId, HttpServletResponse response) {
        try {
            Meeting meeting = meetingService.getById(meetingId);
            String startTime = DateUtil.localDateTimeFormat(meeting.getStartTime(), "yyyy年M月d日");
            List<FreemarkerBatchExportDTO> fbeList;
            if (meeting.getIsInnerProject()) {
                List<MeetingInnerProject> inners = meetingInnerProjectService.listByMeetingId(meetingId);
                Map<Long, String> reviewResultMap = expertReviewManage.buildExpertReviewToStr(meetingId);
                List<Long> projectIds = CollUtils.fieldList(inners, MeetingInnerProject::getProjectId);
                List<Project> projects = projectService.listByIds(projectIds);
                fbeList = projects.stream().map(w -> {
                    FreemarkerBatchExportDTO fbe = new FreemarkerBatchExportDTO();
                    ExpertReviewTableDTO currData = new ExpertReviewTableDTO();
                    currData.setHoldOrg(meeting.getHoldOrg());
                    currData.setMeetingTime(startTime);
                    currData.setMeetingAddress(meeting.getMeetingAddress());
                    currData.setProjectName(w.getProjectName());
                    currData.setReviewResult(reviewResultMap.get(w.getId()));
                    fbe.setData(currData);
                    fbe.setTemplate(EXPERT_REVIEW);
                    fbe.setFileName(w.getProjectName() + ".doc");
                    return fbe;
                }).collect(Collectors.toList());
            } else {
                List<MeetingOuterProject> inners = meetingOuterProjectService.listByMeetingId(meetingId);
                fbeList = inners.stream().map(w -> {
                    FreemarkerBatchExportDTO fbe = new FreemarkerBatchExportDTO();
                    ExpertReviewTableDTO currData = new ExpertReviewTableDTO();
                    currData.setHoldOrg(meeting.getHoldOrg());
                    currData.setMeetingTime(startTime);
                    currData.setMeetingAddress(meeting.getMeetingAddress());
                    currData.setProjectName(w.getProjectName());
                    fbe.setData(currData);
                    fbe.setTemplate(EXPERT_REVIEW);
                    fbe.setFileName(w.getProjectName() + ".doc");
                    return fbe;
                }).collect(Collectors.toList());
            }
            String exportFileName = meeting.getName() + "专家评审表.zip";
            FreemarkerWordUtil.exportBatch(response, fbeList, exportFileName);
        } catch (TemplateException | IOException e) {
            log.error("专家评审单导出异常：{}", meetingId, e);
            throw BizException.wrap("专家评审单导出失败");
        }
    }

    public void laborFeeAuditTable(Long meetingId, HttpServletResponse response) {
        try {
            Meeting meeting = meetingService.getById(meetingId);
            LaborFeeAuditTableDTO data = new LaborFeeAuditTableDTO();
            data.setMeetingTime(DateUtil.localDateTimeFormat(meeting.getStartTime(), "yyyy-MM-dd HH:mm"));
            data.setMeetingUsageTime(meetingUsageTime.apply(meeting.getMeetingUsageTime()));
            List<String> projectNames = new ArrayList<>();
            if (meeting.getIsInnerProject()) {
                List<MeetingInnerProject> inners = meetingInnerProjectService.listByMeetingId(meetingId);
                List<Long> projectIds = CollUtils.fieldList(inners, MeetingInnerProject::getProjectId);
                List<Project> projects = projectService.listByIds(projectIds);
                projects.forEach(w -> projectNames.add(w.getProjectName()));
            } else {
                List<MeetingOuterProject> inners = meetingOuterProjectService.listByMeetingId(meetingId);
                inners.forEach(w -> projectNames.add(w.getProjectName()));
            }
            data.setProjectName(StrUtil.join("、", projectNames));
            data.setExperts(new ArrayList<>());
            // 设置专家信息
            List<MeetingExpert> experts = listExpertsByAgreeOrLeave(meetingId);
            List<Long> expertIds = CollUtils.fieldList(experts, MeetingExpert::getExpertId);
            List<ExpertUserFullInfo> expertInfos = expertUserInfoService.listByUserId(expertIds);
            Map<Long, ExpertUserFullInfo> expertMap = CollUtils.listToMap(expertInfos, ExpertUserFullInfo::getUserId);
            experts.forEach(w -> {
                ExpertInfoDTO expert = new ExpertInfoDTO();
                if (ExpertAttendStatusEnum.ON_LEAVE.eq(w.getStatus())) {
                    expert.setBank(w.getExpertName() + "（请假）");
                } else {
                    expert.setName(w.getExpertName());
                }
                ExpertUserFullInfo expertUser = expertMap.get(w.getExpertId());
                if (expertUser != null) {
                    expert.setCompany(expertUser.getCompany());
                    expert.setJob(expertUser.getAdministrativeDuties());
                }
                data.getExperts().add(expert);
            });
            data.setExpertCount(experts.size());
            FreemarkerWordUtil.export(wordName.get(), data, response, LABOR_FEE);
        } catch (TemplateException | IOException e) {
            log.error("劳务费发放审批单导出异常：{}", meetingId, e);
            throw BizException.wrap("劳务费发放审批单导出失败");
        }
    }

    public void expertInviteTable(Long meetingId, HttpServletResponse response) {
        try {
            Meeting meeting = meetingService.getById(meetingId);
            ExpertInviteTableDTO data = new ExpertInviteTableDTO();
            data.setMeetingTime(DateUtil.localDateTimeFormat(meeting.getStartTime(), "yyyy-MM-dd HH:mm"));
            data.setMeetingUsageTime(meetingUsageTime.apply(meeting.getMeetingUsageTime()));
            data.setMeetingCreateBy(meeting.getCreator());
            data.setMeetingAddress(meeting.getMeetingAddress());
            if (meeting.getIsInnerProject()) {
                List<MeetingInnerProject> inners = meetingInnerProjectService.listByMeetingId(meetingId);
                Assert.isTrue(inners.size() == 1, "专家抽取表导出失败");
                Project project = projectService.getById(inners.get(0).getProjectId());
                data.setProjectCode(project.getProjectCode());
                data.setProjectName(project.getProjectName());
                data.setBuildOrg(project.getBuildOrgName());
            } else {
                List<MeetingOuterProject> inners = meetingOuterProjectService.listByMeetingId(meetingId);
                Assert.isTrue(inners.size() == 1, "专家抽取表导出失败");
                data.setProjectName(inners.get(0).getProjectName());
                data.setProjectCode(StrUtil.EMPTY);
                data.setBuildOrg(StrUtil.EMPTY);
            }
            data.setExperts(new ArrayList<>());
            // 设置专家信息
            List<MeetingExpert> experts = listExpertsByAgreeOrLeave(meetingId);
            List<Long> expertIds = CollUtils.fieldList(experts, MeetingExpert::getExpertId);
            List<ExpertUserFullInfo> expertInfos = expertUserInfoService.listByUserId(expertIds);
            Map<Long, ExpertUserFullInfo> expertMap = CollUtils.listToMap(expertInfos, ExpertUserFullInfo::getUserId);
            AtomicInteger integer = new AtomicInteger(0);
            experts.forEach(w -> {
                ExpertInfoDTO expert = new ExpertInfoDTO();
                expert.setNo(integer.incrementAndGet());
                if (ExpertAttendStatusEnum.ON_LEAVE.eq(w.getStatus())) {
                    expert.setBank(w.getExpertName() + "（请假）");
                } else {
                    expert.setName(w.getExpertName());
                }
                expert.setMobile(w.getMobile());
                ExpertUserFullInfo expertUser = expertMap.get(w.getExpertId());
                if (expertUser != null) {
                    expert.setCompany(expertUser.getCompany());
                }
                data.getExperts().add(expert);
            });
            FreemarkerWordUtil.export(wordName.get(), data, response, EXPERT_INVITE);
        } catch (Exception e) {
            log.error("专家抽取表导出异常：{}", meetingId, e);
            throw BizException.wrap("专家抽取表导出失败");
        }
    }

}
