package com.sundot.airport.web.controller.exam;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sundot.airport.common.core.domain.entity.SysDept;
import com.sundot.airport.common.core.domain.entity.SysRole;
import com.sundot.airport.common.core.domain.model.LoginUser;
import com.sundot.airport.common.enums.RoleTypeEnum;
import com.sundot.airport.common.core.domain.SysAnalysisReportParamDto;
import com.sundot.airport.common.utils.DateRangeQueryUtils;
import com.sundot.airport.common.utils.SecurityUtils;
import com.sundot.airport.exam.common.HttpResult;
import com.sundot.airport.exam.common.service.QuesCatService;
import com.sundot.airport.exam.domain.DailyTask;
import com.sundot.airport.exam.domain.DailyTaskDetail;
import com.sundot.airport.exam.domain.QuesCat;
import com.sundot.airport.exam.mapper.DailyTaskDetailMapper;
import com.sundot.airport.exam.mapper.DailyTaskMapper;
import com.sundot.airport.system.domain.BaseCheckCategory;
import com.sundot.airport.system.mapper.SysDeptMapper;
import com.sundot.airport.system.service.IBaseCheckCategoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 抽问抽答错题分析接口（美兰4级架构适配版）
 * <p>
 * 美兰组织架构：STATION（安检站）> BRIGADE（大队）> MANAGER（主管）> TEAMS（班组）
 * <p>
 * 接口1: GET /wrong-analysis/overview      总体问题分布（饼图，所有角色）
 * 接口2: GET /wrong-analysis/radar         问题分布对比（雷达图，仅站长）
 * 接口3: GET /wrong-analysis/category-detail  大类详情
 *   - 站长：各大队对比表格 + 各大队趋势
 *   - 大队长：各主管对比表格 + 各主管趋势
 *   - 主管：本主管各小类甜甜圈 + 本主管趋势
 *   - 班组长及以下：show=false
 */
@RestController
@RequestMapping("/v1/cs/app/daily-exam/wrong-analysis")
public class DailyExamWrongAnalysisController {

    private static final Logger log = LoggerFactory.getLogger(DailyExamWrongAnalysisController.class);

    @Autowired
    private DailyTaskMapper dailyTaskMapper;

    @Autowired
    private DailyTaskDetailMapper dailyTaskDetailMapper;

    @Autowired
    private SysDeptMapper sysDeptMapper;

    @Autowired
    private QuesCatService quesCatService;

    @Autowired
    private IBaseCheckCategoryService baseCheckCategoryService;

    // =====================================================================
    // 接口1：总体问题分布（饼图）
    // =====================================================================

    /**
     * 总体问题分布（按大类统计错题数）
     * 站长/管理员/质检科：全站；大队长：本大队；主管：本主管室；班组长：本班组
     *
     * @param timeType  year/month/custom，默认 month
     * @param startDate 自定义开始日期 yyyy-MM-dd
     * @param endDate   自定义结束日期 yyyy-MM-dd
     */
    @GetMapping("/overview")
    public HttpResult<Map<String, Object>> getOverview(
            @RequestParam(required = false, defaultValue = "month") String timeType,
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate) {

        try {
            RoleInfo role = getRoleInfo();
            LocalDate[] range = resolveDateRange(timeType, startDate, endDate);

            List<DailyTaskDetail> errorDetails = getErrorDetails(role, range[0], range[1]);

            Map<String, BaseCheckCategory> qcIdToLevel1 = buildQcIdToLevel1Map(errorDetails);

            Map<Long, long[]> countByCategory = new LinkedHashMap<>();
            Map<Long, String> categoryNames = new LinkedHashMap<>();

            for (DailyTaskDetail detail : errorDetails) {
                BaseCheckCategory cat = qcIdToLevel1.get(detail.getDtdModuleId());
                if (cat != null) {
                    countByCategory.merge(cat.getId(), new long[]{1, cat.getOrderNum() != null ? cat.getOrderNum() : 99},
                            (a, b) -> new long[]{a[0] + 1, a[1]});
                    categoryNames.put(cat.getId(), cat.getName());
                }
            }

            List<Map<String, Object>> categories = countByCategory.entrySet().stream()
                    .sorted(Comparator.comparingLong(e -> e.getValue()[1]))
                    .map(e -> {
                        Map<String, Object> item = new LinkedHashMap<>();
                        item.put("id", e.getKey());
                        item.put("name", categoryNames.get(e.getKey()));
                        item.put("count", e.getValue()[0]);
                        return item;
                    })
                    .collect(Collectors.toList());

            Map<String, Object> result = new LinkedHashMap<>();
            result.put("categories", categories);
            return HttpResult.success(result);

        } catch (Exception e) {
            log.error("获取总体问题分布失败", e);
            return HttpResult.error("获取总体问题分布失败：" + e.getMessage());
        }
    }

