package com.jiayue.ipfcst.fileupload.IEC102;

import ch.qos.logback.classic.Logger;
import com.jiayue.ipfcst.common.core.util.SpringContextHolder;
import com.jiayue.ipfcst.common.data.entity.*;
import com.jiayue.ipfcst.console.service.ChannelDisconLogService;
import com.jiayue.ipfcst.console.service.ElectricFieldService;
import com.jiayue.ipfcst.fileupload.config.AppenderFactory;
import com.jiayue.ipfcst.fileupload.service.UploadFileChannelService;
import com.jiayue.ipfcst.fileupload.service.UploadObjectService;
import com.jiayue.ipfcst.fileupload.util.FileConstant;
import com.jiayue.ipfcst.fileupload.util.FileMutableInteger;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.net.InetSocketAddress;
import java.util.*;
import java.util.stream.Collectors;

/**
 * netty服务端中转处理类
 *
 * @author xsl
 * @version 3.0
 */
@Slf4j
public class ServerFor102TransitHandler extends ChannelInboundHandlerAdapter {
  @Autowired
  UploadFileChannelService uploadFileChannelService = SpringContextHolder.getApplicationContext().getBean(UploadFileChannelService.class);
  @Autowired
  ElectricFieldService electricFieldService = SpringContextHolder.getApplicationContext().getBean(ElectricFieldService.class);
  @Autowired
  UploadObjectService uploadObjectService = SpringContextHolder.getApplicationContext().getBean(UploadObjectService.class);
  @Autowired
  ServerFor102StandardService serverFor102StandardService = SpringContextHolder.getApplicationContext().getBean(ServerFor102StandardService.class);
  @Autowired
  ChannelDisconLogService channelDisconLogService = SpringContextHolder.getApplicationContext().getBean(ChannelDisconLogService.class);
  @Autowired
  AppenderFactory appenderFactory = SpringContextHolder.getApplicationContext().getBean(AppenderFactory.class);

  private int readerIdleTime;

  ServerFor102TransitHandler(Integer readerIdleTime) {
    this.readerIdleTime = readerIdleTime;
  }

  private UploadFileChannel uploadFileChannel = null;

  private UploadObject uploadObject = null;

  private Logger uploadLogger = null;

  /**
   * 客户端连接会触发
   */
  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
    // 获取远程IP
    String remoteIP = insocket.getAddress().getHostAddress();

    // 本地IP和端口
    InetSocketAddress localAddress = (InetSocketAddress) ctx.channel().localAddress();
    String localIp = localAddress.getAddress().getHostAddress();
    String localPort = String.valueOf(localAddress.getPort());

    List<UploadObject> tempList = uploadObjectService.get();
    List<UploadObject> uploadObjectList = tempList.stream().filter(s -> s.getUploadProtocolEnum().toString().equals("E1")).collect(Collectors.toList());
    List<UploadFileChannel> uploadFileChannelList = uploadFileChannelService.get();

