package com.jiayue.ipfcst.dataexchange.container;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.RuntimeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jiayue.ipfcst.common.core.util.SpringContextHolder;
import com.jiayue.ipfcst.common.data.constant.enums.TunnelStatus;
import com.jiayue.ipfcst.common.data.constant.enums.TunnelType;
import com.jiayue.ipfcst.common.data.entity.BaseTunnelInfo;
import com.jiayue.ipfcst.common.data.entity.SendLog;
import com.jiayue.ipfcst.common.data.repository.SendLogRepository;
import com.jiayue.ipfcst.dataexchange.exception.DataExchangeException;
import com.jiayue.ipfcst.dataexchange.service.SendLogService;
import com.jiayue.ipfcst.dataexchange.service.TunnelInfoService;
import com.jiayue.ipfcst.dataexchange.tunnelworker.BaseProtocolTunnel;
import com.jiayue.ipfcst.dataexchange.tunnelworker.gathers.DataGatherInterface;
import com.jiayue.ipfcst.dataexchange.tunnelworker.gathers.iml.Iec104TcpDataGather;
import com.jiayue.ipfcst.dataexchange.tunnelworker.gathers.iml.ModbusTcpDataGather;
import com.jiayue.ipfcst.dataexchange.tunnelworker.senders.DataSenderInterface;
import com.jiayue.ipfcst.dataexchange.tunnelworker.workassist.TunnelBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 装载通道对象的容器
 *
 * @author 修唯xiuwei
 * @version 3.0
 */
@Slf4j
public class ProtocolTunnelContainer {

  /**
   * 通道集合
   */
  private Map<String, BaseProtocolTunnel> tunnels = new ConcurrentHashMap<>();


  /**
   * 更新数据的定时任务集合
   */
  private Map<String, Callable> tasks = new ConcurrentHashMap<>();


  /**
   * 储存转发数据的定时任务集合
   */
  private Map<String, Callable> sendTasks = new ConcurrentHashMap<>();


  /**
   * 单例构造
   */
  private ProtocolTunnelContainer() {
   // RecurringTaskContainer.getInstance().addRecurringCallable(5 * 60, "通道状态巡检任务", new CheckTunnelStatusTask());
  }

  /**
   * 单例
   *
   * @return 实例
   */
  public static final ProtocolTunnelContainer getInstance() {
    return LazyHolder.INSTANCE;
  }

  /**
   * 获取tunnelID下的 tunnel
   *
   * @param tunnelId 通道id
   * @return BaseProtocolTunnel 通道
   */
  public BaseProtocolTunnel getTunnel(String tunnelId) {
    if (this.tunnels.containsKey(tunnelId)) {
      return this.tunnels.get(tunnelId);
    }
    return null;
  }

  /**
   * 为前台页面 提供数据  Map<通道ID,通道状态 1 正常，0异常>
   *
   * @return
   */
  public Map<String, Map<String, String>> getStatus4Display() {
    Map<String, String> dataexchangeMap = new HashMap<>();
    Map<String, String> callBackMap = new HashMap<>();
    List<TunnelStatus> properlyStatus = TunnelStatus.getRunningProperlyStatus();
    for (BaseProtocolTunnel tunnel : this.tunnels.values()) {
      if (!tunnel.getTunnelInfo().getIsEnable()) {
        continue;
      }
      if (tunnel.getTunnelType().getFlow() == "in" && !TunnelType.FILEPARSE.equals(tunnel.getTunnelType())) {
        dataexchangeMap.put(tunnel.getTunnelId(), properlyStatus.contains(tunnel.getTunnelStatus()) ? "1" : "0");
      } else if (tunnel.getTunnelType().getFlow() == "out") {
        callBackMap.put(tunnel.getTunnelId(), properlyStatus.contains(tunnel.getTunnelStatus()) ? "1" : "0");
      }
    }
    Map<String, Map<String, String>> map = new HashMap<>();
    map.put("dataexchangeMap", dataexchangeMap);
    map.put("callBackMap", callBackMap);
    return map;
  }

  /**
   * 向集合中置入tunnel
   *
   * @param tunnel 通道对象
   * @return 返回置入的tunnel
   */
  public BaseProtocolTunnel addTunnel(BaseProtocolTunnel tunnel) {
    this.tunnels.put(tunnel.getTunnelId(), tunnel);
    return tunnel;
  }

