package com.microservices.pms.project.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.HttpStatus;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.httpClient.util.GitLinkRequestHelper;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.pms.common.service.IPmsCommonService;
import com.microservices.pms.product.mapper.PmsProductModuleMapper;
import com.microservices.pms.project.domain.PmsProjectModuleStatistics;
import com.microservices.pms.project.domain.PmsProjectTestReport;
import com.microservices.pms.project.domain.PmsProjectTestReportSheet;
import com.microservices.pms.project.domain.PmsProjectTestsheetCases;
import com.microservices.pms.project.domain.vo.*;
import com.microservices.pms.project.mapper.*;
import com.microservices.pms.project.service.*;
import com.microservices.pms.utils.PmsGitLinkRequestUrl;
import com.microservices.pms.utils.PmsUtils;
import com.microservices.system.api.domain.SimpleSysUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.net.URISyntaxException;
import java.util.*;
import java.util.stream.Collectors;

import static com.microservices.common.core.utils.PageUtils.startPage;
import static com.microservices.common.core.utils.PageUtils.toPage;
import static com.microservices.pms.utils.PmsConstants.ISSUE_FILTER_TYPE_ALL;
import static com.microservices.pms.utils.PmsConstants.PROJECT_ISSUE_TYPE_BUG;

/**
 * 测试报告Service业务层处理
 * 
 * @author wanjia
 * @date 2023-12-14
 */
@Service
public class PmsProjectTestReportServiceImpl implements IPmsProjectTestReportService 
{
    private static final Logger logger = LoggerFactory.getLogger(PmsProjectTestReportServiceImpl.class);

    @Autowired
    private PmsProjectTestReportMapper pmsProjectTestReportMapper;

    @Resource
    private PmsProjectTestReportSheetMapper pmsProjectTestReportSheetMapper;

    @Autowired
    private IPmsProjectTestsheetService pmsProjectTestsheetService;

    @Autowired
    private IPmsProjectService pmsProjectService;

    @Autowired
    private IPmsProjectSprintService pmsProjectSprintService;

    @Resource
    private PmsProjectTestsheetIssuesMapper pmsProjectTestsheetIssuesMapper;

    @Resource
    private PmsProjectTestcaseMapper pmsProjectTestcaseMapper;

    @Resource
    private PmsProductModuleMapper pmsProductModuleMapper;

    @Autowired
    private GitLinkRequestHelper gitLinkRequestHelper;

    @Resource
    private PmsProjectTestsheetCasesMapper pmsProjectTestsheetCasesMapper;

    @Autowired
    private IPmsCommonService pmsCommonService;

    @Autowired
    private PmsProjectIssuesService pmsProjectIssuesService;

    /**
     * 查询测试报告
     * 
     * @param id 测试报告主键
     * @return 测试报告
     */
    @Override
    public PmsProjectTestReportDetailVo selectPmsProjectTestReportDetailById(Long id)
    {
        PmsProjectTestReport pmsProjectTestReport = selectPmsProjectTestReportById(id);
        return getPmsProjectTestReportDetailVo(pmsProjectTestReport);
    }

    public PmsProjectTestReport selectPmsProjectTestReportById(Long id) {
        PmsProjectTestReport pmsProjectTestReport = pmsProjectTestReportMapper.selectPmsProjectTestReportById(id);
        if (pmsProjectTestReport == null) {
            throw new ServiceException("该测试报告不存在(测试报告ID[%s])", id);
        }
        pmsProjectService.selectAndCheckPmsProjectById(pmsProjectTestReport.getPmsProjectId());
        return pmsProjectTestReport;
    }

    @Override
    public PmsProjectTestReportChartVo selectPmsProjectTestReportChartById(Long id) {
        PmsProjectTestReport pmsProjectTestReport = selectPmsProjectTestReportById(id);
        return getPmsProjectTestReportChartVo(pmsProjectTestReport);
    }

