package com.ctrip.xpipe.redis.core.proxy.command;

import com.ctrip.xpipe.api.pool.SimpleObjectPool;
import com.ctrip.xpipe.api.proxy.ProxyProtocol;
import com.ctrip.xpipe.netty.commands.NettyClient;
import com.ctrip.xpipe.redis.core.protocal.protocal.SimpleStringParser;
import com.ctrip.xpipe.redis.core.proxy.PROXY_OPTION;
import com.ctrip.xpipe.redis.core.proxy.exception.XPipeProxyResultException;
import com.ctrip.xpipe.redis.core.proxy.monitor.*;
import com.ctrip.xpipe.redis.core.proxy.parser.monitor.ProxyMonitorParser;
import io.netty.buffer.ByteBuf;

import java.util.concurrent.ScheduledExecutorService;

/**
 * @author chen.zhu
 * <p>
 * Oct 26, 2018
 */
public abstract class AbstractProxyMonitorCommand<T> extends AbstractProxyCommand<T[]> {

    private static final String MONITOR_PREFIX = String.format("%s %s", ProxyProtocol.KEY_WORD, PROXY_OPTION.MONITOR.name());

    public AbstractProxyMonitorCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled) {
        super(clientPool, scheduled);
    }

    public AbstractProxyMonitorCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled,
                                       int commandTimeoutMilli) {
        super(clientPool, scheduled, commandTimeoutMilli);
    }

    @Override
    public ByteBuf getRequest() {
        return new SimpleStringParser(String.format("%s %s", MONITOR_PREFIX, getType().name())).format();
    }

    @Override
    protected T[] format(Object payload) {
        if(!payload.getClass().isArray()) {
            throw new XPipeProxyResultException(getClass().getSimpleName() + ": result should be an array");
        }
        Object[] objects = (Object[]) payload;
        T[] result = initArray(objects);
        int index = 0;
        for(Object object : objects) {
            result[index ++] = parseObject(object);
        }
        return result;
    }

    @Override
    protected boolean logRequest() {
        return false;
    }

    @Override
    protected boolean logResponse() {
        return false;
    }

    protected abstract T[] initArray(Object[] objects);

    protected abstract T parseObject(Object object);

    protected abstract ProxyMonitorParser.Type getType();


    public static class ProxyMonitorSocketStatsCommand extends AbstractProxyMonitorCommand<TunnelSocketStatsResult> {

        public ProxyMonitorSocketStatsCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled) {
            super(clientPool, scheduled);
        }

        public ProxyMonitorSocketStatsCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled, int commandTimeoutMilli) {
            super(clientPool, scheduled, commandTimeoutMilli);
        }

        @Override
        protected TunnelSocketStatsResult[] initArray(Object[] objects) {
            return new TunnelSocketStatsResult[objects.length];
        }

        @Override
        protected TunnelSocketStatsResult parseObject(Object object) {
            return TunnelSocketStatsResult.parse(object);
        }

        @Override
        protected ProxyMonitorParser.Type getType() {
            return ProxyMonitorParser.Type.SocketStats;
        }

    }

    public static class ProxyMonitorTunnelStatsCommand extends AbstractProxyMonitorCommand<TunnelStatsResult> {

        public ProxyMonitorTunnelStatsCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled) {
            super(clientPool, scheduled);
        }

        public ProxyMonitorTunnelStatsCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled, int commandTimeoutMilli) {
            super(clientPool, scheduled, commandTimeoutMilli);
        }

        @Override
        protected TunnelStatsResult[] initArray(Object[] objects) {
            return new TunnelStatsResult[objects.length];
        }

        @Override
        protected TunnelStatsResult parseObject(Object object) {
            return TunnelStatsResult.parse(object);
        }

        @Override
        protected ProxyMonitorParser.Type getType() {
            return ProxyMonitorParser.Type.TunnelStats;
        }


    }

    public static class ProxyMonitorPingStatsCommand extends AbstractProxyMonitorCommand<PingStatsResult> {

        public ProxyMonitorPingStatsCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled) {
            super(clientPool, scheduled);
        }

        public ProxyMonitorPingStatsCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled, int commandTimeoutMilli) {
            super(clientPool, scheduled, commandTimeoutMilli);
        }

        @Override
        protected PingStatsResult[] initArray(Object[] objects) {
            return new PingStatsResult[objects.length];
        }

        @Override
        protected PingStatsResult parseObject(Object object) {
            return PingStatsResult.parse(object);
        }

        @Override
        protected ProxyMonitorParser.Type getType() {
            return ProxyMonitorParser.Type.PingStats;
        }

    }

    public static class ProxyMonitorTrafficStatsCommand extends AbstractProxyMonitorCommand<TunnelTrafficResult> {

        public ProxyMonitorTrafficStatsCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled) {
            super(clientPool, scheduled);
        }

        public ProxyMonitorTrafficStatsCommand(SimpleObjectPool<NettyClient> clientPool, ScheduledExecutorService scheduled, int commandTimeoutMilli) {
            super(clientPool, scheduled, commandTimeoutMilli);
        }

        @Override
        protected TunnelTrafficResult[] initArray(Object[] objects) {
            return new TunnelTrafficResult[objects.length];
        }

        @Override
        protected TunnelTrafficResult parseObject(Object object) {
            return TunnelTrafficResult.parse(object);
        }

        @Override
        protected ProxyMonitorParser.Type getType() {
            return ProxyMonitorParser.Type.TrafficStats;
        }

    }
}

@@ -874,24 +874,167 @@
 R.name());%0A%0A
+    private static final int PROXY_CONNECTION_TIMEOUT_MILLI = Integer.parseInt(System.getProperty(%22proxy.connection.timeout.milli%22, %225000%22));%0A%0A
     public A
@@ -2049,32 +2049,142 @@
  result;%0A    %7D%0A%0A
+    @Override%0A    public int getCommandTimeoutMilli() %7B%0A        return PROXY_CONNECTION_TIMEOUT_MILLI;%0A    %7D%0A%0A
     @Override%0A  
@@ -4450,17 +4450,16 @@
      %7D%0A%0A
-%0A
     %7D%0A%0A 

make proxy connect timeout for 5 sec as default