package org.dromara.main.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;

import org.dromara.main.domain.*;
import org.dromara.main.domain.bo.MainExamEvaluationBo;
import org.dromara.main.domain.bo.MainExamEvaluationSyncBo;
import org.dromara.main.domain.vo.MainExamApplyListVo;
import org.dromara.main.domain.vo.MainExamSyncEmployeeOptionVo;
import org.dromara.main.domain.vo.MainExamEvaluationVo;
import org.dromara.main.mapper.*;
import org.dromara.main.service.IMainExamEvaluationService;
import org.springframework.stereotype.Service;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Service
public class MainExamEvaluationServiceImpl implements IMainExamEvaluationService {

    private final MainExamEvaluationMapper baseMapper;
    private final MainAbilityConfigMapper mainAbilityConfigMapper;
    private final MainStudentMapper mainStudentMapper;
    private final MainExamApplyMapper mainExamApplyMapper;
    private final MainPositionMapper mainPositionMapper;

    @Override
    public MainExamEvaluationVo queryById(Long id) {
        MainExamEvaluationVo vo = baseMapper.selectVoByIdWithImages(id);
        return vo;
    }

    @Override
    public TableDataInfo<MainExamEvaluationVo> queryPageList(MainExamEvaluationBo bo, PageQuery pageQuery) {
        QueryWrapper<MainExamEvaluation> lqw = buildQueryWrapper(bo);
        Page<MainExamEvaluationVo> result = baseMapper.selectVoPageWithImages(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public List<MainExamEvaluationVo> queryList(MainExamEvaluationBo bo) {
        QueryWrapper<MainExamEvaluation> lqw = buildQueryWrapper(bo);
        List<MainExamEvaluationVo> list = baseMapper.selectVoList(lqw);
        return list;
    }

    private QueryWrapper<MainExamEvaluation> buildQueryWrapper(MainExamEvaluationBo bo) {
        QueryWrapper<MainExamEvaluation> lqw = Wrappers.query();

        // 1. 测评名称搜索
        lqw.like(StringUtils.isNotBlank(bo.getEvaluationName()), "e.evaluation_name", bo.getEvaluationName());
        lqw.eq(StringUtils.isNotBlank(bo.getGrade()), "e.grade", bo.getGrade());
        lqw.eq(bo.getPositionId() != null, "e.position_id", bo.getPositionId());
        lqw.eq(StringUtils.isNotBlank(bo.getPositionType()), "e.position_type", bo.getPositionType());
        // 2. 业务状态搜索逻辑
        if (StringUtils.isNotBlank(bo.getStatus())) {
            Date now = new Date();
            switch (bo.getStatus()) {
                case "0": // 草稿
                    lqw.eq("e.status", "0");
                    break;
                case "1": // 在售 (状态为发布 且 在上架时间内)
                    lqw.eq("e.status", "1")
                        .and(w -> w.isNull("e.on_time").or().le("e.on_time", now))
                        .and(w -> w.isNull("e.down_time").or().ge("e.down_time", now));
                    break;
                case "2": // 已下架 (手动下架 或 已过下架时间)
                    lqw.and(w -> w.eq("e.status", "2")
                        .or(w2 -> w2.eq("e.status", "1")
                            .isNotNull("e.down_time")
                            .lt("e.down_time", now)));
                    break;
            }
        }

        // 3. 自定义价格区间搜索
        lqw.ge(bo.getMinPrice() != null, "e.price", bo.getMinPrice());
        lqw.le(bo.getMaxPrice() != null, "e.price", bo.getMaxPrice());

        // 4. 上架时间范围搜索 (前端日期选择器筛选的是上架时间 onTime)
        Map<String, Object> params = bo.getParams();
        if (params != null) {
            Object beginTime = params.get("beginTime");
            Object endTime = params.get("endTime");
            if (beginTime != null && endTime != null) {
                lqw.isNotNull("e.on_time").ge("e.on_time", beginTime).le("e.on_time", endTime);
            }
        }

        lqw.orderByDesc("e.create_time");
        return lqw;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(MainExamEvaluationBo bo) {
        MainExamEvaluation add = BeanUtil.toBean(bo, MainExamEvaluation.class);
        if (StringUtils.isBlank(add.getTenantId()) && add.getPositionId() != null) {
            MainPosition position = mainPositionMapper.selectById(add.getPositionId());
            if (position != null && StringUtils.isNotBlank(position.getTenantId())) {
                add.setTenantId(position.getTenantId());
            }
        }
        validEntityBeforeSave(add);

        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
            saveMainAbilityConfigs(bo.getId(), bo.getAbilityConfigs());
        }
        return flag;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(MainExamEvaluationBo bo) {
        MainExamEvaluation update = BeanUtil.toBean(bo, MainExamEvaluation.class);
        validEntityBeforeSave(update);

        boolean flag = baseMapper.updateById(update) > 0;
        if (flag) {
            mainAbilityConfigMapper.deleteByEvaluationId(bo.getId());
            saveMainAbilityConfigs(bo.getId(), bo.getAbilityConfigs());
        }
        return flag;
    }

    private void saveMainAbilityConfigs(Long evaluationId, List<MainAbilityConfig> configs) {
        if (!CollectionUtils.isEmpty(configs)) {
            // 过滤掉完全没填名称的项，但不强制要求必须关联试卷（允许后续编辑）
            List<MainAbilityConfig> validConfigs = configs.stream()
                .filter(c -> c != null && StringUtils.isNotBlank(c.getAbilityName()))
                .collect(Collectors.toList());

            for (int i = 0; i < validConfigs.size(); i++) {
                MainAbilityConfig config = validConfigs.get(i);
                config.setEvaluationId(evaluationId);
                config.setSortOrder(i);
            }
            if (!validConfigs.isEmpty()) {
                mainAbilityConfigMapper.insertBatch(validConfigs);
            }
        }
    }

    private void validEntityBeforeSave(MainExamEvaluation entity) {
        // TODO 做一些数据校验，如唯一约束
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // TODO 做一些业务上的校验
        }
        ids.forEach(mainAbilityConfigMapper::deleteByEvaluationId);
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateStatus(List<Long> ids, String status) {
        if (CollectionUtils.isEmpty(ids) || StringUtils.isBlank(status)) {
            return false;
        }
        for (Long id : ids) {
            MainExamEvaluation entity = new MainExamEvaluation();
            entity.setId(id);
            entity.setStatus(status);
            baseMapper.updateById(entity);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateStatus(Long id, String status) {
        if (id == null || StringUtils.isBlank(status)) {
            return false;
        }
        MainExamEvaluation entity = new MainExamEvaluation();
        entity.setId(id);
        entity.setStatus(status);
        return baseMapper.updateById(entity) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean syncThirdPartyData() {
        log.info("开始同步第三方测评数据");
        try {
            // 查询所有绑定了第三方考试的能力配置
            List<MainAbilityConfig> configs = mainAbilityConfigMapper.selectList(new LambdaQueryWrapper<MainAbilityConfig>()
                .isNotNull(MainAbilityConfig::getThirdExamInfoId));

            if (configs.isEmpty()) {
                log.info("没有需要同步的能力配置");
                return true;
            }

            // 获取考试列表（可以通过考试星接口查询）
            org.dromara.demo.domain.dto.KaoshixingExamListRequest request = new org.dromara.demo.domain.dto.KaoshixingExamListRequest();
            request.setPage(1);
            request.setExpSeconds(300);

            String response = org.dromara.common.core.utils.SpringUtils.getBean(org.dromara.demo.service.impl.KaoshixingService.class)
                    .fetchExamList(request);

            com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
            com.fasterxml.jackson.databind.JsonNode responseNode = objectMapper.readTree(response);

            if (!responseNode.get("success").asBoolean()) {
                log.error("调用考试星接口获取考试列表失败: {}", response);
                return false;
            }

            com.fasterxml.jackson.databind.JsonNode rows = responseNode.get("bizContent").get("rows");

            int updateCount = 0;
            // 遍历配置更新信息
            for (MainAbilityConfig config : configs) {
                for (com.fasterxml.jackson.databind.JsonNode row : rows) {
                    if (row.get("examInfoId").asLong() == config.getThirdExamInfoId()) {
                        String examLink = row.has("examLink") ? row.get("examLink").asText() : null;
                        if (org.dromara.common.core.utils.StringUtils.isNotBlank(examLink) &&
                            !examLink.equals(config.getThirdExamLink())) {
                            config.setThirdExamLink(examLink);
                            mainAbilityConfigMapper.updateById(config);
                            updateCount++;
                        }
                        break;
                    }
                }
            }

            log.info("同步第三方测评数据完成，共更新了 {} 条记录", updateCount);
            return true;
        } catch (Exception e) {
            log.error("同步第三方数据异常", e);
            throw new org.dromara.common.core.exception.ServiceException("同步第三方数据失败: " + e.getMessage());
        }
    }

    @Override
    public TableDataInfo<MainExamApplyListVo> getApplyList(Long evaluationId, String keyword, PageQuery pageQuery) {
        if (evaluationId == null) {
            throw new ServiceException("测评ID不能为空");
        }
        String tenantId = getCurrentTenantId();
        List<MainExamApplyListVo> list = baseMapper.selectApplyListByEvaluationId(evaluationId, tenantId, keyword);
        return TableDataInfo.build(list, pageQuery.build());
    }

    @Override
    public List<MainExamApplyListVo> queryApplyList(Long evaluationId, String keyword) {
        if (evaluationId == null) {
            throw new ServiceException("测评ID不能为空");
        }
        String tenantId = getCurrentTenantId();
        return baseMapper.selectApplyListByEvaluationId(evaluationId, tenantId, keyword);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeApply(Long applyId) {
        if (applyId == null) {
            throw new ServiceException("报名记录不能为空");
        }
        MainExamApply apply = mainExamApplyMapper.selectById(applyId);
        if (apply == null || !StringUtils.equals(apply.getTenantId(), getCurrentTenantId())) {
            throw new ServiceException("报名记录不存在");
        }
        return mainExamApplyMapper.deleteById(applyId) > 0;
    }

    @Override
    public Map<Long, Map<String, Long>> getParticipantStats(Collection<Long> ids) {
        Map<Long, Map<String, Long>> result = new HashMap<>();
        if (CollectionUtils.isEmpty(ids)) {
            return result;
        }
        List<Map<String, Object>> stats = baseMapper.selectParticipantStatsByEvaluationIds(ids);
        for (Map<String, Object> item : stats) {
            if (item == null || item.get("evaluationId") == null) {
                continue;
            }
            Long evaluationId = ((Number) item.get("evaluationId")).longValue();
            long participantCount = item.get("participantCount") == null ? 0L : ((Number) item.get("participantCount")).longValue();
            long totalCount = item.get("totalCount") == null ? 0L : ((Number) item.get("totalCount")).longValue();
            Map<String, Long> countMap = new HashMap<>();
            countMap.put("participantCount", participantCount);
            countMap.put("totalCount", totalCount);
            result.put(evaluationId, countMap);
        }
        return result;
    }

    @Override
    public List<MainExamSyncEmployeeOptionVo> getSyncEmployeeOptions() {
        String tenantId = getCurrentTenantId();
        List<MainStudent> studentList = mainStudentMapper.selectList(
            Wrappers.<MainStudent>lambdaQuery()
                .eq(MainStudent::getTenantId, tenantId)
                .eq(MainStudent::getStatus, "0")
                .orderByDesc(MainStudent::getCreateTime)
        );
        if (CollectionUtils.isEmpty(studentList)) {
            return new ArrayList<>();
        }
        List<MainExamSyncEmployeeOptionVo> result = new ArrayList<>();
        for (MainStudent student : studentList) {
            MainExamSyncEmployeeOptionVo option = new MainExamSyncEmployeeOptionVo();
            option.setId(student.getId());
            option.setName(student.getName());
            option.setMobile(student.getMobile());
            option.setStudentNo(student.getStudentNo());
            result.add(option);
        }
        return result.stream()
            .collect(Collectors.toMap(MainExamSyncEmployeeOptionVo::getId, item -> item, (left, right) -> left))
            .values()
            .stream()
            .sorted(Comparator.comparing(MainExamSyncEmployeeOptionVo::getId).reversed())
            .toList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean syncToEmployees(MainExamEvaluationSyncBo bo) {
        if (bo == null || CollectionUtils.isEmpty(bo.getEvaluationIds()) || CollectionUtils.isEmpty(bo.getStudentIds())) {
            throw new ServiceException("同步参数不能为空");
        }
        String tenantId = getCurrentTenantId();
        Set<Long> evaluationIds = new HashSet<>(bo.getEvaluationIds());
        Set<Long> studentIds = new HashSet<>(bo.getStudentIds());
        List<MainStudent> validStudents = mainStudentMapper.selectList(
            Wrappers.<MainStudent>lambdaQuery()
                .eq(MainStudent::getTenantId, tenantId)
                .eq(MainStudent::getStatus, "0")
                .in(MainStudent::getId, studentIds)
        );
        Set<Long> validStudentIds = validStudents.stream()
            .map(MainStudent::getId)
            .filter(ObjectUtil::isNotNull)
            .collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(validStudentIds)) {
            throw new ServiceException("未找到可同步的员工");
        }
        List<MainExamApply> exists = mainExamApplyMapper.selectList(
            Wrappers.<MainExamApply>lambdaQuery()
                .eq(MainExamApply::getTenantId, tenantId)
                .in(MainExamApply::getEvaluationId, evaluationIds)
                .in(MainExamApply::getStudentId, validStudentIds)
        );
        Set<String> existingKeys = exists.stream()
            .map(item -> item.getEvaluationId() + "_" + item.getStudentId())
            .collect(Collectors.toSet());
        Date now = new Date();
        List<MainExamApply> addList = new ArrayList<>();
        for (Long evaluationId : evaluationIds) {
            for (Long studentId : validStudentIds) {
                String key = evaluationId + "_" + studentId;
                if (existingKeys.contains(key)) {
                    continue;
                }
                MainExamApply apply = new MainExamApply();
                apply.setEvaluationId(evaluationId);
                apply.setTenantId(tenantId);
                apply.setStudentId(studentId);
                apply.setApplySource("3");
                apply.setApplyStatus("0");
                apply.setMaxAttemptCount(1);
                apply.setScheduleStartTime(now);
                apply.setDelFlag("0");
                addList.add(apply);
            }
        }
        for (MainExamApply apply : addList) {
            try {
                mainExamApplyMapper.insert(apply);
            } catch (DuplicateKeyException e) {
                MainExamApply existApply = mainExamApplyMapper.selectAnyByUniqueKey(apply.getEvaluationId(), apply.getTenantId(), apply.getStudentId());
                if (existApply != null && StringUtils.equals(existApply.getDelFlag(), "1")) {
                    mainExamApplyMapper.restoreDeletedApply(existApply.getId());
                    continue;
                }
                log.warn("测评报名已存在，跳过本条: evaluationId={}, studentId={}, tenantId={}", apply.getEvaluationId(), apply.getStudentId(), apply.getTenantId());
            }
        }
        return true;
    }

    private String getCurrentTenantId() {
        String tenantId = LoginHelper.getTenantId();
        if (StringUtils.isBlank(tenantId)) {
            throw new ServiceException("未登录或token已失效");
        }
        return tenantId;
    }
}