  /**
   * 删除隧道 同时删除定时更新数据任务
   *
   * @param tunnelId 隧道id
   */
  public void removeTunnel(String tunnelId) {
    this.tunnels.remove(tunnelId);
    stopUpdateTask(tunnelId);
  }

  /**
   * 停止更新任务
   *
   * @param tunnelId 通道id
   */
  public void stopUpdateTask(String tunnelId) {
    if (this.tasks.containsKey(tunnelId)) {
      RecurringTaskContainer.getInstance().removeTask(tasks.get(tunnelId));
      this.tasks.remove(tunnelId);
    }
    if (this.sendTasks.containsKey(tunnelId)) {
      RecurringTaskContainer.getInstance().removeTask(sendTasks.get(tunnelId));
      this.sendTasks.remove(tunnelId);
    }
  }

  /**
   * 添加数据更新任务
   *
   * @param baseProtocolTunnel 通道对象
   */
  public void addUpdateDateTask(BaseProtocolTunnel baseProtocolTunnel) {
    if (this.tasks.containsKey(baseProtocolTunnel.getTunnelId())) {
      RecurringTaskContainer.getInstance().removeTask(this.tasks.get(baseProtocolTunnel.getTunnelId()));
    }
    Callable callable = null;
    if (baseProtocolTunnel instanceof DataGatherInterface) {
      callable = RecurringTaskContainer.getInstance().addRecurringTask(baseProtocolTunnel.getTunnelInfo().getRefreshInterval(), "采集通道数据更新任务" + baseProtocolTunnel.getTunnelInfo().getTunnelName(), () -> {
        if (TunnelStatus.ABLE_STATUS.contains(baseProtocolTunnel.getTunnelStatus())) {
          ((DataGatherInterface) baseProtocolTunnel).getDataFromProtocol();
        } else {
          log.warn("{}通道处于断开状态,{}，不能执行采集数据操作", baseProtocolTunnel.getTunnelInfo().getTunnelName(), baseProtocolTunnel.getTunnelStatus().getCnDescribe());
        }
        return null;
      });
    } else if (baseProtocolTunnel instanceof DataSenderInterface) {
      callable = RecurringTaskContainer.getInstance().addRecurringTask(baseProtocolTunnel.getTunnelInfo().getRefreshInterval(), "转发通道数据更新任务" + baseProtocolTunnel.getTunnelInfo().getTunnelName(), () -> {
        if (TunnelStatus.ABLE_STATUS.contains(baseProtocolTunnel.getTunnelStatus())) {
          ((DataSenderInterface) baseProtocolTunnel).updateData2Protocol();
        } else {
          baseProtocolTunnel.getLog().info("{}通道监听串口或网口未成功或监听的网口未有客户端连接,{}",baseProtocolTunnel.getTunnelInfo().getTunnelName(),baseProtocolTunnel.getTunnelStatus().getCnDescribe());
        }
        return null;
      });
    }
    this.tasks.put(baseProtocolTunnel.getTunnelId(), callable);
  }


  /**
   * 添加存储转发数据任务
   *
   * @param baseProtocolTunnel 通道对象
   */
  public void addSaveSendDataTask(BaseProtocolTunnel baseProtocolTunnel) {
    if (baseProtocolTunnel instanceof DataSenderInterface) {
      if (this.sendTasks.containsKey(baseProtocolTunnel.getTunnelId())) {
        RecurringTaskContainer.getInstance().removeTask(this.tasks.get(baseProtocolTunnel.getTunnelId()));
      }
      Callable callable = RecurringTaskContainer.getInstance().addRecurringTask(60, "转发通道数据存储任务" + baseProtocolTunnel.getTunnelInfo().getTunnelName(), () -> {
        SendLog sendLog = new SendLog();
        sendLog.setTunnelId(baseProtocolTunnel.getTunnelId());
        sendLog.setTunnelName(baseProtocolTunnel.getTunnelInfo().getTunnelName());
        sendLog.setCreateTime(DateTime.now().toJdkDate());
        if (TunnelStatus.ABLE_STATUS.contains(baseProtocolTunnel.getTunnelStatus()) ) {
          sendLog.setSendLog(((DataSenderInterface) baseProtocolTunnel).getDataBeingSent());
          sendLog.setStatus("通道正常");
        } else {
          sendLog.setStatus("通道异常");
          baseProtocolTunnel.getLog().info("{}通道监听串口或网口未成功或监听的网口未有客户端连接,{}",baseProtocolTunnel.getTunnelInfo().getTunnelName(),baseProtocolTunnel.getTunnelStatus().getCnDescribe());
        }
        SpringContextHolder.getBean(SendLogService.class).save(sendLog);
        return null;
      });
      this.sendTasks.put(baseProtocolTunnel.getTunnelId(), callable);
    }
  }


