package com.supervision.police.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.supervision.common.domain.R;
import com.supervision.common.utils.IPages;
import com.supervision.common.utils.StringUtils;
import com.supervision.police.domain.*;
import com.supervision.police.dto.AtomicData;
import com.supervision.police.dto.CaseAtomicIndexDTO;
import com.supervision.police.dto.CaseAtomicResultWrapper;
import com.supervision.police.dto.JudgeLogic;
import com.supervision.police.mapper.CasePersonMapper;
import com.supervision.police.mapper.ModelAtomicResultMapper;
import com.supervision.police.mapper.ModelIndexMapper;
import com.supervision.police.service.ComDictionaryService;
import com.supervision.police.service.ModelAtomicIndexService;
import com.supervision.police.service.ModelCaseService;
import com.supervision.police.service.ModelIndexService;
import com.supervision.utils.SqlParserUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 指标表(ModelIndex)表服务实现类
 *
 * @author qmy
 * @since 2024-07-05 09:20:10
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ModelIndexServiceImpl extends ServiceImpl<ModelIndexMapper, ModelIndex> implements ModelIndexService {

    private final ComDictionaryService comDictionaryService;

    private final ModelIndexMapper modelIndexMapper;

    private final ModelAtomicIndexService modelAtomicIndexService;

    private final ModelCaseService modelCaseService;

    private final ModelAtomicResultMapper modelAtomicResultMapper;

    private final CasePersonMapper casePersonMapper;

    @Value("${case.evidence.table}")
    private List<String> allowedTables;

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
    public R<?> selectAll(ModelIndex modelIndex, Integer page, Integer size) {
        IPage<ModelIndex> iPage = new Page<>(page, size);
        LambdaQueryWrapper<ModelIndex> wrapper = Wrappers.lambdaQuery();
        wrapper.like(modelIndex.getName() != null, ModelIndex::getName, modelIndex.getName())
                .like(modelIndex.getShortName() != null, ModelIndex::getShortName, modelIndex.getShortName())
                .like(modelIndex.getRemark() != null, ModelIndex::getRemark, modelIndex.getRemark())
                .eq(StringUtils.isNotEmpty(modelIndex.getIndexType()), ModelIndex::getIndexType, modelIndex.getIndexType())
                .eq(StringUtils.isNotEmpty(modelIndex.getCaseType()), ModelIndex::getCaseType, modelIndex.getCaseType())
                .eq(ModelIndex::getDataStatus, "1");
        iPage = modelIndexMapper.selectPage(iPage, wrapper);
        List<ModelIndex> records = iPage.getRecords();
        List<ComDictionary> dicts = comDictionaryService.list();
        for (ModelIndex index : records) {
            index.setIndexTypeName(comDictionaryService.getName(dicts, "index_type", index.getIndexType()));
            index.setCaseTypeName(comDictionaryService.getName(dicts, "case_type", index.getCaseType()));
            //原子指标
            String judgeLogic = index.getJudgeLogic();
            List<String> ids = new ArrayList<>();
            if (StringUtils.isNotEmpty(judgeLogic)) {
                List<JudgeLogic> logic = JSONUtil.toList(judgeLogic, JudgeLogic.class);
                for (JudgeLogic judge : logic) {
                    List<AtomicData> atomicData = judge.getAtomicData();
                    for (AtomicData atomic : atomicData) {
                        ids.add(atomic.getAtomicIndex());
                    }
                }
                List<ModelAtomicIndex> atomicIndexList = modelAtomicIndexService.selectBatchIds(ids);
                index.setAtomicIndexList(atomicIndexList);
            }
            index.setAtomicIndexNum(ids.size());
        }
        iPage.setRecords(records);
        return R.ok(IPages.buildDataMap(iPage));
    }

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
    public R<?> addOrUpd(ModelIndex modelIndex) {
        int i = 0;
        if (StringUtils.isEmpty(modelIndex.getId())) {
            i = modelIndexMapper.insert(modelIndex);
        } else {
            i = modelIndexMapper.updateById(modelIndex);
        }
        if (i > 0) {
            return R.okMsg("保存成功");
        } else {
            return R.fail("保存失败");
        }
    }

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
    public R<?> del(String id) {
        ModelIndex index = modelIndexMapper.selectById(id);
        index.setDataStatus(StringUtils.getUUID());
        int i = modelIndexMapper.updateById(index);
        if (i > 0) {
            return R.okMsg("删除成功");
        } else {
            return R.fail("删除失败");
        }
    }

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
    public R<?> selectAllAtomic(ModelAtomicIndex modelAtomicIndex, Integer page, Integer size) {
        IPage<ModelAtomicIndex> iPage = new Page<>(page, size);
        iPage = modelAtomicIndexService.selectAll(iPage, modelAtomicIndex);
        List<ModelAtomicIndex> records = iPage.getRecords();
        List<ComDictionary> dicts = comDictionaryService.list();
        for (ModelAtomicIndex index : records) {
            index.setCaseTypeName(comDictionaryService.getName(dicts, "case_type", index.getCaseType()));
            index.setIndexSourceName(comDictionaryService.getName(dicts, "index_source", index.getIndexSource()));
            index.setRecordTypeName(comDictionaryService.getName(dicts, "record_type", index.getRecordType()));
        }
        iPage.setRecords(records);
        return R.ok(IPages.buildDataMap(iPage));
    }

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
    public R<?> addOrUpdAtomic(ModelAtomicIndex modelAtomicIndex) {
        int i = 0;
        if (StringUtils.equals("2", modelAtomicIndex.getIndexSource())) {
            // 如果查询类型为数据查询,则校验查询语句
            Assert.notEmpty(modelAtomicIndex.getQueryLang(), "查询语言不能为空");
            Assert.isTrue(checkSql(modelAtomicIndex.getQueryLang()), "查询语句不合法");
        }
        // 校验是否已经存在了相同名称的原子指标
        ModelAtomicIndex existIndex = modelAtomicIndexService.lambdaQuery().eq(ModelAtomicIndex::getName, modelAtomicIndex.getName()).last("limit 1").one();
        if (ObjectUtil.isNotEmpty(existIndex) && !StringUtils.equals(modelAtomicIndex.getId(), existIndex.getId())){
            throw new RuntimeException("已存在相同名称的原子指标");
        }
        if (StringUtils.isEmpty(modelAtomicIndex.getId())) {
            i = modelAtomicIndexService.getMapper().insert(modelAtomicIndex);
        } else {
            i = modelAtomicIndexService.getMapper().updateById(modelAtomicIndex);
        }
        if (i > 0) {
            return R.okMsg("保存成功");
        } else {
            return R.fail("保存失败");
        }
    }

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
    public R<?> delAtomic(String id) {
        ModelAtomicIndex index = modelAtomicIndexService.getMapper().selectById(id);
        index.setDataStatus(StringUtils.getUUID());
        int i = modelAtomicIndexService.getMapper().updateById(index);
        if (i > 0) {
            return R.okMsg("删除成功");
        } else {
            return R.fail("删除失败");
        }
    }

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
    public List<CaseAtomicIndexDTO> listCaseAtomicIndex(String caseId, String indexSource) {

        Assert.notEmpty(caseId, "案件id不能为空");
        ModelCase modelCase = modelCaseService.getById(caseId);
        Assert.notNull(modelCase, "案件不存在");
        String caseType = modelCase.getCaseType();
        Assert.notEmpty(caseType, "案件类型不能为空");
        List<CasePerson> casePersonList = casePersonMapper.selectList(Wrappers.lambdaQuery(CasePerson.class)
                .eq(CasePerson::getCaseId, caseId).eq(CasePerson::getRoleCode, "1"));
        Assert.notEmpty(casePersonList, "案件行为人不能为空!");
        String actorId = CollUtil.getFirst(casePersonList).getId();
        // 获取案件类型对应的指标
        List<ModelIndex> modelIndexList = modelIndexMapper.selectList(
                Wrappers.lambdaQuery(ModelIndex.class)
                        .eq(ModelIndex::getCaseType, caseType).eq(ModelIndex::getDataStatus, "1"));
        if (CollUtil.isEmpty(modelIndexList)) {
            return new ArrayList<>(1);
        }

        // 从指标中计算出所有原子指标id
        List<String> automicIndexIds = modelIndexList.stream().filter(modelIndex -> StrUtil.isNotEmpty(modelIndex.getJudgeLogic()))
                .map(modelIndex -> pickAtomicIndexIds(modelIndex.getJudgeLogic()))
                .flatMap(Collection::stream).distinct().toList();

        if (CollUtil.isEmpty(automicIndexIds)) {
            return new ArrayList<>(1);
        }

        // 查询原子指标相关信息
        List<ModelAtomicIndex> modelAtomicIndexList = modelAtomicIndexService.listCaseAtomicIndex(automicIndexIds, caseType, indexSource);
        if (CollUtil.isEmpty(modelAtomicIndexList)) {
            return new ArrayList<>(1);
        }
        Map<String, ModelAtomicIndex> modelAtomicIndexMap = modelAtomicIndexList.stream().collect(Collectors.toMap(ModelAtomicIndex::getId, v -> v, (v1, v2) -> v1));

        // 查询判定结果数据
        List<ModelAtomicResult> modelAtomicResults = modelAtomicResultMapper.selectList(
                Wrappers.lambdaQuery(ModelAtomicResult.class).eq(ModelAtomicResult::getCaseId, caseId)
                        .eq(ModelAtomicResult::getCasePersonId, actorId)
                        .in(ModelAtomicResult::getAtomicId, automicIndexIds));

        Map<String, ModelAtomicResult> modelAtomicResultMap = modelAtomicResults.stream()
                .filter(modelAtomicResult -> StrUtil.isNotEmpty(modelAtomicResult.getAtomicId())).collect(Collectors.toMap(ModelAtomicResult::getAtomicId, v -> v, (v1, v2) -> v1));

        Map<String, List<ModelIndex>> modelIndexMapAtomic = groupModelIndexByAtomicIndexId(modelIndexList);

        // 以原子指标为基准,组装数据
        return modelAtomicIndexList.stream().flatMap(atomicIndex ->
                modelIndexMapAtomic.get(atomicIndex.getId()).stream().map(modelIndex ->
                        new CaseAtomicIndexDTO(atomicIndex, modelIndex, modelAtomicResultMap.get(atomicIndex.getId())))
        ).toList();
    }

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
    public Boolean saveCaseAtomicResult(CaseAtomicResultWrapper caseAtomicResultWrapper) {

        Assert.notEmpty(caseAtomicResultWrapper.getCaseId(), "案件id不能为空!");
        ModelCase modelCase = modelCaseService.getById(caseAtomicResultWrapper.getCaseId());
        Assert.notNull(modelCase, "案件不存在!");
        // 设置行为人id
        List<CasePerson> casePersonList = casePersonMapper.selectList(Wrappers.lambdaQuery(CasePerson.class)
                .eq(CasePerson::getCaseId, modelCase.getId()).eq(CasePerson::getRoleCode, "1"));
        Assert.notEmpty(casePersonList, "案件行为人不能为空!");
        caseAtomicResultWrapper.setActorId(CollUtil.getFirst(casePersonList).getId());


        //清空人工评估结果
        removeCaseAtomicResult(caseAtomicResultWrapper.getCaseId(), modelCase.getCaseType(),
                caseAtomicResultWrapper.getActorId(), "1");

        // 保存原子评估结果
        caseAtomicResultWrapper.getCaseAtomicIndexList().stream().map(caseAtomicIndexDTO ->
                        caseAtomicIndexDTO.toModelAtomicResult(
                                caseAtomicResultWrapper.getCaseId(), caseAtomicResultWrapper.getActorId())).toList()
                .forEach(modelAtomicResultMapper::insert);

        return true;
    }


    @Override
    public boolean checkSql(String sql) {
        log.info("checkSql:{}", sql);
        if (StringUtils.isEmpty(sql)) {
            return false;
        }
        if (CollUtil.isEmpty(this.allowedTables)) {
            log.info("checkSql:未配置允许的表");
            return false;
        }
        MySqlStatementParser parser = new MySqlStatementParser(sql);
        SQLStatement sqlStatement = SqlParserUtil.parseStatement(parser);
        if (Objects.isNull(sqlStatement)) {
            log.warn("checkSql sql:{}语句解析失败", sql);
            return false;
        }
        String sqlType = SqlParserUtil.detectSQLType(sqlStatement);
        if (!"SELECT".equals(sqlType)) {
            log.warn("checkSql:只支持查询类型语句");
            return false;
        }
        List<String> tableList = SqlParserUtil.extractTableNames(sqlStatement);
        if (CollUtil.isEmpty(tableList)) {
            log.warn("checkSql:未检测到表");
            return false;
        }

        long count = tableList.stream().filter(table -> !this.allowedTables.contains(table)).count();
        if (count > 0) {
            log.warn("checkSql:表{}不在允许的表列表中", tableList);
            return false;
        }
        return true;
    }

    /**
     * 清空案件下的评估结果
     *
     * @param caseId      案件id
     * @param caseType    案件类型
     * @param actorId     行为人id
     * @param indexSource 原子指标来源
     */
    private void removeCaseAtomicResult(String caseId, String caseType, String actorId, String indexSource) {
        List<ModelAtomicIndex> modelAtomicIndexList = modelAtomicIndexService.listCaseAtomicIndex(null, caseType, indexSource);
        List<String> automicIndexIds = modelAtomicIndexList.stream().map(ModelAtomicIndex::getId).distinct().toList();
        modelAtomicResultMapper.delete(Wrappers.lambdaQuery(ModelAtomicResult.class)
                .eq(ModelAtomicResult::getCaseId, caseId)
                .eq(ModelAtomicResult::getCasePersonId, actorId)
                .in(CollUtil.isNotEmpty(automicIndexIds), ModelAtomicResult::getAtomicId, automicIndexIds));
    }

    /**
     * 从逻辑中获取原子指标id
     *
     * @param judgeLogic 判断逻辑json字符串
     * @return 原子指标id(不重复)
     */
    private List<String> pickAtomicIndexIds(String judgeLogic) {
        List<String> ids = new ArrayList<>();
        List<JudgeLogic> logic = JSONUtil.toList(judgeLogic, JudgeLogic.class);
        for (JudgeLogic judge : logic) {
            List<AtomicData> atomicData = judge.getAtomicData();
            for (AtomicData atomic : atomicData) {
                if (!ids.contains(atomic.getAtomicIndex())) {
                    ids.add(atomic.getAtomicIndex());
                }
            }
        }
        return ids;
    }

    private Map<String, List<ModelIndex>> groupModelIndexByAtomicIndexId(List<ModelIndex> modelIndexList) {
        Map<String, List<ModelIndex>> groupMap = new HashMap<>();
        if (CollUtil.isEmpty(modelIndexList)) {
            return groupMap;
        }
        for (ModelIndex modelIndex : modelIndexList) {
            if (StrUtil.isEmpty(modelIndex.getJudgeLogic())) {
                continue;
            }
            String judgeLogic = modelIndex.getJudgeLogic();
            List<JudgeLogic> judgeLogicList = JSONUtil.toList(judgeLogic, JudgeLogic.class);
            for (JudgeLogic logic : judgeLogicList) {
                List<AtomicData> atomicData = logic.getAtomicData();
                if (CollUtil.isEmpty(atomicData)) {
                    continue;
                }
                for (AtomicData atomic : atomicData) {
                    List<ModelIndex> modelIndexs = groupMap.getOrDefault(atomic.getAtomicIndex(), new ArrayList<>());
                    modelIndexs.add(modelIndex);
                    groupMap.put(atomic.getAtomicIndex(), modelIndexs);
                }
            }
        }
        return groupMap;
    }
}