/*
* Copyright (c) 京北方信息技术股份有限公司 Corporation 2022 . All rights reserved.
*
*/
package net.northking.cctp.dataPool.api.dataRule.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import com.mifmif.common.regex.Generex;
import net.northking.cctp.common.cache.EntityNameCache;
import net.northking.cctp.common.cache.EntityNameCatalog;
import net.northking.cctp.common.db.BasicService;
import net.northking.cctp.common.db.DefaultPagination;
import net.northking.cctp.common.db.OrderBy;
import net.northking.cctp.common.db.Pagination;
import net.northking.cctp.common.db.annotation.DatabaseReadOnly;
import net.northking.cctp.common.db.excel.AbstractExcelService;
import net.northking.cctp.common.db.excel.ExcelHeader;
import net.northking.cctp.common.dto.CpLogAddDto;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.common.feign.MonsterLogServer;
import net.northking.cctp.common.http.QueryByPage;
import net.northking.cctp.common.s3.SimpleStorageService;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import net.northking.cctp.common.util.UUIDUtil;
import net.northking.cctp.dataPool.bus.LogRabbitMQPublisher;
import net.northking.cctp.dataPool.constants.DataPoolConstant;
import net.northking.cctp.dataPool.db.entity.DataResourcePackage;
import net.northking.cctp.dataPool.db.entity.DataRule;
import net.northking.cctp.dataPool.db.service.DataResourcePackageService;
import net.northking.cctp.dataPool.db.service.DataRuleService;
import net.northking.cctp.dataPool.dto.dataRule.*;
import net.northking.cctp.dataPool.dto.dataSets.AtuDataSetFeignDto;
import net.northking.cctp.dataPool.dto.dataSets.AtuScriptInputSetQuoteFieldDto;
import net.northking.cctp.dataPool.enums.DataPoolError;
import net.northking.cctp.dataPool.feign.ScriptFeignClient;
import net.northking.cctp.dataPool.feign.dto.AtuInputDataSetResultDto;
import net.northking.cctp.dataPool.utils.MinioPathUtils;
import net.northking.cctp.dataPool.utils.MyUrlClassLoader;
import net.northking.cctp.dataPool.utils.ScriptUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 数据规则表服务实现类
 *
 * @author maven-cctp-plugin
 * @since 1.0
 */

@Service
@ConditionalOnMissingBean(name = "DataRuleApiServiceImpl")
public class DataRuleApiServiceImpl extends AbstractExcelService<DataRule> implements DataRuleApiService
{
  /**
   * 日志
   */
  private final static Logger logger = LoggerFactory.getLogger(DataRuleApiServiceImpl.class);

  @Autowired
  private DataRuleService dataRuleService;
  @Autowired
  private DataResourcePackageService dataResourcePackageService;
  @Autowired
  private MinioPathUtils pathUtils;
  @Autowired
  private EntityNameCache entityNameCache;
  @Autowired
  private SimpleStorageService simpleStorageService;
  @Autowired
  private LogRabbitMQPublisher logPublisher;
  @Autowired
  private ScriptFeignClient scriptFeignClient;

  @Override
  public BasicService<DataRule> getService()
  {
    return this.dataRuleService;
  }

  @Override
  public ExcelHeader[] excelHeaders()
  {
    ExcelHeader ruleId = new ExcelHeader("规则id", "ruleId", String.class);
    ExcelHeader name = new ExcelHeader("规则名称", "name", String.class);
    ExcelHeader type = new ExcelHeader("规则类型", "type", String.class);
    ExcelHeader contentExpression = new ExcelHeader("正则表达式、脚本编码", "contentExpression", String.class);
    ExcelHeader jarIds = new ExcelHeader("资源包集合", "jarIds", String.class);
    ExcelHeader proId = new ExcelHeader("系统id", "proId", String.class);
    ExcelHeader tenantId = new ExcelHeader("租户id", "tenantId", String.class);
    ExcelHeader createdBy = new ExcelHeader("创建人", "createdBy", String.class);
    ExcelHeader createdTime = new ExcelHeader("创建时间", "createdTime", Date.class);
    ExcelHeader updatedBy = new ExcelHeader("更新人", "updatedBy", String.class);
    ExcelHeader updatedTime = new ExcelHeader("更新时间", "updatedTime", Date.class);
    return new ExcelHeader[]{};
  }

