package com.supervision.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.supervision.domain.IrKnowledge;
import com.supervision.domain.IrSessionParam;
import com.supervision.domain.IrSqlParam;
import com.supervision.dto.QueryProcessDTO;
import com.supervision.dto.robot.ParamCheckDTO;
import com.supervision.service.IrKnowledgeService;
import com.supervision.service.IrSessionParamService;
import com.supervision.service.IrSqlParamService;
import com.supervision.service.QueryTemplateProcessor;
import com.supervision.util.ParamTypeConverter;
import com.supervision.util.freemark.StringTemplateConfig;
import com.supervision.util.mybatis.RowSqlMapper;
import freemarker.template.TemplateException;
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.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class QueryTemplateProcessorImpl implements QueryTemplateProcessor {

    private final IrKnowledgeService knowledgeService;

    private final IrSqlParamService sqlParamService;

    private final IrSessionParamService sessionParamService;

    private final RowSqlMapper rowSqlMapper;

    @Value("${query.rootPath:/data/intro-robot}")
    private String rootPath;
    @Override
    //note:重新构建一个事务，与调用方事务隔离
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public QueryProcessDTO process(String templateId, String sessionId, Supplier<List<IrSessionParam>> sessionParamSupplier) {

        Assert.notEmpty(templateId, "模板ID不能为空");

        // 根据模板ID查询查询sql模板和结果模板
        IrKnowledge knowledge = knowledgeService.getById(templateId);

        // 校验必要参数
        QueryProcessDTO queryProcessDTO = QueryProcessDTO.builder().state(0).build();
        queryProcessDTO.notNullParam(null, knowledge, "未查询到意图信息");
        queryProcessDTO.notEmptyParam("resultTemplate", knowledge.getResultTemplate(), "结果模板不能为空");
        queryProcessDTO.notNullParam("resultType", knowledge.getResultType(), "结果类型不能为空");
        if (!Integer.valueOf(4).equals(knowledge.getResultType())){
            // 除 档案文件 类型外，其他情况sql模板不能为空
            queryProcessDTO.notEmptyParam("sqlTemplate", knowledge.getSqlTemplate(), "模板内容不能为空");
        }
        if (Integer.valueOf(1).equals(queryProcessDTO.getState())) {
            // 参数校验不通过，返回结果
            return queryProcessDTO;
        }

        // 查询知识查询sql需要的参数
        List<IrSqlParam> paramsList = sqlParamService.lambdaQuery().eq(IrSqlParam::getKnowledgeId, templateId).list();
        // 校验知识查询sql必填参数
        List<IrSessionParam> sessionParams = null == sessionParamSupplier ?
                sessionParamService.lambdaQuery().eq(IrSessionParam::getSessionId, sessionId).list() : sessionParamSupplier.get();
        Set<String> sessionParamsNames = sessionParams.stream().filter(param->StrUtil.isNotEmpty(param.getParamValue())).map(IrSessionParam::getParamName).collect(Collectors.toSet());
        paramsList.stream().filter(param -> Integer.valueOf(1).equals(param.getParamRequire())).forEach(param -> {
            queryProcessDTO.notNullParam(param.getParamName(),
                    sessionParamsNames.contains(param.getParamName())? true : null, param.getParamDesc());
        });
        if (Integer.valueOf(1).equals(queryProcessDTO.getState())) {
            // 参数校验不通过，返回结果
            return queryProcessDTO;
        }

        // 执行查询sql
        List<Map<String, Object>> maps = null;
        try {
            if (Integer.valueOf(4).equals(knowledge.getResultType())){
                Map<String, Object> params = getParams(paramsList, sessionParams);
                String path = localArchivesFilePath(rootPath, MapUtil.getStr(params, "ajid"),
                        MapUtil.getStr(params, "khrzjhm"),MapUtil.getStr(params, "khrmc"));
                if (Objects.nonNull(path)){
                    File tempFile = FileUtil.createTempFile();
                    generateZip(tempFile, FileUtil.loopFiles(path), true);
                    Map<String, Object> stringHashMap = new HashMap<>();
                    stringHashMap.put("fileContent", tempFile);
                    maps = CollUtil.toList(stringHashMap);
                }
            }else {
                maps = rowSqlMapper.selectList(knowledge.getSqlTemplate(), getParams(paramsList, sessionParams));
            }
        } catch (Exception e) {
            log.error("获取数据失败", e);
            queryProcessDTO.setState(2);
            return queryProcessDTO;
        }

        // 组装返回结果
        wrapperSqlResult(knowledge, maps, queryProcessDTO);

        return queryProcessDTO;
    }

    @Override
    public List<ParamCheckDTO> paramCheck(String templateId, String sessionId, List<IrSessionParam> sessionParams) {
        // 查询知识查询sql需要的参数
        List<IrSqlParam> paramsList = sqlParamService.lambdaQuery().eq(IrSqlParam::getKnowledgeId, templateId).list();

        // 校验知识查询sql必填参数
        Set<String> sessionParamsNames = sessionParams.stream().filter(param->StrUtil.isNotEmpty(param.getParamValue())).map(IrSessionParam::getParamName).collect(Collectors.toSet());

        return paramsList.stream().filter(param -> Integer.valueOf(1).equals(param.getParamRequire())).map(param -> {
            if (!sessionParamsNames.contains(param.getParamName())) {
                return new ParamCheckDTO(param.getParamName(), param.getParamDesc(), 1);
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    public void generateZip(File targetZipFile, List<File> sourceFiles, boolean dirWithFlag) {
        if (CollUtil.isNotEmpty(sourceFiles)) {
            File[] fileArr = sourceFiles.toArray(new File[]{});
            ZipUtil.zip(targetZipFile, dirWithFlag, fileArr);
        }
    }

    private void wrapperSqlResult(IrKnowledge knowledge, List<Map<String, Object>> maps, QueryProcessDTO queryProcessDTO) {

        // 先处理文件类型的数据，如果文件为空，则修改处理类型位文本
        if (Integer.valueOf(3).equals(knowledge.getResultType())) {
            // 如果存在byte类型的数据，数据key统一命名为 byteContent
            queryProcessDTO.setContentType(3);
            Map<String, Object> resultMap = CollUtil.getFirst(maps);
            if (CollUtil.isNotEmpty(resultMap)){
                queryProcessDTO.setByteContent((byte[]) CollUtil.getFirst(maps).get("byte_content"));
            }else {
                knowledge.setResultType(1);
                queryProcessDTO.setByteContent(null);
            }
        }

        if (Integer.valueOf(4).equals(knowledge.getResultType())){
            queryProcessDTO.setStringContent(knowledge.getResultTemplate());
            if (CollUtil.isNotEmpty(CollUtil.getFirst(maps))){
                queryProcessDTO.setFileContent((File) CollUtil.getFirst(maps).get("fileContent"));
            }
            queryProcessDTO.setContentType(6);
            try {
                String process = StringTemplateConfig.getInstance().process(knowledge.getResultTemplate(),
                        Objects.isNull(CollUtil.getFirst(maps)) ? new HashMap<>() : CollUtil.getFirst(maps));
                queryProcessDTO.setStringContent(process);
            } catch (IOException | TemplateException e) {
                log.error("执行结果模板失败", e);
                queryProcessDTO.setState(2);
            }
        }

        if (Integer.valueOf(1).equals(knowledge.getResultType())) {
            try {
                String process = StringTemplateConfig.getInstance().process(knowledge.getResultTemplate(),
                        Objects.isNull(CollUtil.getFirst(maps)) ? new HashMap<>() : CollUtil.getFirst(maps));
                queryProcessDTO.setStringContent(process);
                queryProcessDTO.setContentType(1);
            } catch (IOException | TemplateException e) {
                log.error("执行结果模板失败", e);
                queryProcessDTO.setState(2);
            }
        }
        if (Integer.valueOf(2).equals(knowledge.getResultType())) {
            try {
                String process = StringTemplateConfig.getInstance().process(knowledge.getResultTemplate(), maps);
                queryProcessDTO.setStringContent(process);
                queryProcessDTO.setContentType(1);
            } catch (IOException | TemplateException e) {
                log.error("执行结果模板失败", e);
                queryProcessDTO.setState(2);
            }
        }

        // 去除回响应字符串中的特殊字符
        if (StrUtil.isNotEmpty(queryProcessDTO.getStringContent())){
            // 移除所有空白字符（包括空格、换行符和制表符）
            queryProcessDTO.setStringContent(cleanContent(queryProcessDTO.getStringContent()));
        }
    }

    /**
     * 清洗字符串中的特殊字符 包括不可见字符。如果单独出现一个空格，则保留
     * @param input
     * @return
     */
    private String cleanContent(String input){
        if (StrUtil.isEmpty(input)){
            return input;
        }
        String regex = "\\s+";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        while (matcher.find()) {
            String group = matcher.group();
            if (StrUtil.isNotEmpty(group) && !group.equals(" ")){
                input = input.replace(group, "");
            }
        }
        return input;
    }


    private Map<String, Object> getParams(List<IrSqlParam> paramsList, List<IrSessionParam> sessionParams) {
        Map<String, Object> params = new HashMap<>();
        ParamTypeConverter converter = ParamTypeConverter.getInstance();
        for (IrSqlParam param : paramsList) {
            Optional<IrSessionParam> sessionParam = sessionParams.stream().filter(v -> v.getParamName().equals(param.getParamName())).findFirst();
            sessionParam.ifPresent(irSessionParam -> params.put(param.getParamName(), converter.convert(param.getParamType(), irSessionParam.getParamValue())));
        }
        return params;
    }


    private String localArchivesFilePath(String rootPath,String caseId,
                                         String idNumber,String name) {
        String path = StrUtil.join(File.separator,caseId,
                StrUtil.join("-",name,idNumber));
        if (StrUtil.isNotEmpty(rootPath)){
            path = StrUtil.join(File.separator, rootPath, path);
        }
        log.info("定位到的本地文件路径：{}",path);
        // 判断文件是否存在
        if (FileUtil.exist(path)){
            return path;
        }
        log.info("文件路径:{}不存在...",path);
        return null;
    }


}