    // =====================================================================
    // 接口2：问题分布对比（雷达图，仅站长）
    // =====================================================================

    /**
     * 问题分布对比雷达图（仅站长可见，各大队在各大类的错题数对比）
     *
     * @param timeType  year/month/custom，默认 month
     * @param startDate 自定义开始日期
     * @param endDate   自定义结束日期
     */
    @GetMapping("/radar")
    public HttpResult<Map<String, Object>> getRadar(
            @RequestParam(required = false, defaultValue = "month") String timeType,
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate) {

        try {
            RoleInfo role = getRoleInfo();
            if (!role.isStation) {
                Map<String, Object> noShow = new HashMap<>();
                noShow.put("show", false);
                return HttpResult.success(noShow);
            }

            LocalDate[] range = resolveDateRange(timeType, startDate, endDate);

            // 查询所有大类，作为雷达图的维度
            BaseCheckCategory level1Query = new BaseCheckCategory();
            level1Query.setLevel(1);
            List<BaseCheckCategory> level1Categories = baseCheckCategoryService.selectBaseCheckCategoryList(level1Query);
            level1Categories.sort(Comparator.comparingInt(c -> c.getOrderNum() != null ? c.getOrderNum() : 99));

            List<String> indicators = level1Categories.stream()
                    .map(BaseCheckCategory::getName)
                    .collect(Collectors.toList());
            List<Long> indicatorIds = level1Categories.stream()
                    .map(BaseCheckCategory::getId)
                    .collect(Collectors.toList());

            // 获取站下各大队（直接下级）
            List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
            List<SysDept> brigadeDepts = subDepts == null ? Collections.emptyList() :
                    subDepts.stream()
                            .filter(d -> role.userDeptId.equals(d.getParentId()) && "0".equals(d.getDelFlag()))
                            .collect(Collectors.toList());

            // 为每个大队构建一条雷达折线
            List<Map<String, Object>> series = new ArrayList<>();
            for (SysDept brigade : brigadeDepts) {
                // 查该大队下所有主管的错误明细（通过dtDepartmentId匹配主管子部门）
                List<DailyTaskDetail> details = getErrorDetailsBySubDepts(brigade.getDeptId(), range[0], range[1]);
                Map<String, BaseCheckCategory> qcIdToLevel1 = buildQcIdToLevel1Map(details);

                Map<Long, Long> countMap = details.stream()
                        .filter(d -> qcIdToLevel1.get(d.getDtdModuleId()) != null)
                        .collect(Collectors.groupingBy(
                                d -> qcIdToLevel1.get(d.getDtdModuleId()).getId(),
                                Collectors.counting()));

                List<Long> data = indicatorIds.stream()
                        .map(id -> countMap.getOrDefault(id, 0L))
                        .collect(Collectors.toList());

                Map<String, Object> seriesItem = new LinkedHashMap<>();
                seriesItem.put("name", brigade.getDeptName());
                seriesItem.put("deptId", brigade.getDeptId());
                seriesItem.put("data", data);
                series.add(seriesItem);
            }

            Map<String, Object> result = new LinkedHashMap<>();
            result.put("show", true);
            result.put("indicators", indicators);
            result.put("series", series);
            return HttpResult.success(result);

        } catch (Exception e) {
            log.error("获取问题分布对比失败", e);
            return HttpResult.error("获取问题分布对比失败：" + e.getMessage());
        }
    }