  @Override
  public String toString() {
    Map<String, JSONObject> str = new HashMap<>();
    this.tunnels.forEach((k, v) -> {
      str.put(k, v.getJSONObj());
    });
    return JSON.toJSONString(str);
  }

  /**
   * 格式化打印信息 以表格的形式打印通道状态
   *
   * @return
   */
  public String toStringFormatted() {
    StringBuffer stringBuffer = new StringBuffer();
    String splitLine = "|——————————————————————————————————|————————————————————|—————————————————————|———————————————————————|\n";
    stringBuffer.append(splitLine);
    stringBuffer.append("|              通道id              |       通道类型      |        通道状态      |         通道名        |\n");
    stringBuffer.append(splitLine);
    for (BaseProtocolTunnel t : this.tunnels.values()) {
      stringBuffer.append("|");
      stringBuffer.append(StringUtils.center(t.getTunnelInfo().getId(), 34));
      stringBuffer.append("|");
      stringBuffer.append(StringUtils.center(t.getTunnelInfo().getTunnelType().toString(), 20));
      stringBuffer.append("|");
      stringBuffer.append(StringUtils.center(t.getTunnelStatus().toString(), 21));
      stringBuffer.append("|");
      stringBuffer.append(StringUtils.center(t.getTunnelInfo().getTunnelName(), 20));
      stringBuffer.append("|");
      stringBuffer.append("\n");
      stringBuffer.append(splitLine);
    }
    return stringBuffer.toString();
  }

  /**
   * 静态内部类 为了创建安全的单例
   */
  private static class LazyHolder {
    private static final ProtocolTunnelContainer INSTANCE = new ProtocolTunnelContainer();
  }

  /**
   * 创建一个对通道池内通道状态的检查任务
   */
  class CheckTunnelStatusTask implements Callable {

    @Override
    public Object call() throws Exception {
      TunnelStatus tunnelStatus;
      log.info("开始对所有的通道状态进行巡检");
      List<BaseTunnelInfo> storedTunnels = SpringContextHolder.getBean(TunnelInfoService.class).getAllTunnel();
      BaseProtocolTunnel t;
      for (BaseTunnelInfo tInfo : storedTunnels) {
        if (!tInfo.getIsEnable()) {
          continue;
        }
        t = getTunnel(tInfo.getId());
        if (t == null) {
          try {
            TunnelBuilder.buildTunnel(tInfo).startTunnel();
          } catch (DataExchangeException e) {
            log.error("通道{}开启时发生异常", tInfo.getTunnelName(), e);
          }
          continue;
        }
        tunnelStatus = t.getTunnelStatus();
        if (!(TunnelStatus.ABLE_STATUS.contains(tunnelStatus) || (tunnelStatus == TunnelStatus.LOSECONN && "out".equals(t.getTunnelType().getFlow())))) {
          log.warn("发现本该开启的通道:{} 处在未开启成功的状态,重启该通道", tInfo.getTunnelName());
          try {
            t.tunnelStop();
          } catch (DataExchangeException e) {
            log.error("通道{}关闭时发生异常", t.getTunnelInfo().getTunnelName(), e);
          }
          t.refreshTunnelInfo(tInfo).buildTunnel();

          BaseProtocolTunnel finalT = t;
          Executors.newSingleThreadScheduledExecutor().schedule(() -> {
            try {
              finalT.startTunnel();
            } catch (DataExchangeException e) {
              log.error("通道{}开启时发生异常", finalT.getTunnelInfo().getTunnelName(), e);
            }
          }, 10, TimeUnit.SECONDS);
        }
      }
      return null;
    }
  }


}