    /**
     * 查询测试报告列表
     * 
     * @param pmsProjectTestReport 测试报告
     * @return 测试报告
     */
    @Override
    public List<PmsProjectTestReport> selectPmsProjectTestReportList(PmsProjectTestReport pmsProjectTestReport)
    {
        pmsProjectService.selectPmsProjectById(pmsProjectTestReport.getPmsProjectId());
        return pmsProjectTestReportMapper.selectPmsProjectTestReportList(pmsProjectTestReport);
    }

    /**
     * 新增测试报告
     * 
     * @param pmsProjectTestReport 测试报告
     * @return 结果
     */
    @Override
    public int insertPmsProjectTestReport(PmsProjectTestReport pmsProjectTestReport)
    {
        pmsProjectService.selectPmsProjectById(pmsProjectTestReport.getPmsProjectId());
        pmsProjectTestReport.setCreateTime(DateUtils.getNowDate());
        return pmsProjectTestReportMapper.insertPmsProjectTestReport(pmsProjectTestReport);
    }

    /**
     * 修改测试报告
     * 
     * @param pmsProjectTestReport 测试报告
     * @return 结果
     */
    @Override
    public int updatePmsProjectTestReport(PmsProjectTestReport pmsProjectTestReport)
    {
        selectPmsProjectTestReportById(pmsProjectTestReport.getId());
        pmsProjectTestReport.setUpdateTime(DateUtils.getNowDate());
        return pmsProjectTestReportMapper.updatePmsProjectTestReport(pmsProjectTestReport);
    }