  /**
   * Excel导入，入库之前的处理
   * @param records 从Excel读取到的记录
   * @return 待入库的记录
   */
  @Override
  protected List<DataRule> beforeInsertBatch(List<DataRule> records)
  {
    return records;
  }

  /**
   * 写入Excel之前的处理<br>
   * 数据的特殊处理在此处加工处理
   *
   * @param record 来自数据库记录
   * @return 待写入Excel的记录
   */
  @Override
  protected DataRule beforeWriteExcel(DataRule record)
  {
    return record;
  }

  //= Excel 导入导出相关代码 end =======================//

  //= 增删改查 相关代码 start ==================//
  @Override
  @DatabaseReadOnly
  public Pagination<DataRuleDetailDto> pagingQuery(QueryByPage<DataRuleQueryDto> queryByPage)
  {
    DefaultPagination<DataRuleDetailDto> pagination = new DefaultPagination<>();
    DataRuleQueryDto queryDto = queryByPage.getQuery();
    //处理查询参数
    if (null != queryDto) {
      DataRuleQueryDto dataRuleQueryDto = handleQueryParam(queryDto);
      //无符合条件创建人
      if (null == dataRuleQueryDto) {
        return pagination;
      }
      queryByPage.setQuery(dataRuleQueryDto);
    }
    //数据租户隔离
    queryByPage.getQuery().setTenantId(NKSecurityContext.getTenantId());
    handlePageParam(queryByPage);  //处理分页、排序参数
    pagination.setPageSize(queryByPage.getPageSize());
    pagination.setPageNo(queryByPage.getPageNo());

    Page<DataRuleDetailDto> page = PageMethod.startPage(queryByPage.getPageNo(), queryByPage.getPageSize());
    List<DataRuleDetailDto> result = dataRuleService.getDataRules(queryByPage);
    if (!CollectionUtils.isEmpty(result)) {
      for (DataRuleDetailDto data : result) {
        data.setProjectName(entityNameCache.translateById(EntityNameCatalog.MONSTER_SYSTEM.name(),data.getProId()));
        data.setCreator(entityNameCache.translateById(EntityNameCatalog.MONSTER_USER,data.getCreatedBy()));
        data.setModifier(entityNameCache.translateById(EntityNameCatalog.MONSTER_USER,data.getUpdatedBy()));
      }
    }
    pagination.setRecords(result);
    pagination.setRecordCount(page.getTotal());
    return pagination;
  }



