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

import net.northking.cctp.common.cache.EntityNameCache;
import net.northking.cctp.common.cache.EntityNameCatalog;
import net.northking.cctp.common.db.Pagination;
import net.northking.cctp.common.db.annotation.DatabaseReadOnly;
import net.northking.cctp.common.db.annotation.DatabaseWriteOnly;
import net.northking.cctp.common.dto.AssociatedFilesDto;
import net.northking.cctp.common.dto.CpLogAddDto;
import net.northking.cctp.common.enums.FileBusinessTypeEnum;
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.FileDownloadException;
import net.northking.cctp.common.s3.FileUploadException;
import net.northking.cctp.common.s3.NKFile;
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.element.core.Library;
import net.northking.cctp.element.core.keyword.KeywordArgument;
import net.northking.cctp.element.core.keyword.KeywordInfo;
import net.northking.cctp.element.scanner.JarLibraryScanner;
import net.northking.cctp.platform.bus.notice.LogPublisher;
import net.northking.cctp.platform.constants.ErrorMessage;
import net.northking.cctp.platform.constants.PlatformAppError;
import net.northking.cctp.platform.constants.PlatformConstants;
import net.northking.cctp.platform.db.entity.CpComponentList;
import net.northking.cctp.platform.db.entity.CpComponentParamter;
import net.northking.cctp.platform.db.service.CpComponentLibraryService;
import net.northking.cctp.platform.db.entity.CpComponentLibrary;

import net.northking.cctp.platform.db.service.CpComponentListService;
import net.northking.cctp.platform.db.service.CpComponentParamterService;
import net.northking.cctp.platform.dto.comlibrary.*;
import net.northking.cctp.platform.feign.AttachmentFeignClient;
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.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 平台组件库服务实现类
 *
 * @author maven-cctp-plugin
 * @since 1.0
 */

@Service
@ConditionalOnMissingBean(name = "CpComponentLibraryApiServiceImpl")
public class CpComponentLibraryApiServiceImpl implements CpComponentLibraryApiService
{
  /**
   * 日志
   */
  private final static Logger logger = LoggerFactory.getLogger(CpComponentLibraryApiServiceImpl.class);

  @Autowired
  private CpComponentLibraryService cpComponentLibraryService;
  @Autowired
  private CpComponentListService cpComponentListService;
  @Autowired
  private CpComponentParamterService cpComponentParamterService;
  @Autowired
  private SimpleStorageService simpleStorageService;
  @Autowired
  private EntityNameCache entityNameCache;
  @Autowired
  private LogPublisher logPublisher;
  @Autowired
  private AttachmentFeignClient attachmentFeignClient;


  @Override
  @DatabaseReadOnly
  public Pagination<CpComponentLibraryDetailDto> pagingQuery(QueryByPage<CpComponentLibraryQueryDto> queryByPage)
  {
    return this.cpComponentLibraryService.pagingQuery(queryByPage);
  }

  @Override
  public CpComponentLibraryDetailDto findByPK(String uuid) {
    CpComponentLibrary library = cpComponentLibraryService.findByPrimaryKey(uuid);
    CpComponentLibraryDetailDto result = new CpComponentLibraryDetailDto();
    BeanUtils.copyProperties(library, result);
    String name = entityNameCache.translateById(EntityNameCatalog.MONSTER_USER.name(), result.getCreatedBy());
    String upName = entityNameCache.translateById(EntityNameCatalog.MONSTER_USER.name(), result.getUpdatedBy());
    result.setCreatedBy(name);
    result.setUpdatedBy(upName);
    return result;
  }

  @Override
  public CpComponentLibraryDetailDto getLibInfoByName(CpComponentLibraryAddDto target) {
    CpComponentLibrary query = new CpComponentLibrary();
    query.setLibraryName(target.getLibraryName());
    List<CpComponentLibrary> result = cpComponentLibraryService.query(query);
    CpComponentLibraryDetailDto dto = new CpComponentLibraryDetailDto();
    if (result.size()>0) {
      BeanUtils.copyProperties(result.get(0),dto);
    }
    return dto;
  }


  @Override
  @DatabaseWriteOnly
  public Integer updateByPK(CpComponentLibraryUpdateDto dto) throws IllegalAccessException
  {
    CpComponentLibrary entity = new CpComponentLibrary();
    BeanUtils.copyProperties(dto, entity);
    return this.cpComponentLibraryService.updateByPrimaryKey(entity);
  }