    /**
     * 批量删除测试报告
     * 
     * @param ids 需要删除的测试报告主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deletePmsProjectTestReportByIds(Long[] ids)
    {
        for (Long id : ids) {
            selectPmsProjectTestReportById(id);
        }
        pmsProjectTestReportSheetMapper.deletePmsProjectTestReportSheetByProjectTestReportIds(ids);
        return pmsProjectTestReportMapper.deletePmsProjectTestReportByIds(ids);
    }

    @Override
    public GenericsTableDataInfo<PmsProjectTestReportDetailVo> selectPmsProjectTestReportList(PmsProjectTestReportSearchVo pmsProjectTestReportSearchVo) {
        pmsProjectService.selectPmsProjectById(pmsProjectTestReportSearchVo.getPmsProjectId());
        startPage();
        List<PmsProjectTestReport> pmsProjectTestReportList = pmsProjectTestReportMapper.selectPmsProjectTestReportListBySearchVo(pmsProjectTestReportSearchVo);
        return toPage(pmsProjectTestReportList, this::buildPmsProjectTestReportDetailVoList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertPmsProjectTestReportByInputVo(PmsProjectTestReportInputVo pmsProjectTestReportInputVo) {
        pmsProjectService.selectPmsProjectById(pmsProjectTestReportInputVo.getPmsProjectId());
        PmsProjectTestReport pmsProjectTestReport = new PmsProjectTestReport();
        BeanUtils.copyProperties(pmsProjectTestReportInputVo, pmsProjectTestReport);
        pmsProjectTestReport.setCreateBy(SecurityUtils.getUsername());
        pmsProjectTestReport.setReportAssigneeId(SecurityUtils.getGitlinkUserId());
        pmsProjectTestReportMapper.insertPmsProjectTestReport(pmsProjectTestReport);
        //测试报告-测试用例关联表中插入数据
        this.insertPmsProjectTestReportSheetByInputVo(pmsProjectTestReportInputVo.getTestSheetIds(), pmsProjectTestReport.getId());
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updatePmsProjectTestReportByUpdateVo(PmsProjectTestReportUpdateVo pmsProjectTestReportUpdateVo) {
        selectPmsProjectTestReportById(pmsProjectTestReportUpdateVo.getId());
        PmsUtils.validStartTimeAndEndTime(pmsProjectTestReportUpdateVo.getStartTime(), pmsProjectTestReportUpdateVo.getEndTime());
        PmsProjectTestReport pmsProjectTestReport = new PmsProjectTestReport();
        BeanUtils.copyProperties(pmsProjectTestReportUpdateVo, pmsProjectTestReport);
        pmsProjectTestReport.setUpdateBy(SecurityUtils.getUsername());
        if (StringUtils.isNotBlank(pmsProjectTestReportUpdateVo.getTestSheetIds())) {
            //根据测试报告id删除测试报告-测试用例关联表中数据
            pmsProjectTestReportSheetMapper.deletePmsProjectTestReportSheetByProjectTestReportId(pmsProjectTestReport.getId());
            //测试报告-测试用例关联表中插入数据
            this.insertPmsProjectTestReportSheetByInputVo(pmsProjectTestReportUpdateVo.getTestSheetIds(), pmsProjectTestReport.getId());
        }
        return pmsProjectTestReportMapper.updatePmsProjectTestReport(pmsProjectTestReport);
    }

    private void insertPmsProjectTestReportSheetByInputVo(String testSheetIds, Long pmsProjectTestReportId) {
        if (StringUtils.isNotBlank(testSheetIds)) {
            String[] pmsProjectTestSheetIds = testSheetIds.split(",");
            for (String pmsProjectTestSheetId : pmsProjectTestSheetIds) {
                PmsProjectTestReportSheet pmsProjectTestReportSheet = new PmsProjectTestReportSheet();
                pmsProjectTestReportSheet.setProjectTestReportId(pmsProjectTestReportId);
                pmsProjectTestReportSheet.setProjectTestSheetId(Long.valueOf(pmsProjectTestSheetId));
                pmsProjectTestReportSheetMapper.insertPmsProjectTestReportSheet(pmsProjectTestReportSheet);
            }
        }
    }

    private PmsProjectTestReportDetailVo buildPmsProjectTestReportDetailVoList(PmsProjectTestReport pmsProjectTestReport) {
        PmsProjectTestReportDetailVo pmsProjectTestReportDetailVo = new PmsProjectTestReportDetailVo();
        BeanUtils.copyProperties(pmsProjectTestReport, pmsProjectTestReportDetailVo);
        List<PmsProjectTestsheetVo> pmsProjectTestSheetVos = this.getPmsProjectTestSheetVoListByTestReportId(pmsProjectTestReport.getId());
        pmsProjectTestReportDetailVo.setPmsProjectTestsheetList(pmsProjectTestSheetVos);
        pmsProjectTestReportDetailVo.setCreator(pmsCommonService.getSimpleSysUserByUsername(pmsProjectTestReport.getCreateBy()));
        return pmsProjectTestReportDetailVo;
    }

    private List<PmsProjectTestsheetVo> getPmsProjectTestSheetVoListByTestReportId(Long pmsProjectTestReportId) {
        List<PmsProjectTestReportSheet> pmsProjectTestReportSheets =
                pmsProjectTestReportSheetMapper.selectPmsProjectTestReportSheetByProjectTestReportId(pmsProjectTestReportId);
        String pmsProjectTestSheetIds = StringUtils.join(
                pmsProjectTestReportSheets
                        .stream().map(PmsProjectTestReportSheet::getProjectTestSheetId)
                        .collect(Collectors.toList())
                , ",");
        return pmsProjectTestsheetService.selectPmsProjectTestsheetVosByIds(pmsProjectTestSheetIds);
    }

    private PmsProjectTestReportDetailVo getPmsProjectTestReportDetailVo(PmsProjectTestReport pmsProjectTestReport) {
        PmsProjectTestReportDetailVo pmsProjectTestReportDetailVo = new PmsProjectTestReportDetailVo();
        BeanUtils.copyProperties(pmsProjectTestReport, pmsProjectTestReportDetailVo);
        pmsProjectTestReportDetailVo.setPmsProjectTestsheetList(this.getPmsProjectTestSheetVoListByTestReportId(pmsProjectTestReport.getId()));
        pmsProjectTestReportDetailVo.setCreator(pmsCommonService.getSimpleSysUserByUsername(pmsProjectTestReport.getCreateBy()));
        return pmsProjectTestReportDetailVo;
    }

    private PmsProjectTestReportChartVo getPmsProjectTestReportChartVo(PmsProjectTestReport pmsProjectTestReport) {
        PmsProjectTestReportChartVo pmsProjectTestReportChartVo = new PmsProjectTestReportChartVo();
        BeanUtils.copyProperties(pmsProjectTestReport, pmsProjectTestReportChartVo);
        pmsProjectTestReportChartVo.setReportAssignee(pmsCommonService.getSimpleSysUserByGitlinkUserId(pmsProjectTestReport.getReportAssigneeId()));
        buildProjectTestReportChartVo(pmsProjectTestReportChartVo);
        pmsCommonService.setBaseEntityVo(pmsProjectTestReportChartVo, pmsProjectTestReport);
        return pmsProjectTestReportChartVo;
    }

    private void buildProjectTestReportChartVo(PmsProjectTestReportChartVo pmsProjectTestReportChartVo) {
        //测试报告关联的测试单列表
        List<PmsProjectTestsheetVo> pmsProjectTestSheetVos = this.getPmsProjectTestSheetVoListByTestReportId(pmsProjectTestReportChartVo.getId());
        pmsProjectTestReportChartVo.setPmsProjectTestsheetList(pmsProjectTestSheetVos);
        //测试报告关联的测试单列表的负责人集合
        List<SimpleSysUser> testSheetAssignee = pmsProjectTestSheetVos.stream().map(PmsProjectTestsheetVo::getAssigneeUser).collect(Collectors.toList());
        pmsProjectTestReportChartVo.setTestSheetAssignees(testSheetAssignee);
        //测试开始时间及结束时间
        if (pmsProjectTestReportChartVo.getStartTime() == null || pmsProjectTestReportChartVo.getEndTime() == null) {
            setEarliestStartTimeAndLatestEndTimeToVo(pmsProjectTestSheetVos, pmsProjectTestReportChartVo);
        }
        //测试报告关联的项目
        pmsProjectTestReportChartVo.setLinkedProject(pmsProjectService.getSimpleProject(pmsProjectTestReportChartVo.getPmsProjectId()));
        //测试报告关联的迭代
        Long[] testSheetLinkSprintIds = pmsProjectTestSheetVos.stream().map(PmsProjectTestsheetVo::getProjectSprintId).toArray(Long[]::new);
        if (testSheetLinkSprintIds.length > 0) {
            pmsProjectTestReportChartVo.setLinkedSprint(pmsProjectSprintService.selectPmsProjectSimpleSprintByIds(testSheetLinkSprintIds));
        }
        //测试单ids
        Long [] testSheetIds = pmsProjectTestSheetVos.stream().map(PmsProjectTestsheetVo::getId).toArray(Long[]::new);
        if (testSheetIds.length > 0) {
            //测试单用例执行人集合
            pmsProjectTestReportChartVo.setTestSheetCaseTesters(getTestSheetCaseTestersByTestSheetIds(testSheetIds));
            //获取测试单下测试用例关联的缺陷列表
            JSONObject testSheetLinkedIssueList = getTestSheetLinkedIssueList(pmsProjectTestReportChartVo, testSheetIds);
            pmsProjectTestReportChartVo.setPmsProjectIssueList(testSheetLinkedIssueList);
            //设置缺陷相关统计数据
            this.setIssueStatusAndPriorityStatistics(pmsProjectTestReportChartVo, testSheetLinkedIssueList);
            //测试用例执行结果
            pmsProjectTestReportChartVo.setTestCaseExecutionResultStatistics(
                    pmsProjectTestsheetIssuesMapper.getTestCaseExecutionResultStatisticsByIds(testSheetIds)
            );
            //模块数据统计
            Long[] testCaseIds = pmsProjectTestcaseMapper.selectPmsProjectTestcaseIdsByTestSheetIds(testSheetIds);
            if (testCaseIds != null && testCaseIds.length > 0) {
                List<PmsProjectModuleStatistics> pmsProjectModuleStatistics = pmsProductModuleMapper.selectModuleStatisticsByTestCaseIds(testSheetIds, testCaseIds);
                pmsProjectTestReportChartVo.setModuleStatistics(pmsProjectModuleStatistics);
            }
        }
    }

    private List<SimpleSysUser> getTestSheetCaseTestersByTestSheetIds(Long[] testSheetIds) {
        Long[] testSheetCaseTesterIds = pmsProjectTestsheetCasesMapper.selectTestSheetCaseTesterIdsByTestSheetIds(testSheetIds);
        String testerIdsString = Arrays.stream(testSheetCaseTesterIds)
                .map(String::valueOf)  // 将Long转换为String
                .collect(Collectors.joining(","));  // 使用逗号连接所有字符串
        return pmsCommonService.getSimpleSysUserListByGitlinkUserIdsAndCache(testerIdsString, null);
    }

    private void setEarliestStartTimeAndLatestEndTimeToVo(List<PmsProjectTestsheetVo> pmsProjectTestSheetVos, PmsProjectTestReportChartVo pmsProjectTestReportChartVo) {
        // 最早的beginTime
        Optional<PmsProjectTestsheetVo> earliestBeginTime = pmsProjectTestSheetVos.stream()
                .filter(sheet -> sheet.getBeginTime() != null) // 过滤掉空的BeginTime
                .min(Comparator.comparing(PmsProjectTestsheetVo::getBeginTime));
        // 最晚的endTime
        Optional<PmsProjectTestsheetVo> latestEndTime = pmsProjectTestSheetVos.stream()
                .filter(sheet -> sheet.getEndTime() != null) // 过滤掉空的EndTime
                .max(Comparator.comparing(PmsProjectTestsheetVo::getEndTime));
        // 检查是否存在最早的beginTime和最晚的endTime
        if (earliestBeginTime.isPresent() && latestEndTime.isPresent()) {
            PmsProjectTestsheetVo earliestBeginTimeTestSheetVo = earliestBeginTime.get();
            PmsProjectTestsheetVo latestEndTimeTestSheetVo = latestEndTime.get();
            pmsProjectTestReportChartVo.setStartTime(earliestBeginTimeTestSheetVo.getBeginTime());
            pmsProjectTestReportChartVo.setEndTime(latestEndTimeTestSheetVo.getEndTime());
        }
    }


    private void setIssueStatusAndPriorityStatistics(PmsProjectTestReportChartVo pmsProjectTestReportChartVo, JSONObject testSheetLinkedIssueList) {
        if (testSheetLinkedIssueList != null) {
            //测试用例关联的缺陷
            JSONArray testSheetLinkedIssueArray = testSheetLinkedIssueList.getJSONArray("issues");
            //缺陷状态统计分布
            JSONArray statusDistribution = getIssueStatusDistribution(testSheetLinkedIssueArray);
            pmsProjectTestReportChartVo.setIssueStatusStatistics(statusDistribution);
            //缺陷优先级统计分布
            JSONArray priorityDistribution = getIssuePriorityDistribution(testSheetLinkedIssueArray);
            pmsProjectTestReportChartVo.setIssuePriorityStatistics(priorityDistribution);
        }
    }

    private JSONObject getTestSheetLinkedIssueList(PmsProjectTestReportChartVo pmsProjectTestReportChartVo, Long[] testSheetIds) {
        //测试报告关联的执行用例列表
        List<PmsProjectTestsheetCases> pmsProjectTestsheetCasesList = pmsProjectTestsheetCasesMapper.selectPmsProjectTestsheetCasesListBy(testSheetIds);
        //测试报告关联的执行用例列表ids
        Long[] projectTestSheetCasesIds = pmsProjectTestsheetCasesList.stream().map(PmsProjectTestsheetCases::getId).toArray(Long[]::new);
        //执行用例下关联的缺陷列表
        return this.getTestSheetLinkedIssueList(pmsProjectTestReportChartVo.getPmsProjectId(), projectTestSheetCasesIds);
    }

    private JSONArray getIssueStatusDistribution(JSONArray testSheetLinkedIssueArray) {
        return getIssueDistribution(testSheetLinkedIssueArray, "status");
    }

    private JSONArray getIssuePriorityDistribution(JSONArray testSheetLinkedIssueArray) {
        return getIssueDistribution(testSheetLinkedIssueArray, "priority");
    }

    private JSONArray getIssueDistribution(JSONArray jsonArray, String type) {
        // Initialize a JSONArray to store the issue status statistics
        JSONArray issueStatusStatistics = new JSONArray();

        // Initialize a HashMap to store the status ID or priority ID and its occurrence count
        HashMap<Integer, JSONObject> distributionMap = new HashMap<>();

        // Iterate through the JSONArray
        for (Object o : jsonArray) {
            JSONObject issue = (JSONObject) o;

            // Get the type ID (status ID or priority ID)
            int typeId = issue.getJSONObject(type).getInteger("id");

            // Get the type name (status name or priority name)
            String name = issue.getJSONObject(type).getString("name");

            // Update the count for the type ID in the distributionMap
            JSONObject typeInfo = distributionMap.get(typeId);
            if (typeInfo == null) {
                typeInfo = new JSONObject();
                typeInfo.put("id", typeId);
                typeInfo.put("name", name);
                typeInfo.put("count", 0);
                distributionMap.put(typeId, typeInfo);
            }
            int count = typeInfo.getInteger("count") + 1;
            typeInfo.put("count", count);
        }

        // Convert the distributionMap to a JSONArray
        issueStatusStatistics.addAll(distributionMap.values());

        return issueStatusStatistics;
    }

    private JSONObject getTestSheetLinkedIssueList(Long pmsProjectId, Long[] projectTestSheetCasesIds) {
        if (projectTestSheetCasesIds == null || projectTestSheetCasesIds.length == 0) {
            return null;
        }
        Long[] testSheetLinkedIssueIds = pmsProjectTestsheetIssuesMapper.selectIssueIdsByTestSheetCaseIds(projectTestSheetCasesIds);
        String testSheetLinkedIssueIdString = StringUtils.join(testSheetLinkedIssueIds, ",");
        if (testSheetLinkedIssueIds != null && testSheetLinkedIssueIds.length > 0) {
            JSONObject params = new JSONObject();
            params.put("ids", testSheetLinkedIssueIdString);
            params.put("pm_project_id", String.valueOf(pmsProjectId));
            params.put("category", ISSUE_FILTER_TYPE_ALL);
            params.put("participant_category", ISSUE_FILTER_TYPE_ALL);
            params.put("pm_issue_type", PROJECT_ISSUE_TYPE_BUG);
            params.put("sort_by", "issues.status_id");
            params.put("sort_direction", "asc");
            JSONObject result = null;
            try {
                result = pmsProjectIssuesService.setProjectIssueListUserState(gitLinkRequestHelper.doGet(PmsGitLinkRequestUrl.GET_ISSUE_LIST(params)));
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            } catch (ServiceException se) {
                //调用gitlink接口返回404时，捕获异常，返回空值
                if (se.getCode() != null && se.getCode() == HttpStatus.NOT_FOUND) {
                    return null;
                }
            }
            return result;
        }
        return null;
    }
}