    for (UploadFileChannel u:uploadFileChannelList){
      if ((u.getLocalIp()!=null && u.getLocalPort()!=null) && u.getLocalIp().equals(localIp) && u.getLocalPort().equals(localPort) && u.getRemoteIp().equals(remoteIP)){
        for (UploadObject ub:uploadObjectList){
          if (u.getUploadObjectId()==ub.getId()){
            uploadFileChannel = u;
            uploadObject = ub;
            break;
          }
        }
      }
    }
    uploadLogger = appenderFactory.getLogger(uploadObject.getUploadObjectName(), uploadObject.getUploadProtocolEnum().getMessage(), uploadObject.getObjectNo());
    uploadLogger.info("远程"+remoteIP+"准备接入通道至本机的"+localIp+":"+localPort);
    if (FileConstant.channelIpPortMap.get(uploadObject.getId()+remoteIP) != null) {
      uploadLogger.info(remoteIP + ":" + FileConstant.channelIpPortMap.get(remoteIP) + "已在交互，新连接" + remoteIP + ":" + insocket.getPort() + "不能再次连接");
      ctx.close();
    } else {
      uploadLogger.info(ctx.channel().remoteAddress() + "接入通道");
      // 设置通道状态正常
      FileConstant.channelStatusMap.put(uploadObject.getId() + "-" + uploadFileChannel.getId(), "1");
      FileConstant.channelIpPortMap.put(uploadObject.getId()+remoteIP, insocket.getPort());
      List<ChannelDisconLog> channelDisconLogList = channelDisconLogService.get(String.valueOf(uploadFileChannel.getId()));
      if (channelDisconLogList.size() > 0) {
        channelDisconLogList.sort(Comparator.comparing(ChannelDisconLog::getCreateTime).reversed());
        Long reconnectTime = new Date().getTime();
        ChannelDisconLog channelDisconLog = channelDisconLogList.get(0);
        channelDisconLog.setReconnectTime(reconnectTime);
        // 计算时长
        Long durationL = reconnectTime - channelDisconLog.getDisconnectTime();
        if (durationL / 1000 >= 60) {
          channelDisconLog.setDuration(String.valueOf(durationL / (1000 * 60)) + "分");
        } else {
          channelDisconLog.setDuration(String.valueOf(durationL / 1000) + "秒");
        }
        channelDisconLogService.save(channelDisconLog);
      }
    }
  }

  /**
   * 客户端发消息会触发
   */
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // 本地IP和端口
    InetSocketAddress localAddress = (InetSocketAddress) ctx.channel().localAddress();
    // 设置通道状态正常
    FileConstant.channelStatusMap.put(uploadObject.getId() + "-" + uploadFileChannel.getId(), "1");
    // 获取场站信息
    ElectricField electricField = electricFieldService.get();
    // 返回的报文
    String receiveMessage = serverFor102StandardService.handlerMessage(msg.toString(), uploadObject, uploadFileChannel, electricField, uploadLogger, ctx);
    ctx.channel().writeAndFlush(receiveMessage);
  }

  /**
   * 发生异常触发
   */
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    Iterator<Map.Entry<String, FileMutableInteger>> countMap = FileConstant.uploadCountMap.entrySet().iterator();
    while (countMap.hasNext()) {
      Map.Entry<String, FileMutableInteger> entry = countMap.next();
      String key = entry.getKey();
      if (key.contains(uploadObject.getObjectNo() + "@")) {
        countMap.remove();
      }
    }

    Iterator<Map.Entry<String, String>> contentMap = FileConstant.fileContentMap.entrySet().iterator();
    while (contentMap.hasNext()) {
      Map.Entry<String, String> entry = contentMap.next();
      String key = entry.getKey();
      if (key.contains(uploadObject.getObjectNo() + "@")) {
        contentMap.remove();
      }
    }
    FileConstant.validateMessage.remove(uploadFileChannel.getId() + "");

    uploadLogger.error(uploadFileChannel.getChannelName()+" "+ctx.channel().remoteAddress() + "发生异常", cause);
    FileConstant.nettyInstanceMap.get(uploadFileChannel.getLocalIp()+ ":" + uploadFileChannel.getLocalPort()).destroy();
    FileConstant.nettyInstanceMap.remove(uploadFileChannel.getLocalIp()+ ":" + uploadFileChannel.getLocalPort());
    Thread.sleep(2000);
    uploadFileChannelService.startAllChannel();
  }

  /**
   * 通道断开触发
   */
  @Override
  public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
    String remoteIP = insocket.getAddress().getHostAddress();
    Integer remotePort = insocket.getPort();

    if (FileConstant.channelIpPortMap.get(uploadObject.getId()+remoteIP) != null) {
      if (FileConstant.channelIpPortMap.get(uploadObject.getId()+remoteIP).intValue() == remotePort.intValue()) {
        // 断开的连接是已经交互的连接
        uploadLogger.info(ctx.channel().remoteAddress() + "通道断开");

        // 设置通道状态不通
        FileConstant.channelStatusMap.put(uploadObject.getId() + "-" + uploadFileChannel.getId(), "0");
        // 保存通道断开日志
        ChannelDisconLog channelDisconLog = new ChannelDisconLog();
        channelDisconLog.setChannelId(String.valueOf(uploadFileChannel.getId()));
        channelDisconLog.setChannelName(uploadFileChannel.getChannelName());
        channelDisconLog.setDisconnectTime(new Date().getTime());
        channelDisconLogService.save(channelDisconLog);
        Iterator<Map.Entry<String, FileMutableInteger>> countMap = FileConstant.uploadCountMap.entrySet().iterator();
        while (countMap.hasNext()) {
          Map.Entry<String, FileMutableInteger> entry = countMap.next();
          String key = entry.getKey();
          if (key.contains(uploadObject.getObjectNo() + "@")) {
            countMap.remove();
          }
        }

        Iterator<Map.Entry<String, String>> contentMap = FileConstant.fileContentMap.entrySet().iterator();
        while (contentMap.hasNext()) {
          Map.Entry<String, String> entry = contentMap.next();
          String key = entry.getKey();
          if (key.contains(uploadObject.getObjectNo() + "@")) {
            contentMap.remove();
          }
        }
        FileConstant.validateMessage.remove(uploadFileChannel.getId() + "");
        FileConstant.channelIpPortMap.remove(uploadObject.getId()+remoteIP);
      }
    }
    super.channelInactive(ctx);
  }

  @Override
  public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    // 设置通道状态不通
    FileConstant.channelStatusMap.put(uploadObject.getId() + "-" + uploadFileChannel.getId(), "0");
    uploadLogger.info(ctx.channel().remoteAddress() + " {}秒没有报文交互,关闭通道", readerIdleTime);
    Iterator<Map.Entry<String, FileMutableInteger>> countMap = FileConstant.uploadCountMap.entrySet().iterator();
    while (countMap.hasNext()) {
      Map.Entry<String, FileMutableInteger> entry = countMap.next();
      String key = entry.getKey();
      if (key.contains(uploadObject.getObjectNo() + "@")) {
        countMap.remove();
      }
    }

    Iterator<Map.Entry<String, String>> contentMap = FileConstant.fileContentMap.entrySet().iterator();
    while (contentMap.hasNext()) {
      Map.Entry<String, String> entry = contentMap.next();
      String key = entry.getKey();
      if (key.contains(uploadObject.getObjectNo() + "@")) {
        contentMap.remove();
      }
    }
    FileConstant.validateMessage.remove(uploadFileChannel.getId() + "");

    log.info(uploadFileChannel.getChannelName()+" "+ctx.channel().remoteAddress() + " {}秒没有报文交互,自动重启102服务端", readerIdleTime);
    uploadLogger.info(uploadFileChannel.getChannelName()+" "+ctx.channel().remoteAddress() + " {}秒没有报文交互,自动重启102服务端", readerIdleTime);
    FileConstant.nettyInstanceMap.get(uploadFileChannel.getLocalIp()+ ":" + uploadFileChannel.getLocalPort()).destroy();
    FileConstant.nettyInstanceMap.remove(uploadFileChannel.getLocalIp()+ ":" + uploadFileChannel.getLocalPort());
    uploadFileChannelService.startAllChannel();
  }
}
