package com.zksy.dispatch.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zksy.dispatch.domain.DisPlanLibrary;
import com.zksy.dispatch.domain.DisPlanMatchRecord;
import com.zksy.dispatch.dto.PlanMatchDTO;
import com.zksy.dispatch.dto.PlanMatchResultVO;
import com.zksy.dispatch.mapper.DisPlanLibraryMapper;
import com.zksy.dispatch.mapper.DisPlanMatchRecordMapper;
import com.zksy.dispatch.service.DisPlanLibraryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 数字预案库 Service 实现：突发事件条件设定 → 预案匹配
 */
@Slf4j
@Service
public class DisPlanLibraryServiceImpl extends ServiceImpl<DisPlanLibraryMapper, DisPlanLibrary>
        implements DisPlanLibraryService {

    /** 加权评分：事件类型40 / 事件级别25 / 发生区域20 / 责任单位15 */
    private static final int SCORE_TYPE = 40;
    private static final int SCORE_LEVEL = 25;
    private static final int SCORE_REGION = 20;
    private static final int SCORE_UNIT = 15;
    /** 专题匹配基础匹配度 */
    private static final int TOPIC_BASE_SCORE = 90;
    /** 最近匹配记录保留上限（超出删除最旧） */
    private static final int MAX_HISTORY = 20;

    @Autowired
    private DisPlanMatchRecordMapper matchRecordMapper;

    @Override
    public List<DisPlanLibrary> listAll() {
        LambdaQueryWrapper<DisPlanLibrary> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(DisPlanLibrary::getEventType);
        wrapper.orderByDesc(DisPlanLibrary::getId);
        return this.list(wrapper);
    }

    @Override
    public List<PlanMatchResultVO> match(PlanMatchDTO dto, String operUser) {
        List<DisPlanLibrary> plans = this.list();
        List<PlanMatchResultVO> results = plans.stream().map(plan -> {
            PlanMatchResultVO vo = toVO(plan);
            int score = 0;
            if (StringUtils.isNotEmpty(dto.getEventType()) && dto.getEventType().equals(plan.getEventType())) {
                score += SCORE_TYPE;
            }
            if (StringUtils.isNotEmpty(dto.getEventLevel()) && dto.getEventLevel().equals(plan.getEventLevel())) {
                score += SCORE_LEVEL;
            }
            if (StringUtils.isNotEmpty(dto.getEventRegion()) && dto.getEventRegion().equals(plan.getEventRegion())) {
                score += SCORE_REGION;
            }
            if (StringUtils.isNotEmpty(dto.getResponsibleUnit()) && dto.getResponsibleUnit().equals(plan.getResponsibleUnit())) {
                score += SCORE_UNIT;
            }
            vo.setMatchScore(score);
            return vo;
        }).filter(vo -> vo.getMatchScore() > 0)
                .sorted(Comparator.comparing(PlanMatchResultVO::getMatchScore).reversed())
                .collect(Collectors.toList());

        if (!results.isEmpty()) {
            PlanMatchResultVO top = results.get(0);
            String desc = "手动匹配 " + StringUtils.defaultString(dto.getEventType(), "-")
                    + "/" + StringUtils.defaultString(dto.getEventRegion(), "-")
                    + "/" + StringUtils.defaultString(dto.getEventLevel(), "-");
            saveRecord(dto, "manual", top.getId(), top.getPlanName(), top.getMatchScore(), desc, operUser);
        }
        return results;
    }

    @Override
    public List<PlanMatchResultVO> matchByTopic(String topic, String operUser) {
        LambdaQueryWrapper<DisPlanLibrary> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotEmpty(topic), DisPlanLibrary::getTopic, topic);
        List<PlanMatchResultVO> results = this.list(wrapper).stream().map(plan -> {
            PlanMatchResultVO vo = toVO(plan);
            vo.setMatchScore(TOPIC_BASE_SCORE);
            return vo;
        }).collect(Collectors.toList());

        return results;
    }

    @Override
    public List<DisPlanMatchRecord> getHistory(int limit) {
        LambdaQueryWrapper<DisPlanMatchRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(DisPlanMatchRecord::getCreateTime);
        wrapper.last("LIMIT " + limit);
        return matchRecordMapper.selectList(wrapper);
    }

    private void saveRecord(PlanMatchDTO dto, String matchType, Long planId, String planName,
                            Integer matchScore, String recordDesc, String operUser) {
        try {
            // 相同匹配条件不重复生成记录，仅更新预案信息与时间
            DisPlanMatchRecord existing = findSameConditionRecord(dto);
            if (existing != null) {
                DisPlanMatchRecord update = new DisPlanMatchRecord();
                update.setId(existing.getId());
                update.setPlanId(planId);
                update.setPlanName(planName);
                update.setMatchScore(matchScore);
                update.setRecordDesc(recordDesc);
                update.setCreateBy(operUser);
                update.setCreateTime(LocalDateTime.now());
                matchRecordMapper.updateById(update);
                log.info("预案匹配记录已更新: 条件={}/{}/{}/{}",
                        dto.getEventType(), dto.getEventRegion(), dto.getEventLevel(), dto.getResponsibleUnit());
                return;
            }

            DisPlanMatchRecord record = new DisPlanMatchRecord();
            record.setEventType(dto.getEventType());
            record.setEventLevel(dto.getEventLevel());
            record.setEventRegion(dto.getEventRegion());
            record.setResponsibleUnit(dto.getResponsibleUnit());
            record.setMatchType(matchType);
            record.setPlanId(planId);
            record.setPlanName(planName);
            record.setMatchScore(matchScore);
            record.setRecordDesc(recordDesc);
            record.setCreateBy(operUser);
            record.setCreateTime(LocalDateTime.now());
            matchRecordMapper.insert(record);
            trimHistory();
        } catch (Exception e) {
            log.error("记录预案匹配历史失败", e);
        }
    }

    /**
     * 查询相同匹配条件的已有记录（四个条件全部一致），不存在返回null
     */
    private DisPlanMatchRecord findSameConditionRecord(PlanMatchDTO dto) {
        LambdaQueryWrapper<DisPlanMatchRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DisPlanMatchRecord::getMatchType, "manual");
        wrapper.eq(StringUtils.isNotEmpty(dto.getEventType()), DisPlanMatchRecord::getEventType, dto.getEventType());
        wrapper.eq(StringUtils.isNotEmpty(dto.getEventLevel()), DisPlanMatchRecord::getEventLevel, dto.getEventLevel());
        wrapper.eq(StringUtils.isNotEmpty(dto.getEventRegion()), DisPlanMatchRecord::getEventRegion, dto.getEventRegion());
        wrapper.eq(StringUtils.isNotEmpty(dto.getResponsibleUnit()), DisPlanMatchRecord::getResponsibleUnit, dto.getResponsibleUnit());
        wrapper.last("LIMIT 1");
        return matchRecordMapper.selectOne(wrapper);
    }

    /**
     * 控制最近匹配记录总量，超出上限删除最旧记录
     */
    private void trimHistory() {
        try {
            List<DisPlanMatchRecord> all = matchRecordMapper.selectList(
                    new LambdaQueryWrapper<DisPlanMatchRecord>()
                            .orderByDesc(DisPlanMatchRecord::getCreateTime));
            if (all.size() > MAX_HISTORY) {
                for (int i = MAX_HISTORY; i < all.size(); i++) {
                    matchRecordMapper.deleteById(all.get(i).getId());
                }
                log.info("最近匹配记录超上限，已清理 {} 条", all.size() - MAX_HISTORY);
            }
        } catch (Exception e) {
            log.error("清理最近匹配记录失败", e);
        }
    }

    private PlanMatchResultVO toVO(DisPlanLibrary plan) {
        PlanMatchResultVO vo = new PlanMatchResultVO();
        vo.setId(plan.getId());
        vo.setPlanName(plan.getPlanName());
        vo.setEventType(plan.getEventType());
        vo.setEventLevel(plan.getEventLevel());
        vo.setEventRegion(plan.getEventRegion());
        vo.setResponsibleUnit(plan.getResponsibleUnit());
        vo.setTopic(plan.getTopic());
        vo.setPlanSummary(plan.getPlanSummary());
        vo.setPlanContent(plan.getPlanContent());
        return vo;
    }
}