  @Override
  public Integer deleteByPK(String uuid) {
    CpComponentLibrary lib = cpComponentLibraryService.findByPrimaryKey(uuid);
    removeComponentLibrary(uuid);
    logger.info("组件库【{}】已卸载，发送删除日志",lib.getLibraryName());
    CpLogAddDto dto = new CpLogAddDto();
    dto.setContent(PlatformConstants.DELETE_LIBRARY +lib.getLibraryName()+PlatformConstants.DELETE_LIBRARY_END);
    dto.setModule(PlatformConstants.LIBRARY_MODULE);
    dto.setType(PlatformConstants.LOG_STATUS_DEL);
    MonsterLogServer.sendLogToMonster(dto);
    logPublisher.sendLog(dto);
    return 1;
  }



  /**
   * 上传组件库并检查版本
   * @param file
   * @return
   */
  @Override
  @Transactional
  public CpComponentLibraryAddDto importComponentLibrary(MultipartFile file) {
    CpComponentLibraryAddDto result = new CpComponentLibraryAddDto();
    String classPath = System.getProperty(PlatformConstants.SYSTEM_PROPERTY) + PlatformConstants.SYSTEM_TMP;
    String saveName = classPath+"/"+file.getOriginalFilename();
    File localFile = null;
    try {
      File fileCheck = new File(classPath);
      if(!fileCheck.exists()){
        boolean mkdirs = fileCheck.mkdirs();//若路径不存在则进行创建
        if (!mkdirs){
          logger.error("创建失败！");
        }
      }
      localFile = new File(saveName);
      file.transferTo(localFile);
      logger.info("======================文件保存成功:"+saveName);
    } catch (IOException e) {
      logger.info("importComponentLibrary=========文件处理失败"+e);
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_FILE_ERROR);
    }