    // =====================================================================
    // 接口3：大类详情（站长/大队长/主管）
    // =====================================================================

    /**
     * 大类详情 - 点击饼图某一块后展示该大类下的小类分布及趋势
     * <p>
     * 站长：各大队小类对比表 + 各大队按日趋势
     * 大队长：各主管小类对比表 + 各主管按日趋势
     * 主管：本主管各小类甜甜圈分布 + 本主管按日趋势
     * 班组长及以下：show=false
     *
     * @param categoryId 大类ID（base_check_category.id，level=1）
     * @param param      dateRangeQueryType=YEAR|QUARTER|MONTH，year，quarter，month
     */
    @GetMapping("/category-detail")
    public HttpResult<Map<String, Object>> getCategoryDetail(
            @RequestParam Long categoryId,
            SysAnalysisReportParamDto param) {

        try {
            RoleInfo role = getRoleInfo();

            if (!role.isStation && !role.isBrigade && !role.isKezhang) {
                Map<String, Object> noShow = new HashMap<>();
                noShow.put("show", false);
                return HttpResult.success(noShow);
            }

            LocalDate[] range = resolveDateRange(param);

            BaseCheckCategory level1Cat = baseCheckCategoryService.selectBaseCheckCategoryById(categoryId);
            if (level1Cat == null) {
                return HttpResult.error("未找到该分类");
            }

            BaseCheckCategory level2Query = new BaseCheckCategory();
            level2Query.setParentId(categoryId);
            level2Query.setLevel(2);
            List<BaseCheckCategory> subCategories = baseCheckCategoryService.selectBaseCheckCategoryList(level2Query);
            subCategories.sort(Comparator.comparingInt(c -> c.getOrderNum() != null ? c.getOrderNum() : 99));

            List<String> subCatNames = subCategories.stream()
                    .map(BaseCheckCategory::getName).collect(Collectors.toList());
            Map<String, String> subCatNameToQcId = buildSubCatNameToQcIdMap(subCatNames);
            Map<String, String> qcIdToSubCatName = new HashMap<>();
            subCatNameToQcId.forEach((name, qcId) -> { if (qcId != null) qcIdToSubCatName.put(qcId, name); });

            Map<String, Object> result = new LinkedHashMap<>();
            result.put("show", true);
            result.put("categoryId", categoryId);
            result.put("categoryName", level1Cat.getName());

            boolean byMonth = "YEAR".equals(param != null ? param.getDateRangeQueryType() : null);
            List<String> xAxis = buildXAxis(range[0], range[1], byMonth);
            SimpleDateFormat sdf = new SimpleDateFormat(byMonth ? "yyyy-MM" : "yyyy-MM-dd");

            if (role.isStation) {
                // ---- 站长视角：各大队对比 ----
                List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
                List<SysDept> brigadeDepts = subDepts == null ? Collections.emptyList() :
                        subDepts.stream()
                                .filter(d -> role.userDeptId.equals(d.getParentId()) && "0".equals(d.getDelFlag()))
                                .collect(Collectors.toList());

                // 查各大队错误明细（按qcId过滤）
                Map<Long, List<DailyTaskDetail>> detailsByBrigade = new LinkedHashMap<>();
                for (SysDept brigade : brigadeDepts) {
                    List<DailyTaskDetail> details = getErrorDetailsBySubDeptsAndQcIds(
                            brigade.getDeptId(), new HashSet<>(qcIdToSubCatName.keySet()), range[0], range[1]);
                    detailsByBrigade.put(brigade.getDeptId(), details);
                }

                // 表格数据：行=小类，列=各大队
                List<Map<String, Object>> tableRows = new ArrayList<>();
                for (String subCatName : subCatNames) {
                    String qcId = subCatNameToQcId.get(subCatName);
                    Map<String, Object> row = new LinkedHashMap<>();
                    row.put("subCategory", subCatName);
                    List<Long> deptCounts = new ArrayList<>();
                    for (SysDept brigade : brigadeDepts) {
                        List<DailyTaskDetail> details = detailsByBrigade.get(brigade.getDeptId());
                        long cnt = details == null ? 0 : details.stream()
                                .filter(d -> qcId != null && qcId.equals(d.getDtdModuleId()))
                                .count();
                        deptCounts.add(cnt);
                    }
                    row.put("counts", deptCounts);
                    tableRows.add(row);
                }

                List<String> deptNames = brigadeDepts.stream()
                        .map(SysDept::getDeptName).collect(Collectors.toList());

                Map<String, Object> table = new LinkedHashMap<>();
                table.put("depts", deptNames);
                table.put("rows", tableRows);
                result.put("table", table);

                // 趋势数据（各大队按日/月错题数）
                List<Map<String, Object>> trendSeries = new ArrayList<>();
                for (SysDept brigade : brigadeDepts) {
                    List<DailyTaskDetail> details = detailsByBrigade.get(brigade.getDeptId());
                    List<Integer> data = buildTrendData(xAxis, details, sdf);
                    Map<String, Object> s = new LinkedHashMap<>();
                    s.put("name", brigade.getDeptName());
                    s.put("deptId", brigade.getDeptId());
                    s.put("data", data);
                    trendSeries.add(s);
                }
                Map<String, Object> trend = new LinkedHashMap<>();
                trend.put("xAxis", xAxis);
                trend.put("series", trendSeries);
                result.put("trend", trend);

            } else if (role.isBrigade) {
                // ---- 大队长视角：各主管对比 ----
                List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
                List<SysDept> managerDepts = subDepts == null ? Collections.emptyList() :
                        subDepts.stream()
                                .filter(d -> role.userDeptId.equals(d.getParentId()) && "0".equals(d.getDelFlag()))
                                .collect(Collectors.toList());

                // 查各主管错误明细（按qcId过滤）
                Map<Long, List<DailyTaskDetail>> detailsByManager = new LinkedHashMap<>();
                for (SysDept manager : managerDepts) {
                    List<DailyTaskDetail> details = getErrorDetailsByDepartmentIdAndQcIds(
                            manager.getDeptId(), new HashSet<>(qcIdToSubCatName.keySet()), range[0], range[1]);
                    detailsByManager.put(manager.getDeptId(), details);
                }

                // 表格数据：行=小类，列=各主管
                List<Map<String, Object>> tableRows = new ArrayList<>();
                for (String subCatName : subCatNames) {
                    String qcId = subCatNameToQcId.get(subCatName);
                    Map<String, Object> row = new LinkedHashMap<>();
                    row.put("subCategory", subCatName);
                    List<Long> deptCounts = new ArrayList<>();
                    for (SysDept manager : managerDepts) {
                        List<DailyTaskDetail> details = detailsByManager.get(manager.getDeptId());
                        long cnt = details == null ? 0 : details.stream()
                                .filter(d -> qcId != null && qcId.equals(d.getDtdModuleId()))
                                .count();
                        deptCounts.add(cnt);
                    }
                    row.put("counts", deptCounts);
                    tableRows.add(row);
                }

                List<String> deptNames = managerDepts.stream()
                        .map(SysDept::getDeptName).collect(Collectors.toList());

                Map<String, Object> table = new LinkedHashMap<>();
                table.put("depts", deptNames);
                table.put("rows", tableRows);
                result.put("table", table);

                // 趋势数据（各主管按日/月错题数）
                List<Map<String, Object>> trendSeries = new ArrayList<>();
                for (SysDept manager : managerDepts) {
                    List<DailyTaskDetail> details = detailsByManager.get(manager.getDeptId());
                    List<Integer> data = buildTrendData(xAxis, details, sdf);
                    Map<String, Object> s = new LinkedHashMap<>();
                    s.put("name", manager.getDeptName());
                    s.put("deptId", manager.getDeptId());
                    s.put("data", data);
                    trendSeries.add(s);
                }
                Map<String, Object> trend = new LinkedHashMap<>();
                trend.put("xAxis", xAxis);
                trend.put("series", trendSeries);
                result.put("trend", trend);

            } else {
                // ---- 主管视角：甜甜圈 + 趋势 ----
                List<DailyTaskDetail> details = getErrorDetailsByDepartmentIdAndQcIds(
                        role.userDeptId, new HashSet<>(qcIdToSubCatName.keySet()), range[0], range[1]);

                Map<String, Long> qcIdCountMap = details.stream()
                        .collect(Collectors.groupingBy(
                                d -> d.getDtdModuleId() != null ? d.getDtdModuleId() : "",
                                Collectors.counting()));

                List<Map<String, Object>> donut = new ArrayList<>();
                for (String subCatName : subCatNames) {
                    String qcId = subCatNameToQcId.get(subCatName);
                    Map<String, Object> item = new LinkedHashMap<>();
                    item.put("name", subCatName);
                    item.put("count", qcId != null ? qcIdCountMap.getOrDefault(qcId, 0L) : 0L);
                    donut.add(item);
                }
                result.put("donut", donut);

                String deptName = getDeptName(role.userDeptId);
                List<Integer> trendData = buildTrendData(xAxis, details, sdf);
                Map<String, Object> seriesItem = new LinkedHashMap<>();
                seriesItem.put("name", deptName);
                seriesItem.put("deptId", role.userDeptId);
                seriesItem.put("data", trendData);

                Map<String, Object> trend = new LinkedHashMap<>();
                trend.put("xAxis", xAxis);
                trend.put("series", Collections.singletonList(seriesItem));
                result.put("trend", trend);
            }

            return HttpResult.success(result);

        } catch (Exception e) {
            log.error("获取大类详情失败 categoryId={}", categoryId, e);
            return HttpResult.error("获取大类详情失败：" + e.getMessage());
        }
    }