  @Override
  public String add(DataRuleAddDto dto)
  {
    logger.debug("规则重名校验");
    long startTime = System.currentTimeMillis();
    DataRule dataRule = new DataRule();
    dataRule.setName(dto.getName());
    dataRule.setProId(dto.getProId());
    List<DataRule> dataRuleList = dataRuleService.query(dataRule);
    if (!CollectionUtils.isEmpty(dataRuleList)) {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_NAME_IS_EXSIT);
    }
    logger.debug("规则类型校验");
    String type = dto.getType();
    String contentExpression = dto.getContentExpression();
    if (!StringUtils.hasText(contentExpression)) {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_EXP_IS_NULL);
    }
    //校验是否为正确表达式
    if ("1".equals(type)) {
      try {
        Pattern.compile(contentExpression);
        //特殊符号前反斜杠校验 @#
        char[] chars = contentExpression.toCharArray();
        for (int i = 0; i < chars.length; i++) {
          if (chars[i] == '@' || chars[i] == '#') {
            if (i == 0 || chars[i-1] != '\\') {
              throw new PlatformRuntimeException(DataPoolError.DATA_RULE_REXP_IS_VALID);
            }
          }
        }
        //生成随机字符串
        Generex generex = new Generex(contentExpression);
        String genValue = generex.random();
        logger.debug("Rexp:{}, res: {}",contentExpression,genValue);
      }catch (Exception e) {
        throw new PlatformRuntimeException(DataPoolError.DATA_RULE_REXP_IS_VALID);
      }
    }
    logger.debug("保存数据库数据");
    String uuid = UUIDUtil.create32UUID();
    DataRule entity = new DataRule();
    BeanUtils.copyProperties(dto, entity);
    entity.setRuleId(uuid);
    entity.setCreatedBy(NKSecurityContext.getUserId());
    entity.setCreatedTime(new Date());
    entity.setUpdatedBy(entity.getCreatedBy());
    entity.setUpdatedTime(entity.getCreatedTime());
    entity.setTenantId(NKSecurityContext.getTenantId());
    this.dataRuleService.insert(entity);
    logger.debug("新增模糊数据完成，耗时: {}", System.currentTimeMillis() - startTime);
    return uuid;
  }

  @Override
  @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
  public Integer updateByPK(DataRuleUpdateDto dto) throws IllegalAccessException
  {
    //规则存在校验
    DataRule oldRule = dataRuleService.findByPrimaryKey(dto.getRuleId());
    if (null == oldRule) {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_IS_NULL);
    }
    //规则同名校验
    if (!StringUtils.hasText(dto.getProId())) {
      dto.setProId(oldRule.getProId());
    }
    Integer count = dataRuleService.queryRuleByName(dto);
    if (count > 0) {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_NAME_IS_EXSIT);
    }
    //规则类型校验
    String type = dto.getType();
    String contentExpression = dto.getContentExpression();
    if (!StringUtils.hasText(contentExpression)) {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_EXP_IS_NULL);
    }
    //校验是否为正确表达式
    if ("1".equals(type)) {
      try {
        Pattern.compile(contentExpression);
        //特殊符号前反斜杠校验 @#
        char[] chars = contentExpression.toCharArray();
        for (int i = 0; i < chars.length; i++) {
          if (chars[i] == '@' || chars[i] == '#') {
            if (i == 0 || chars[i-1] != '\\') {
              throw new PlatformRuntimeException(DataPoolError.DATA_RULE_REXP_IS_VALID);
            }
          }
        }
        //生成随机字符串
        Generex generex = new Generex(contentExpression);
        String genValue = generex.random();
        logger.debug("Rexp:{}, res: {}",contentExpression,genValue);
      }catch (Exception e) {
        throw new PlatformRuntimeException(DataPoolError.DATA_RULE_REXP_IS_VALID);
      }
    }
    //修改数据规则
    DataRule entity = new DataRule();
    BeanUtils.copyProperties(oldRule, entity);
    entity.setName(dto.getName());
    entity.setContentExpression(dto.getContentExpression());
    entity.setJarIds(dto.getJarIds());
    entity.setType(dto.getType());
    entity.setUpdatedBy(NKSecurityContext.getUserId());
    entity.setUpdatedTime(new Date());
    int res = this.dataRuleService.updateByPrimaryKey(entity);
    return res;
  }

  @Override
  @DatabaseReadOnly
  public DataRuleDetailDto findByPK(String uuid)
  {
    DataRule rule = dataRuleService.findByPrimaryKey(uuid);
    if (null == rule) {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_IS_NULL);
    }
    DataRuleDetailDto dto = new DataRuleDetailDto();
    BeanUtils.copyProperties(rule, dto);
    //封装资源包信息列表
    String jarIds = rule.getJarIds();
    if (StringUtils.hasText(jarIds)) {
      String[] jarIdList = jarIds.split(",");
      List<String> idList = Arrays.asList(jarIdList);
      List<DataResourcePackage> jarInfos = dataResourcePackageService.queryByIds(idList);
      List<DataResourcePackageInfoDto> jars = new ArrayList<>();
      for (DataResourcePackage jarInfo : jarInfos) {
        DataResourcePackageInfoDto infoDto = new DataResourcePackageInfoDto();
        infoDto.setJarId(jarInfo.getId());
        infoDto.setName(jarInfo.getName());
        jars.add(infoDto);
      }
      dto.setJarInfoList(jars);
    }
    return dto;
  }

  @Override
  @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
  public Integer deleteByPK(String uuid)
  {
    DataRule rule = dataRuleService.findByPrimaryKey(uuid);
    if (null == rule) {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_IS_NULL);
    }
    // 校验是否被引用
    //校验数据是否存在关联
    List<String> list = new ArrayList<>();
    list.add(uuid);
    List<AtuInputDataSetResultDto> dtos = scriptFeignClient.selectLinkBySetId(list);
    for (AtuInputDataSetResultDto dto : dtos) {
      if (dto.getCountNum() > 0){
        throw new PlatformRuntimeException(DataPoolError.FUZZY_DATA_SET_HAS_LINK);
      }
    }
    //删除数据规则
    int num = dataRuleService.deleteByPrimaryKey(uuid);
    //记录平台日志
    CpLogAddDto cpLogAddDto = new CpLogAddDto();
    String projectName = entityNameCache.translateById(EntityNameCatalog.MONSTER_SYSTEM.name(), rule.getProId());
    cpLogAddDto.setContent("【" + DataPoolConstant.CPLOGADDDTO_CONTENT_DELETE+"模糊测试数据】系统：" + projectName + "，规则名称：" + rule.getName());
    cpLogAddDto.setModule(DataPoolConstant.CPLOGADDDTO_DATA_POOL_MANAGEMENT);
    cpLogAddDto.setType(DataPoolConstant.CPLOGADDDTO_LOG_TYPE_DELETE);
    MonsterLogServer.sendLogToMonster(cpLogAddDto);
    logPublisher.deployLogContent(cpLogAddDto);
    return num;
  }

  @Override
  public List<String> executeRule(DataRuleExecuteDto dto) {
    List<String> res = new ArrayList<>();
    //规则类型校验
    String type = dto.getType();
    String contentExpression = dto.getContentExpression();
    MyUrlClassLoader loader = null;
    if (!StringUtils.hasText(contentExpression)) {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_EXP_IS_NULL);
    }
    //正则表达式类型
    if ("1".equals(type)) {
      //校验是否为正确表达式
      try {
        Pattern.compile(contentExpression);
        //特殊符号前反斜杠校验 @#
        char[] chars = contentExpression.toCharArray();
        for (int i = 0; i < chars.length; i++) {
          if (chars[i] == '@' || chars[i] == '#') {
            if (i == 0 || chars[i-1] != '\\') {
              throw new PlatformRuntimeException(DataPoolError.DATA_RULE_REXP_IS_VALID);
            }
          }
        }
        //表达式含Unicode需解码
        String exp = decodeExpression(chars);
        Pattern.compile(exp);
        //生成随机字符串
        Generex generex = new Generex(exp);
        String genValue = generex.random();
        logger.debug("Rexp:{}, exp:{}, res: {}",contentExpression,exp,genValue);
        res.add(genValue);
      }catch (Exception e) {
        throw new PlatformRuntimeException(DataPoolError.DATA_RULE_REXP_IS_VALID);
      }
    }else if ("0".equals(type)) {
      //beanshell脚本类型
      String jarIds = dto.getJarIds();
      loader = new MyUrlClassLoader();
      if (StringUtils.hasText(jarIds)) {
        //有jar包依赖
        String[] jars = jarIds.split(",");
        //查询jar包路径
        for (String jar : jars) {
          //跳过空串
          if (!StringUtils.hasText(jar)) {
            continue;
          }
          //根据jar包id获取路径（同一jar包被删除后重新上传jar包id会改变）
          DataResourcePackage resourcePackage = dataResourcePackageService.findByPrimaryKey(jar);
          //下载jar包，并进行加载
          try {
            if (Objects.nonNull(resourcePackage)) {//资源包记录存在
              if (StringUtils.hasText(resourcePackage.getPackageAddr())) {
                //路径存在
                String path = resourcePackage.getPackageAddr();
                String[] result = pathUtils.idToPath(path);
                File file = simpleStorageService.downloadAsFile(result[0], "/" + result[1]);
                loader.addURLFile(file);
              }
            }
          } catch (Exception e) {
            logger.error("资源包加载异常", e);
            throw new PlatformRuntimeException(DataPoolError.DATA_RESOURCE_PACKAGE_LOAD_FAIL);
          }
        }
      }
      try {
        //执行脚本1次
        res = ScriptUtil.executesBeanShell(contentExpression,1);
        if (res.contains(null)) {
          throw new PlatformRuntimeException(DataPoolError.DATA_RULE_EXECUTE_RESULT_IS_NULL);
        }
      } catch (Exception e) {
        logger.error("脚本执行异常", e);
        throw new PlatformRuntimeException(DataPoolError.DATA_RULE_EXECUTE_RESULT_IS_EXCEPTION);
      }finally {
        if (loader != null){
          //删除文件夹下文件
          loader.unloadJarFiles();
          try {
            loader.close();
          } catch (IOException e) {
            logger.error("",e);
          }
        }
      }
    }else {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_NOT_SUPPORT_TYPE);
    }
    return res;
  }

  /**
   * 将Unicode字符串解码成普通字符
   * @param chars
   * @return
   */
  private String decodeExpression(char[] chars) {
    //存储解码后的字符串
    StringBuilder tempSb = new StringBuilder();
    for (int i = 0; i < chars.length; i++) {
      //查找unicode前缀
      if (chars[i] == '\\' && i != chars.length-1 && chars[i+1] == 'u') {
        StringBuilder hexSb = new StringBuilder(); //unicode中十六进制部分
        StringBuilder zeroSb = new StringBuilder(); //unicode中左位补零部分
        StringBuilder otherSb = new StringBuilder(); //不满足unicode十六进制部分
        //从第二位开始，跳过前缀直接处理十六进制（后四位）
        for (int j = 2; j <= 5; j++) {
          char tmpChar = chars[i + j];
          //判断当前符号是否满足十六进制条件 0-9a-f
          if (checkHex(tmpChar)) {
            hexSb.append(tmpChar);
          }else {
            //后四位中不满足条件，则左位补零凑齐四位，同时记录后续不满足字符
            zeroSb.append("0");
            otherSb.append(tmpChar);
          }
        }
        //补零位加上十六进制位凑齐四位完整unicode码
        zeroSb.append(hexSb);
        //十六进制转换成字符
        char ch = (char) Integer.parseInt(zeroSb.toString(),16);
        tempSb.append(ch);
        tempSb.append(otherSb);
        //直接从转换后的字符开始处理
        i = i+5;
        continue;
      }
      //非unicode编码部分则直接记录
      tempSb.append(chars[i]);
    }
    return tempSb.toString();
  }

  /**
   * 判断当前符号是否满足十六进制条件 0-9a-fA-F
   * @param c
   * @return
   */
  private boolean checkHex(char c) {
    boolean res = false;
    if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9')) {
      res = true;
    }
    return res;
  }

  @Override
  public String getRuleDataByName(String ruleName, String proId) {
    DataRule dataRule = new DataRule();
    dataRule.setName(ruleName);
    dataRule.setProId(proId);
    List<DataRule> dataRules = dataRuleService.query(dataRule);
    if (!CollectionUtils.isEmpty(dataRules)) {
      DataRule tempRule = dataRules.get(0);
      if (null != tempRule) {
        DataRuleExecuteDto execRule = new DataRuleExecuteDto();
        BeanUtils.copyProperties(tempRule,execRule);
        try {
          List<String> resultList = executeRule(execRule);
          if (!CollectionUtils.isEmpty(resultList)) {
            return resultList.get(0);
          }else {
            throw new PlatformRuntimeException(DataPoolError.DATA_RULE_EXECUTE_RESULT_IS_NULL);
          }
        }catch (PlatformRuntimeException e) {
          throw e;
        }
      }
    }else {
      throw new PlatformRuntimeException(DataPoolError.DATA_RULE_IS_NULL);
    }
    return null;
  }

    @Override
    public Map<String, String> queryFuzzyDataNameMap(AtuDataSetFeignDto setFeignDto) {
      Map<String, String> dataRuleNameMap = new HashMap<>();
      Set<String> dataSetList = setFeignDto.getDataSetList();
      if (dataSetList == null || dataSetList.size() <= 0){
        logger.debug("查询的id集合为空");
        return dataRuleNameMap;
      }
      List<DataRule>  dataRuleList = dataRuleService.queryByIdSet(dataSetList);
      if (dataRuleList == null || dataRuleList.size() <= 0){
        logger.debug("查询的数据规则为空");
        return dataRuleNameMap;
      }
      for (DataRule dataRule : dataRuleList) {
        dataRuleNameMap.put(dataRule.getRuleId(), dataRule.getName());
      }
      return dataRuleNameMap;
    }

    @Override
    public AtuScriptInputSetQuoteFieldDto queryFuzzyDataQuoteInfo(AtuScriptInputSetQuoteFieldDto atuScriptInputSetQuoteFieldDto) {
      AtuScriptInputSetQuoteFieldDto quoteFieldDto = new AtuScriptInputSetQuoteFieldDto();
      if (atuScriptInputSetQuoteFieldDto.getSetId() == null){
        throw new PlatformRuntimeException(DataPoolError.PARAM_IS_MISSING);
      }
      DataRule dataRule = dataRuleService.findByPrimaryKey(atuScriptInputSetQuoteFieldDto.getSetId());
      if (dataRule == null){
        throw new PlatformRuntimeException(DataPoolError.SETS_NO_EXISTS);
      }
      quoteFieldDto.setFullName(dataRule.getName());
      quoteFieldDto.setName(dataRule.getName());
      quoteFieldDto.setPath(dataRule.getName());
      quoteFieldDto.setType(DataPoolConstant.INPUT_SET_QUOTE_TYPE_FUZZY);
      quoteFieldDto.setValue(DataPoolConstant.FUZZY_DATA_QUOTE_PREFIX + dataRule.getName() + DataPoolConstant.FUZZY_DATA_QUOTE_SUFFIX);
      return quoteFieldDto;
    }

    /**
   * 处理分页查询参数
   *
   * @param query
   */
  private void handlePageParam(QueryByPage<DataRuleQueryDto> query) {
    if (query.getPageNo() <= 0) {
      query.setPageNo(1);
    }
    if (query.getPageSize() <= 0 || query.getPageSize() > 500) {
      query.setPageSize(500);
    }
    OrderBy sort = query.toSqlOrderBy();
    if (sort != null && sort.isNotEmpty() && StringUtils.hasText(sort.toString())) {
      PageMethod.orderBy(sort.toString());
    }
  }

  /**
   * 处理查询参数
   *
   * @param query
   * @return
   */
  private DataRuleQueryDto handleQueryParam(DataRuleQueryDto query) {
    DataRuleQueryDto res = new DataRuleQueryDto();
    BeanUtils.copyProperties(query, res);
    //创建人查询参数修改
    String creatorName = query.getCreator();
    if (StringUtils.hasText(creatorName)) {
      List<String> creatorIds = new ArrayList<>();
      creatorIds.add(creatorName);
      res.setCreatorIds(creatorIds);
    }
    return res;
  }
}
