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.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.MainAbilityConfig;
import org.dromara.main.domain.MainExamApply;
import org.dromara.main.domain.MainExamEvaluation;
import org.dromara.main.domain.MainStudent;
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.MainAbilityConfigMapper;
import org.dromara.main.mapper.MainExamApplyMapper;
import org.dromara.main.mapper.MainExamEvaluationMapper;
import org.dromara.main.mapper.MainStudentMapper;
import org.dromara.main.service.IMainExamEvaluationService;
import org.springframework.stereotype.Service;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
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;

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

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

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

    private LambdaQueryWrapper<MainExamEvaluation> buildQueryWrapper(MainExamEvaluationBo bo) {
        LambdaQueryWrapper<MainExamEvaluation> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getEvaluationName()), MainExamEvaluation::getEvaluationName, bo.getEvaluationName());
        lqw.eq(StringUtils.isNotBlank(bo.getGrade()), MainExamEvaluation::getGrade, bo.getGrade());
        lqw.eq(StringUtils.isNotBlank(bo.getPosition()), MainExamEvaluation::getPosition, bo.getPosition());
        lqw.eq(StringUtils.isNotBlank(bo.getPositionType()), MainExamEvaluation::getPositionType, bo.getPositionType());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), MainExamEvaluation::getStatus, bo.getStatus());
        lqw.orderByDesc(MainExamEvaluation::getCreateTime);
        return lqw;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(MainExamEvaluationBo bo) {
        decodeDetailFieldInBo(bo);
        MainExamEvaluation add = BeanUtil.toBean(bo, MainExamEvaluation.class);
        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) {
        decodeDetailFieldInBo(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)) {
            Map<String, MainAbilityConfig> distinctConfigMap = new LinkedHashMap<>();
            for (MainAbilityConfig config : configs) {
                if (config == null || StringUtils.isBlank(config.getAbilityName()) || config.getThirdExamInfoId() == null) {
                    continue;
                }
                String distinctKey = config.getAbilityName().trim() + "_" + config.getThirdExamInfoId();
                distinctConfigMap.putIfAbsent(distinctKey, config);
            }
            List<MainAbilityConfig> distinctConfigs = distinctConfigMap.values().stream().toList();
            for (int i = 0; i < distinctConfigs.size(); i++) {
                MainAbilityConfig config = distinctConfigs.get(i);
                config.setEvaluationId(evaluationId);
                config.setSortOrder(i);
            }
            if (!distinctConfigs.isEmpty()) {
                mainAbilityConfigMapper.insertBatch(distinctConfigs);
            }
        }
    }

    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;
    }

    private void decodeDetailFieldInBo(MainExamEvaluationBo bo) {
        if (StringUtils.isNotBlank(bo.getDetail())) {
            try {
                String decoded = new String(Base64.getDecoder().decode(bo.getDetail()), StandardCharsets.UTF_8);
                bo.setDetail(decoded);
            } catch (Exception e) {
                log.warn("详情字段Base64解码失败，使用原始值", e);
            }
        }
    }

    private void encodeDetailFieldInVo(MainExamEvaluationVo vo) {
        if (StringUtils.isNotBlank(vo.getDetail())) {
            try {
                String encoded = Base64.getEncoder().encodeToString(vo.getDetail().getBytes(StandardCharsets.UTF_8));
                vo.setDetail(encoded);
            } catch (Exception e) {
                log.warn("详情字段Base64编码失败，使用原始值", e);
            }
        }
    }
}