    // =====================================================================
    // 私有工具方法
    // =====================================================================

    /**
     * 获取当前用户角色信息
     * 美兰角色优先级：站长 > 大队长(经理/行政) > 主管 > 班组长 > 安检员
     */
    private RoleInfo getRoleInfo() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        List<String> roleKeys = loginUser.getUser().getRoles().stream()
                .map(SysRole::getRoleKey).collect(Collectors.toList());
        Long userDeptId = loginUser.getDeptId();
        Long userId = loginUser.getUserId();

        boolean isStation = userId == 1L
                || roleKeys.contains(RoleTypeEnum.admin.getCode())
                || roleKeys.contains(RoleTypeEnum.test.getCode())
                || roleKeys.contains(RoleTypeEnum.zhijianke.getCode());
        boolean isBrigade = !isStation && (roleKeys.contains(RoleTypeEnum.jingli.getCode())
                || roleKeys.contains(RoleTypeEnum.xingzheng.getCode()));
        boolean isKezhang = !isStation && !isBrigade && roleKeys.contains(RoleTypeEnum.kezhang.getCode());

        RoleInfo info = new RoleInfo();
        info.isStation = isStation;
        info.isBrigade = isBrigade;
        info.isKezhang = isKezhang;
        info.userDeptId = userDeptId;
        return info;
    }

    private static class RoleInfo {
        boolean isStation;
        boolean isBrigade;
        boolean isKezhang;
        Long userDeptId;
    }

    /**
     * 根据角色获取错误答题明细
     */
    private List<DailyTaskDetail> getErrorDetails(RoleInfo role, LocalDate start, LocalDate end) {
        if (role.isStation) {
            // 站长：站下所有子部门的任务（dtDeptId匹配）
            List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
            Set<Long> allDeptIds = new HashSet<>();
            allDeptIds.add(role.userDeptId);
            if (subDepts != null) {
                subDepts.forEach(d -> allDeptIds.add(d.getDeptId()));
            }
            return getErrorDetailsByDeptIds(allDeptIds, start, end);
        } else if (role.isBrigade) {
            // 大队长：本大队下所有子部门的任务（dtDeptId匹配）
            List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
            Set<Long> allDeptIds = new HashSet<>();
            allDeptIds.add(role.userDeptId);
            if (subDepts != null) {
                subDepts.forEach(d -> allDeptIds.add(d.getDeptId()));
            }
            return getErrorDetailsByDeptIds(allDeptIds, start, end);
        } else if (role.isKezhang) {
            // 主管：本主管室（dtDepartmentId匹配）
            return getErrorDetailsByDepartmentId(role.userDeptId, start, end);
        } else {
            // 班组长：本班组（dtDeptId匹配）
            return getErrorDetailsByDeptId(role.userDeptId, start, end);
        }
    }

    /**
     * 按 dt_dept_id 集合查询错误明细
     */
    private List<DailyTaskDetail> getErrorDetailsByDeptIds(Set<Long> deptIds, LocalDate start, LocalDate end) {
        if (deptIds == null || deptIds.isEmpty()) return Collections.emptyList();
        LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
        taskWrapper.in(DailyTask::getDtDeptId, deptIds);
        taskWrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(start));
        taskWrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(end));
        taskWrapper.eq(DailyTask::getDelStatus, 0);
        List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
        return getErrorDetailsFromTasks(tasks);
    }

    /**
     * 按 dt_department_id（主管级别）查询错误明细
     */
    private List<DailyTaskDetail> getErrorDetailsByDepartmentId(Long departmentId, LocalDate start, LocalDate end) {
        LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
        taskWrapper.eq(DailyTask::getDtDepartmentId, departmentId);
        taskWrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(start));
        taskWrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(end));
        taskWrapper.eq(DailyTask::getDelStatus, 0);
        List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
        return getErrorDetailsFromTasks(tasks);
    }

    /**
     * 按 dt_department_id + 指定 qcId 集合查询错误明细
     */
    private List<DailyTaskDetail> getErrorDetailsByDepartmentIdAndQcIds(
            Long departmentId, Set<String> qcIds, LocalDate start, LocalDate end) {
        List<DailyTaskDetail> all = getErrorDetailsByDepartmentId(departmentId, start, end);
        if (qcIds == null || qcIds.isEmpty()) return all;
        return all.stream()
                .filter(d -> qcIds.contains(d.getDtdModuleId()))
                .collect(Collectors.toList());
    }

    /**
     * 按 dt_dept_id（班组级别）查询错误明细
     */
    private List<DailyTaskDetail> getErrorDetailsByDeptId(Long deptId, LocalDate start, LocalDate end) {
        LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
        taskWrapper.eq(DailyTask::getDtDeptId, deptId);
        taskWrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(start));
        taskWrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(end));
        taskWrapper.eq(DailyTask::getDelStatus, 0);
        List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
        return getErrorDetailsFromTasks(tasks);
    }

    /**
     * 按上级部门ID（大队）查询其下所有主管的错误明细
     * 通过查询大队的直接下级主管，再按 dtDepartmentId 匹配
     */
    private List<DailyTaskDetail> getErrorDetailsBySubDepts(Long parentDeptId, LocalDate start, LocalDate end) {
        List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(parentDeptId);
        if (subDepts == null || subDepts.isEmpty()) return Collections.emptyList();
        Set<Long> managerIds = subDepts.stream()
                .filter(d -> parentDeptId.equals(d.getParentId()) && "0".equals(d.getDelFlag()))
                .map(SysDept::getDeptId)
                .collect(Collectors.toSet());
        if (managerIds.isEmpty()) return Collections.emptyList();

        LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
        taskWrapper.in(DailyTask::getDtDepartmentId, managerIds);
        taskWrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(start));
        taskWrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(end));
        taskWrapper.eq(DailyTask::getDelStatus, 0);
        List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
        return getErrorDetailsFromTasks(tasks);
    }

    /**
     * 按上级部门ID（大队）查询其下所有主管的错误明细，并按 qcId 过滤
     */
    private List<DailyTaskDetail> getErrorDetailsBySubDeptsAndQcIds(
            Long parentDeptId, Set<String> qcIds, LocalDate start, LocalDate end) {
        List<DailyTaskDetail> all = getErrorDetailsBySubDepts(parentDeptId, start, end);
        if (qcIds == null || qcIds.isEmpty()) return all;
        return all.stream()
                .filter(d -> qcIds.contains(d.getDtdModuleId()))
                .collect(Collectors.toList());
    }

    /**
     * 从任务列表中查询错误答题明细（dtdIsCorrect=false）
     */
    private List<DailyTaskDetail> getErrorDetailsFromTasks(List<DailyTask> tasks) {
        if (tasks == null || tasks.isEmpty()) return Collections.emptyList();
        Set<String> taskIds = tasks.stream().map(DailyTask::getDtId).collect(Collectors.toSet());

        LambdaQueryWrapper<DailyTaskDetail> detailWrapper = new LambdaQueryWrapper<>();
        detailWrapper.in(DailyTaskDetail::getDtdTaskId, taskIds);
        detailWrapper.eq(DailyTaskDetail::getDtdIsCorrect, false);
        detailWrapper.eq(DailyTaskDetail::getDelStatus, 0);
        return dailyTaskDetailMapper.selectList(detailWrapper);
    }

    /**
     * 构建 qcId -> level1 BaseCheckCategory 的映射（带缓存，避免重复查询）
     */
    private Map<String, BaseCheckCategory> buildQcIdToLevel1Map(List<DailyTaskDetail> details) {
        Map<String, BaseCheckCategory> result = new HashMap<>();
        Map<String, BaseCheckCategory> qcNameToLevel1Cache = new HashMap<>();

        for (DailyTaskDetail detail : details) {
            String qcId = detail.getDtdModuleId();
            if (qcId == null || qcId.trim().isEmpty() || result.containsKey(qcId)) continue;

            LambdaQueryWrapper<QuesCat> catWrapper = new LambdaQueryWrapper<>();
            catWrapper.eq(QuesCat::getQcId, qcId);
            catWrapper.eq(QuesCat::getDelStatus, 0);
            QuesCat quesCat = quesCatService.getOne(catWrapper);

            if (quesCat == null) {
                result.put(qcId, null);
                continue;
            }

            String qcName = quesCat.getQcName();
            if (qcNameToLevel1Cache.containsKey(qcName)) {
                result.put(qcId, qcNameToLevel1Cache.get(qcName));
            } else {
                BaseCheckCategory level1 = baseCheckCategoryService.selectLevel1ByLevel2Name(qcName);
                qcNameToLevel1Cache.put(qcName, level1);
                result.put(qcId, level1);
            }
        }
        return result;
    }

    /**
     * 构建小类名称 -> qcId 的映射
     */
    private Map<String, String> buildSubCatNameToQcIdMap(List<String> subCatNames) {
        Map<String, String> result = new HashMap<>();
        for (String name : subCatNames) {
            LambdaQueryWrapper<QuesCat> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(QuesCat::getQcName, name);
            wrapper.eq(QuesCat::getDelStatus, 0);
            wrapper.last("LIMIT 1");
            QuesCat cat = quesCatService.getOne(wrapper);
            result.put(name, cat != null ? cat.getQcId() : null);
        }
        return result;
    }

    /**
     * 构建折线趋势数据，与 xAxis 对齐
     */
    private List<Integer> buildTrendData(List<String> xAxis, List<DailyTaskDetail> details, SimpleDateFormat sdf) {
        if (details == null || details.isEmpty()) {
            return xAxis.stream().map(x -> 0).collect(Collectors.toList());
        }
        Set<String> taskIds = details.stream().map(DailyTaskDetail::getDtdTaskId).collect(Collectors.toSet());
        LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
        taskWrapper.in(DailyTask::getDtId, taskIds);
        taskWrapper.eq(DailyTask::getDelStatus, 0);
        List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
        Map<String, String> taskIdToDate = tasks.stream()
                .filter(t -> t.getDtBusinessDate() != null)
                .collect(Collectors.toMap(DailyTask::getDtId, t -> sdf.format(t.getDtBusinessDate()), (a, b) -> a));

        Map<String, Long> countByDate = details.stream()
                .filter(d -> taskIdToDate.containsKey(d.getDtdTaskId()))
                .collect(Collectors.groupingBy(d -> taskIdToDate.get(d.getDtdTaskId()), Collectors.counting()));

        return xAxis.stream()
                .map(label -> countByDate.getOrDefault(label, 0L).intValue())
                .collect(Collectors.toList());
    }

    /**
     * 根据 timeType 计算起止日期（供 overview/radar 使用）
     */
    private LocalDate[] resolveDateRange(String timeType, String startDate, String endDate) {
        LocalDate today = LocalDate.now();
        LocalDate start;
        LocalDate end = today;
        switch (timeType == null ? "month" : timeType) {
            case "year":
                start = today.withDayOfYear(1);
                end = today.withMonth(12).withDayOfMonth(31);
                break;
            case "custom":
                start = (startDate != null) ? LocalDate.parse(startDate) : today.withDayOfMonth(1);
                end = (endDate != null) ? LocalDate.parse(endDate) : today;
                break;
            default: // month
                start = today.withDayOfMonth(1);
                break;
        }
        return new LocalDate[]{start, end};
    }

    /**
     * 根据 SysAnalysisReportParamDto 计算起止日期（供 category-detail 使用）
     * YEAR → 全年；QUARTER → 指定季度；MONTH → 指定月份；默认 → 当月
     */
    private LocalDate[] resolveDateRange(SysAnalysisReportParamDto param) {
        LocalDate today = LocalDate.now();
        int year = (param != null && param.getYear() != null) ? param.getYear() : today.getYear();
        String type = (param != null && param.getDateRangeQueryType() != null) ? param.getDateRangeQueryType() : "MONTH";
        com.sundot.airport.common.core.domain.SysAnalysisReportDateRangeDto range;
        switch (type) {
            case "YEAR":
                range = DateRangeQueryUtils.getYearRange(year, null);
                break;
            case "QUARTER":
                int quarter = (param.getQuarter() != null) ? param.getQuarter() : 1;
                range = DateRangeQueryUtils.getQuarterRange(year, quarter, null);
                break;
            default: // MONTH
                int month = (param != null && param.getMonth() != null) ? param.getMonth() : today.getMonthValue();
                range = DateRangeQueryUtils.getMonthRange(year, month, null);
                break;
        }
        return new LocalDate[]{
                ((java.sql.Date) range.getStartDate()).toLocalDate(),
                ((java.sql.Date) range.getEndDate()).toLocalDate()
        };
    }

    /**
     * 生成 X 轴标签列表
     */
    private List<String> buildXAxis(LocalDate start, LocalDate end, boolean byMonth) {
        List<String> xAxis = new ArrayList<>();
        if (byMonth) {
            LocalDate cursor = start.withDayOfMonth(1);
            LocalDate endMonth = end.withDayOfMonth(1);
            while (!cursor.isAfter(endMonth)) {
                xAxis.add(cursor.format(DateTimeFormatter.ofPattern("yyyy-MM")));
                cursor = cursor.plusMonths(1);
            }
        } else {
            LocalDate cursor = start;
            while (!cursor.isAfter(end)) {
                xAxis.add(cursor.toString());
                cursor = cursor.plusDays(1);
            }
        }
        return xAxis;
    }

    /**
     * 获取部门名称
     */
    private String getDeptName(Long deptId) {
        try {
            LoginUser loginUser = SecurityUtils.getLoginUser();
            if (loginUser.getUser().getDept() != null) {
                return loginUser.getUser().getDept().getDeptName();
            }
        } catch (Exception ignored) {
        }
        return "本主管";
    }
}
