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

import net.northking.cctp.common.db.BasicService;
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.db.excel.AbstractExcelService;
import net.northking.cctp.common.db.excel.ExcelHeader;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.common.http.QueryByPage;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import net.northking.cctp.common.util.UUIDUtil;
import net.northking.cctp.device.config.EngineConfig;
import net.northking.cctp.device.constants.DeviceConstants;
import net.northking.cctp.device.constants.DeviceError;
import net.northking.cctp.device.db.entity.*;
import net.northking.cctp.device.db.service.*;
import net.northking.cctp.device.dto.engineInfo.*;
import net.northking.cctp.device.util.DeviceStatusUtil;
import net.northking.cctp.device.util.OkHttpClientUtil;
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 java.util.*;

/**
 * 执行引擎信息表服务实现类
 *
 * @author maven-cctp-plugin
 * @since 1.0
 */

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

  @Autowired
  private CdEngineInfoService cdEngineInfoService;

  @Autowired
  private CdPcDeviceService cdPcDeviceService;

  @Autowired
  private CdDeviceStatusService cdDeviceStatusService;

  @Autowired
  private CdEngineReleasesService cdEngineReleasesService;

  @Autowired
  private CdDeviceProjectService cdDeviceProjectService;

  @Autowired
  private CdDeviceTenantService cdDeviceTenantService;

  @Autowired
  private CdDeviceTokenService cdDeviceTokenService;

  @Autowired
  private OkHttpClientUtil okHttpClientUtil;

  @Autowired
  private DeviceStatusUtil statusUtil;

  @Autowired
  private EngineConfig engineConfig;

  //= Excel 导入导出相关代码 start ==================//
  @Override
  public BasicService<CdEngineInfo> getService()
  {
    return this.cdEngineInfoService;
  }

  @Override
  public ExcelHeader[] excelHeaders()
  {
    ExcelHeader id = new ExcelHeader("主键id", "id", String.class);
    ExcelHeader engineName = new ExcelHeader("引擎名称", "engineName", String.class);
    ExcelHeader ipAddr = new ExcelHeader("ip地址", "ipAddr", String.class);
    ExcelHeader runningRelease = new ExcelHeader("引擎版本", "runningRelease", String.class);
    ExcelHeader registedTime = new ExcelHeader("注册时间", "registedTime", Date.class);
    ExcelHeader enabled = new ExcelHeader("启用/禁用", "enabled", Boolean.class);
    ExcelHeader engineStatus = new ExcelHeader("引擎状态", "engineStatus", String.class);
    ExcelHeader engineDesc = new ExcelHeader("引擎描述", "engineDesc", String.class);
    return new ExcelHeader[]{};
  }

  /**
   * Excel导入，入库之前的处理
   * @param records 从Excel读取到的记录
   * @return 待入库的记录
   */
  @Override
  protected List<CdEngineInfo> beforeInsertBatch(List<CdEngineInfo> records)
  {
    for (CdEngineInfo record : records)
    {
      record.setId(UUIDUtil.create32UUID());
    }
    return records;
  }

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

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

  //= 增删改查 相关代码 start ==================//
  @Override
  @DatabaseReadOnly
  public Pagination<CdEngineInfoDetailDto> pagingQuery(QueryByPage<CdEngineInfoQueryDto> queryByPage)
  {
    return this.cdEngineInfoService.queryPages(queryByPage);
  }

  @Override
  @DatabaseWriteOnly
  public String add(CdEngineInfoAddDto dto)
  {
    String uuid = UUIDUtil.create32UUID();
    CdEngineInfo entity = new CdEngineInfo();
    BeanUtils.copyProperties(dto, entity);
    entity.setId(uuid);
    this.cdEngineInfoService.insert(entity);
    return uuid;
  }

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

  @Override
  @DatabaseReadOnly
  public CdEngineInfoDetailDto findByPK(String uuid)
  {
    CdEngineInfo example = new CdEngineInfo();
    example.setId(uuid);
    List<CdEngineInfo> entitys = this.cdEngineInfoService.query(example);
    if (entitys.isEmpty())
    {
      return null;
    }

    CdEngineInfoDetailDto dto = new CdEngineInfoDetailDto();
    BeanUtils.copyProperties(entitys.get(0), dto);
    return dto;
  }

  @Override
  @DatabaseWriteOnly
  public Integer deleteByPK(String uuid)
  {
    CdEngineInfo entity = new CdEngineInfo();
    entity.setId(uuid);
    return this.cdEngineInfoService.deleteByExample(entity);
  }

  @Override
  public List<String> addBatch(List<CdEngineInfoAddDto> batch)
  {
    List<String> result = new ArrayList<>();
    for (CdEngineInfoAddDto dto : batch)
    {
      result.add(this.add(dto));
    }
    return result;
  }

  @Override
  public Integer updateBatch(List<CdEngineInfoUpdateDto> batch) throws IllegalAccessException
  {
    int sum = 0;
    for (CdEngineInfoUpdateDto dto : batch)
    {
      sum = sum + this.updateByPK(dto);
    }
    return sum;
  }

  @Override
  public Integer deleteBatch(List<Object> batch)
  {
    return this.cdEngineInfoService.deleteByPrimaryKeys(batch);
  }


  @Override
  public CdEngineInfoResultDto register(CdEngineInfoRegisterDto dto) {
    CdEngineInfoResultDto resultDto = new CdEngineInfoResultDto();
    //根据 ip+端口 校验设备是否存在
    CdPcDevice cdPcDevice = new CdPcDevice();
    cdPcDevice.setIpAddr(dto.getCdPcDevice().getIpAddr());
    Date date = new Date();
    List<CdPcDevice> pcDevices = this.cdPcDeviceService.query(cdPcDevice);
    Boolean isPcExecutor = true;
    if (!Objects.isNull(dto.getCdEngineInfo().getIsPcExecutor())) {
      isPcExecutor = dto.getCdEngineInfo().getIsPcExecutor();
      logger.debug("是否执行机：isPcExecutor,{}",isPcExecutor);
    }
    if (isPcExecutor) {
      if (pcDevices.size() > 0) {
        //存在设备 修改状态
        CdDeviceStatus cdDeviceStatus = new CdDeviceStatus();
        cdDeviceStatus.setId(pcDevices.get(0).getId());
        cdDeviceStatus.setDeviceStatus("1");
        CdDeviceToken deviceToken = this.cdDeviceTokenService.findByPrimaryKey(pcDevices.get(0).getId());
        if (deviceToken != null) {
          cdDeviceStatus.setUsageStatus("2");
        } else {
          cdDeviceStatus.setUsageStatus("1");
        }
        cdDeviceStatus.setLastHeartbeatTime(date);
        statusUtil.updatePcStatus(cdDeviceStatus);
        //返回设备id
        resultDto.setDeviceId(pcDevices.get(0).getId());

        //更新PC设备信息
        dto.getCdPcDevice().setId(pcDevices.get(0).getId());
        cdPcDeviceService.updateByPrimaryKey(dto.getCdPcDevice());
      } else {
        //新增设备
        String deviceId = UUIDUtil.create32UUID();
        dto.getCdPcDevice().setId(deviceId);
        dto.getCdPcDevice().setRegistedTime(date);
        this.cdPcDeviceService.insert(dto.getCdPcDevice());
        CdDeviceStatus cdDeviceStatus = new CdDeviceStatus();
        cdDeviceStatus.setId(deviceId);
        cdDeviceStatus.setDeviceType(DeviceConstants.DEVICE_PC_TYPE);
        cdDeviceStatus.setDeviceStatus(DeviceConstants.DEVICE_STATUS_ONLINE);
        cdDeviceStatus.setUsageStatus(DeviceConstants.USAGE_STATUS_FREE);
        cdDeviceStatus.setLastHeartbeatTime(date);
        this.cdDeviceStatusService.insert(cdDeviceStatus);
        //返回设备id
        resultDto.setDeviceId(deviceId);
      }
    }


    //根据 ip+端口 校验引擎是否存在
    CdEngineInfo cdEngineInfo = new CdEngineInfo();
    cdEngineInfo.setIpAddr(dto.getCdEngineInfo().getIpAddr());
    cdEngineInfo.setIpPort(dto.getCdEngineInfo().getIpPort());
    List<CdEngineInfo> engineInfos = this.cdEngineInfoService.query(cdEngineInfo);
    if (engineInfos.size() > 0) {
      //存在引擎 修改状态
      dto.getCdEngineInfo().setId(engineInfos.get(0).getId());
      dto.getCdEngineInfo().setEngineStatus(DeviceConstants.ENGINE_STATUS_ONLINE);
      dto.getCdEngineInfo().setEnabled(true);
      dto.getCdEngineInfo().setLastHeartbeatTime(date);
      dto.getCdEngineInfo().setDeviceId(resultDto.getDeviceId());
      this.cdEngineInfoService.updateByPrimaryKey(dto.getCdEngineInfo());
      resultDto.setEngineId(engineInfos.get(0).getId());
    } else {
      //不存在引擎 新增引擎
      String engineId = UUIDUtil.create32UUID();
      dto.getCdEngineInfo().setId(engineId);
      dto.getCdEngineInfo().setDeviceId(resultDto.getDeviceId());
      dto.getCdEngineInfo().setRegistedTime(date);
      dto.getCdEngineInfo().setEnabled(true);
      dto.getCdEngineInfo().setEngineStatus(DeviceConstants.ENGINE_STATUS_ONLINE);
      dto.getCdEngineInfo().setLastHeartbeatTime(date);
      this.cdEngineInfoService.insert(dto.getCdEngineInfo());
      resultDto.setEngineId(engineId);
    }
    return resultDto;
  }

  @Override
  public Integer updateVersion(List<String> ids) {
    CdEngineInfoVersionDto dto = new CdEngineInfoVersionDto();
    dto.setIds(ids);
    //获取最新版本
    CdEngineReleases releases = cdEngineReleasesService.findPackage();
    if (releases==null){
      throw new PlatformRuntimeException(DeviceError.ENGINE_VERSION_NOT_EXIST);
    }
    dto.setVersion(releases.getReleaseVersion());
    Integer update = this.cdEngineInfoService.updateVersion(dto);
    return update;
  }

  @Override
  public String restart(List<String> list) {
    String result = new String();
    StringBuffer name = new StringBuffer();
    for (String engineId : list) {
      CdEngineInfo engineInfo = this.cdEngineInfoService.findByPrimaryKey(engineId);
      if (engineInfo.getEngineStatus().equals(DeviceConstants.ENGINE_STATUS_ONLINE)){
        //给在线的引擎发送重启请求
        HashMap<String,Object> body= new HashMap<>();
        String url = String.format(DeviceConstants.ENGINE_STOP_URL,engineInfo.getIpAddr(),engineInfo.getIpPort());
        okHttpClientUtil.httpPost(url,body.toString(),NKSecurityContext.getUserId());
      }else{
        name.append("[");
        name.append(engineInfo.getEngineName());
        name.append("]");
      }
    }
    if (name.length()>0) {
      result = name.toString() + DeviceConstants.ENGINE_NO_ONLINE;
    }else{
      result = DeviceConstants.ENGINE_IS_RESTART;
    }
    return result;
  }

  /**
   * 获取可用的引擎
   * @param proId
   * @return
   */
  @Override
  public CdEngineInfo getEngineInfo(String proId) {
    //查询系统所关联在线的PC设备
    List<String> pcDeviceIds = this.cdDeviceProjectService.findProByType(proId);  // 系统有分配引擎
    if (pcDeviceIds.isEmpty()) {  // 系统没有分配引擎设备，从当前租户下获取引擎设备
      pcDeviceIds = this.cdDeviceTenantService.findDeviceByType(NKSecurityContext.getTenantId());
    }
    // 系统和租户下都没有引擎设备，从平台获取一台可用
    List<CdEngineInfo> ngInfos = this.cdEngineInfoService.queryNgInDeviceIds(pcDeviceIds, 1);
    if (ngInfos.isEmpty()) {
      throw new PlatformRuntimeException(DeviceError.ENGINE_IS_NULL);
    }
    return ngInfos.get(0);
  }

  @Override
  public String releaseDevice(String engineId) {
    CdEngineInfo engineInfo = this.cdEngineInfoService.findByPrimaryKey(engineId);
    CdDeviceStatus status = this.cdDeviceStatusService.findByPrimaryKey(engineInfo.getDeviceId());
    this.cdDeviceTokenService.deleteByPrimaryKey(engineInfo.getDeviceId());
    if (status.getDeviceStatus().equals(DeviceConstants.DEVICE_STATUS_OFFLINE) && status.getDeviceType().equals(DeviceConstants.DEVICE_PC_TYPE)){
      logger.info("PC设备离线不修改使用状态");
    }else {
      status.setUsageStatus(DeviceConstants.USAGE_STATUS_FREE);
      statusUtil.updatePcStatus(status);
    }
    logger.info("释放设备:"+status.getId());
    return DeviceConstants.RELEASE_SUCCESS;
  }

  @Override
  public List<EngineInfoListDto> queryEngineByType(List<DeviceListDto> dto) {
    //1、移动 2、PC
    List<EngineInfoListDto> dtoList = new ArrayList<>();
    for (DeviceListDto deviceListDto : dto) {
      //PC设备，查询设备相应的引擎
      if (deviceListDto.getType().equals(DeviceConstants.DEVICE_PC_TYPE)) {
        dtoList.add(handleEngineInfo(deviceListDto.getDeviceList(), deviceListDto.getType()));
      }

      // 移动设备，查询所有可用引擎
      if (deviceListDto.getType().equals(DeviceConstants.DEVICE_MOB_TYPE)) {
        dtoList.add(handleEngineInfo(new ArrayList<>(), deviceListDto.getType()));
      }
    }
    return dtoList;
  }

  /**
   * 查询可用引擎信息并整理返回结果
   * @param deviceIdList 设备id集合
   * @param deviceType 设备类型
   * @return 结果集
   */
  private EngineInfoListDto handleEngineInfo(List<String> deviceIdList, String deviceType){
    EngineInfoListDto infoDto = new EngineInfoListDto();
    infoDto.setType(deviceType);
    infoDto.setEngineInfoList(new ArrayList<>());
    List<CdEngineInfo> engineInfoList = this.cdEngineInfoService.queryUsableEngine(deviceIdList);
    for (CdEngineInfo engineInfo : engineInfoList) {
      Map<String, String> map = new HashMap<>(4);
      map.put(DeviceConstants.DEVICE_UUID, engineInfo.getId());
      map.put(DeviceConstants.DEVICE_IP, engineInfo.getIpAddr());
      map.put(DeviceConstants.DEVICE_PORT, engineInfo.getIpPort());
      infoDto.getEngineInfoList().add(map);
    }
    return infoDto;
  }
}