    //文件扫描-校验版本
    Library libraryInfo = null;
    try {
      libraryInfo = JarLibraryScanner.scan(saveName);
    } catch (Exception e) {
      logger.error("======================扫描失败:", e);
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_SCAN_ERROR);
    }
    if(libraryInfo == null){
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_SCAN_ERROR);
    }
    String libraryName = libraryInfo.getName();
    CpComponentLibrary checkLib = new CpComponentLibrary();
    checkLib.setLibraryName(libraryName);
    List<CpComponentLibrary> checkList = cpComponentLibraryService.query(checkLib);
    int checkResult = versionCheck(libraryInfo,checkList);
    if(checkResult == PlatformConstants.VERSION_NUM_ONE || checkResult == PlatformConstants.VERSION_NUM_TWO){
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_VERSION_LOW);
    }

    //文件上传至文件服务器
    NKFile upload = null;
    try {
      upload = simpleStorageService.upload(NKSecurityContext.getTenantId(),localFile, "-1", FileBusinessTypeEnum.BASIC_ELEMENT_LIB);
    } catch (FileUploadException e) {
      logger.info("importComponentLibrary=========文件上传服务器失败"+e);
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_FILE_SERVICE_DOWN);
    }
    logger.info("importComponentLibrary=========文件上传完成，文件id为:"+upload.getId());
    result.setFilePath(upload.getId());
    result.setFileName(file.getOriginalFilename());
    if(checkResult == 0){
      //更改文件名为上传id
      String idFile = classPath+upload.getId();
      File path = new File(idFile.substring(0,idFile.lastIndexOf("/")));
      if(!path.exists()){
        boolean mkdirs = path.mkdirs();
        if (!mkdirs){
          logger.error("创建目录失败");
        }
      }
      boolean renameTo = localFile.renameTo(new File(classPath + upload.getId()));
      if (renameTo){
        logger.info("重命名成功");
      }
      result.setId(checkList.get(0).getId());
      result.setNowVersion(checkList.get(0).getLibraryVersion());
      result.setUpdateVersion(libraryInfo.getVersion());
      result.setLibraryName(libraryInfo.getName());
      logger.info("importComponentLibrary=========确认为更新组件库");
    }else{
      //新插入
      logger.info("importComponentLibrary=========第一次导入,直接插入");
      String id = saveComLibrary(libraryInfo, result);
    }

    return result;
  }

  /**
   * 确认更新组件库
   * @param target
   * @return
   */
  @Override
  @Transactional
  public String updateLibrary(CpComponentLibraryAddDto target) {
    String fileId = target.getFilePath();
    String classPath = System.getProperty(PlatformConstants.SYSTEM_PROPERTY) + PlatformConstants.SYSTEM_TMP;
    String saveName = classPath+fileId;
    //文件扫描-校验版本
    Library libraryInfo = null;
    try {
      File fileCheck = new File(saveName);
      if(!fileCheck.exists()){
        logger.info("scanComponentLibrary=========文件流重新获取...");
        //文件不存在则需要进行重新下载
        InputStream is = simpleStorageService.downloadAsStream(NKSecurityContext.getTenantId(),fileId);
        logger.info("scanComponentLibrary=========文件流重新获取成功");
        libraryInfo = JarLibraryScanner.scan(is);
      }else{
        libraryInfo = JarLibraryScanner.scan(saveName);
      }
      //清楚组件库
      removeComponentLibrary(target.getId());
      //插入组件库
      return saveComLibrary(libraryInfo, target);
    }catch (FileDownloadException e){
      logger.info("scanComponentLibrary=========文件重新下载失败："+e.getMessage());
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_FILE_LOST);
    }catch (IOException e){
      logger.info("scanComponentLibrary=========文件丢失，处理失败原因："+e.getMessage());
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_FILE_LOST);
    } catch (Exception e){
      logger.info("scanComponentLibrary======================扫描失败",e);
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_FILE_ERROR);
    }
  }

  /**
   * 获取组件库列表，作为一级目录
   * @param query
   * @return
   */
  @Override
  public List<CpComLibraryTree> getLibraryTree(CpComponentLibraryQueryDto query) {
    CpComponentLibrary libQuery = new CpComponentLibrary();
    if (!query.isQueryAll()) {
      //非全量查询
      libQuery.setLibraryType(query.getLibraryType());
      libQuery.setLibraryStatus(0);//系统内使用只查询启用状态组件库
    }
    return cpComponentLibraryService.queryLibraryListTree(libQuery);
  }


  /**
   * 模糊搜索列表
   * @param query
   * @return
   */
  @Override
  public List<CpComLibraryTree> selectByLike(CpComponentLibraryQueryDto query) {
    //模糊搜索组件库
    CpComponentLibrary libQuery = new CpComponentLibrary();
    libQuery.setLibraryName(query.getSelectName());
    if (!query.isQueryAll()) {
      //非全量查询
      libQuery.setLibraryType(query.getLibraryType());
      libQuery.setLibraryStatus(0);//项目内使用只查询启用状态组件库
    }
    List<CpComLibraryTree> libList = cpComponentLibraryService.queryLibraryListTree(libQuery);
    Map<String,String> tooMap = new HashMap<>();
    for (CpComLibraryTree one : libList) {
      tooMap.put(one.getId(),"");
    }
    //模糊搜索组建列表
    CpComponentList listQuery = new CpComponentList();
    listQuery.setComAlias(query.getSelectName());
    if (!query.isQueryAll()) {
      //非全量查询
      listQuery.setIsDeprecated(1);
      listQuery.setComCategory(query.getComCategory());
    }
    List<CpComLibraryTree> comList = cpComponentListService.queryComListTree(listQuery);
    //进行树目录的组合
    Map<String, List<CpComLibraryTree>> toolList = new HashMap<>();
    List<Object> other  = new ArrayList<>();
    for (CpComLibraryTree com : comList) {
      if(!tooMap.containsKey(com.getFaId())){
        other.add(com.getFaId());
      }
      if (toolList.containsKey(com.getFaId())) {
        toolList.get(com.getFaId()).add(com);
      }else{
        List<CpComLibraryTree> list = new ArrayList<>();
        list.add(com);
        toolList.put(com.getFaId(),list);
      }
    }
    if(other.size()>0){
      List<CpComponentLibrary> otherList = cpComponentLibraryService.findByPrimaryKeys(other);
      for (CpComponentLibrary one : otherList) {
        CpComLibraryTree tree = new CpComLibraryTree();
        tree.setId(one.getId());
        tree.setTreeAlias(one.getLibraryAlias());
        tree.setTreeName(one.getLibraryName());
        libList.add(tree);
      }
    }
    List<CpComLibraryTree> newList = libList.stream().sorted(Comparator.comparing(CpComLibraryTree::getTreeName)).collect(Collectors.toList());

    for (CpComLibraryTree lib : newList) {
      if (toolList.containsKey(lib.getId())) {
        lib.setChildList(toolList.get(lib.getId()));
      }
    }
    return libList;
  }

  @Override
  public void downloadLibFile(HttpServletResponse response, String libId) {
    CpComponentLibrary lib = cpComponentLibraryService.findByPrimaryKey(libId);
    if(lib==null){
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_NOT_EXIT);
    }
    String path = lib.getFilePath();
    try {
      InputStream is = simpleStorageService.downloadAsStream(lib.getTenantId(),path);
      if (response != null) {
        //返回数据流
        dealResponse(response, lib.getFileName());
        OutputStream os = null;
        try {
          os = response.getOutputStream();
          byte[] buff = new byte[1024];
          int len;
          while ((len=is.read(buff))>0){
            os.write(buff,0,len);
          }
          os.flush();
        } catch (IOException e) {
          logger.error(e.getMessage());
        } finally {
          if (os != null) {
            try {
              os.close();
            } catch (IOException e) {
              logger.error(e.getMessage());
            }
          }
          if (is != null) {
            try {
              is.close();
            } catch (IOException e) {
              logger.error(e.getMessage());
            }
          }
        }
      }
    } catch (FileDownloadException e) {
      logger.info("downloadLibFile=========文件下载失败："+e.getMessage());
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_FILE_LOST);
    }
  }

  /**
   * 用于引擎获取最新jar包
   * @param query
   * @return
   */
  @Override
  public List<CpComponentLibraryDetailDto> getLibListByEngine(CpComLibListEngineDto query) {
    CpComponentLibrary lib = new CpComponentLibrary();
    List<CpComponentLibrary> result = cpComponentLibraryService.query(lib);
    List<CpComponentLibraryDetailDto> libList = new ArrayList<>();
    for (CpComponentLibrary library : result) {
      CpComponentLibraryDetailDto entity = new CpComponentLibraryDetailDto();
      entity.setId(library.getId());
      entity.setFileName(library.getFileName());
      entity.setFilePath(library.getFilePath());
      entity.setLibraryName(library.getLibraryName());
      entity.setLibraryVersion(library.getLibraryVersion());
      entity.setTenantId(library.getTenantId());
      libList.add(entity);
    }
    return libList;
  }


  /**
   * 物理删除原组件库以及组件清单
   * @param id
   */
  private void removeComponentLibrary(String id) {
    //清除旧组件库
    CpComponentLibrary byPrimaryKey = cpComponentLibraryService.findByPrimaryKey(id);
    try {
      simpleStorageService.delete(NKSecurityContext.getTenantId(), byPrimaryKey.getFilePath());
    } catch (Exception e) {
      logger.error("文件删除失败id:{},{}", byPrimaryKey.getFilePath(), e);
      throw new PlatformRuntimeException(new ErrorMessage("文件删除失败"));
    }
    cpComponentLibraryService.deleteByPrimaryKey(id);
    CpComponentList deleteList = new CpComponentList();
    deleteList.setLibraryId(id);
    cpComponentListService.deleteByExample(deleteList);
    CpComponentParamter deleteParam = new CpComponentParamter();
    deleteParam.setLibId(id);
    cpComponentParamterService.deleteByExample(deleteParam);
    logger.info("scanComponentLibrary=========旧版组件库清除成功");
  }


  /**
   * 保存组件库
   */
  private String saveComLibrary(Library libInfo,CpComponentLibraryAddDto target){
    try {
      CpComponentLibrary lib = new CpComponentLibrary();
      String id = UUIDUtil.create32UUID();
      lib.setId(id);
      lib.setChangeLog(libInfo.getChangeLog());
      lib.setFileName(target.getFileName());
      lib.setFilePath(target.getFilePath());
      lib.setLibraryAlias(libInfo.getChName());
      lib.setLibraryIntro(libInfo.getIntro());
      lib.setLibraryName(libInfo.getName());
      lib.setLibraryStatus(0);//默认组件为可用-0
      lib.setLibraryType(libInfo.getType());
      lib.setLibraryVersion(libInfo.getVersion());
      cpComponentLibraryService.insert(lib);
      List<String> fileIds = new ArrayList<>();
      fileIds.add(target.getFilePath());
      AssociatedFilesDto associatedFilesDto = new AssociatedFilesDto();
      associatedFilesDto.setObjId(target.getId());
      associatedFilesDto.setFileIds(fileIds);
      attachmentFeignClient.associatedFiles(associatedFilesDto);
      logger.info(String.format("saveComLibrary=========组件库{%s}保存完毕",libInfo.getName()));
      List<KeywordInfo> keywordList = libInfo.getAllKeywords();
      List<CpComponentList> comList = new ArrayList<>();
      List<CpComponentParamter> parList = new ArrayList<>();
      for (KeywordInfo info : keywordList) {
        CpComponentList com = new CpComponentList();
        com.setId(UUIDUtil.create32UUID());
        com.setLibraryId(lib.getId());
        com.setComAlias(info.getKeywordAlias());
        com.setComCategory(info.getCategory());
        com.setComAttributes(info.getAttributes());
        if(PlatformConstants.KEYWORD_INFO_ATTRIBUTES.equals(info.getAttributes())){
          if(lib.getLibraryType().equals(PlatformConstants.INT_MINUS_ONE)){
            com.setComAttributes(PlatformConstants.KEYWORD_INFO_COMATTRIBUTES);
          }else{
            com.setComAttributes(lib.getLibraryType()+"");
          }
        }
        com.setComIntro(info.getKeywordDocumentation());
        com.setIsDeprecated(info.isDeprecated()?PlatformConstants.INT_ZERO:PlatformConstants.INT_ONE);
        com.setComName(info.getKeywordName());
        com.setIsCommonlyUse(info.isCommonlyUse()?PlatformConstants.INT_ONE:PlatformConstants.INT_ZERO);
        comList.add(com);
        int order =0;
        for (KeywordArgument param : info.getKeywordArguments()) {
          CpComponentParamter input = new CpComponentParamter();
          input.setId(UUIDUtil.create32UUID());
          input.setComId(com.getId());
          input.setLibId(lib.getId());
          input.setDefaultValue(param.getDefaultValue());
          input.setParamIntro(param.getComment());
          input.setParamName(param.getName());
          input.setParamOrder(order++);
          input.setParamScope(param.getScope().name());
          input.setParamFlag(PlatformConstants.PARAM_FLAG.equals(input.getParamScope())?PlatformConstants.INT_ONE:PlatformConstants.INT_ZERO);//输入参数
          input.setParamType(param.getType().name());
          input.setRequired(param.isRequired()?PlatformConstants.INT_ZERO:PlatformConstants.INT_ONE);
          input.setEnumValues(param.getEnumValues());
          input.setInputType(param.getInputType().name());
          input.setDefaultDisplay(param.isDefaultDisplay()?PlatformConstants.INT_ZERO:PlatformConstants.INT_ONE);
          parList.add(input);
        }
      }
      cpComponentListService.insertByBatch(comList);
      logger.info("saveComLibrary=========组件清单保存完毕");
      cpComponentParamterService.insertByBatch(parList);
      logger.info("saveComLibrary=========组件参数保存完毕");
      return id;
    } catch (Exception e) {
      logger.info("saveComLibrary=========组件库保存失败,回滚:", e);
      throw new PlatformRuntimeException(PlatformAppError.COMPONENT_LIBRARY_FILE_ERROR);
    }

  }


  /**
   * 判断组件库是否存在并校验版本
   * @param libraryInfo
   * @return 0：版本更新
   * @return 1：版本过低
   * @return 2：版本相同
   * @return 3：判定为新组件库
   */
  private int versionCheck(Library libraryInfo , List<CpComponentLibrary> checkList) {
    //判断库是否已存在，若存在则判断版本信息
    int result = PlatformConstants.INT_TWO; //版本相同——默认
    if (checkList != null && checkList.size() > 0) {
      //已存在有效组件库，根据版本号判断是否需要更新
      CpComponentLibrary reComponentLibrary = checkList.get(0);
      String versionNew = libraryInfo.getVersion();
      String versionOld = reComponentLibrary.getLibraryVersion();
      //全量比较
      String[] verNew = versionNew.split(PlatformConstants.VERSION_REGEX);
      String[] verOld = versionOld.split(PlatformConstants.VERSION_REGEX);
      int len = Math.min(verNew.length, verOld.length);
      for (int i = 0; i < len; i++) {
        //逐个校验
        int newNum = Integer.parseInt(verNew[i]);
        int oldNum = Integer.parseInt(verOld[i]);
        if (newNum > oldNum) {
          result = PlatformConstants.INT_ZERO; //新版更新
          break;
        }
        if (newNum < oldNum) {
          result = PlatformConstants.INT_ONE; //旧版不跟新
          break;
        }
      }
      if (result == PlatformConstants.INT_TWO && verNew.length > verOld.length) {
        result = PlatformConstants.INT_ZERO;//新版更新
      }
      if (result == PlatformConstants.INT_TWO && verNew.length < verOld.length) {
        result = PlatformConstants.INT_ONE;//旧版不跟新
      }
    } else {
      result = PlatformConstants.INT_THREE;//库表不存在，为新组件库
    }
    return result;
  }

  public static void dealResponse(HttpServletResponse response, String fileName){
    response.reset();
    response.setStatus(HttpStatus.OK.value());
    response.setHeader(PlatformConstants.CONTENT_TYPE, PlatformConstants.APPLICATION_CHARSET);
    response.addHeader(PlatformConstants.CONTENT_DISPOSITION, PlatformConstants.ATTACHMENT_FILENAME + fileName);
  }
}
