package com.chushang.common.canal.client.transfer;

import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
import com.chushang.common.canal.client.ListenerPoint;
import com.chushang.common.canal.config.CanalConfig;
import com.chushang.common.canal.event.CanalEventListener;
import com.chushang.common.core.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public abstract class AbstractMessageTransponder extends MessageTransponder {

    /**
     * canal connector
     */
    private final CanalConnector connector;

    /**
     * custom config
     */
    protected final CanalConfig.Instance config;

    /**
     * destination of canal server
     */
    protected final String destination;

    /**
     * listeners which are used by implementing the Interface
     */
    protected final List<CanalEventListener> listeners = new ArrayList<>();

    /**
     * listeners which are used by annotation
     */
    protected final List<ListenerPoint> annoListeners = new ArrayList<>();

    /**
     * running flag
     */
    private volatile boolean running = true;

    private static final Logger logger = LoggerFactory.getLogger(AbstractMessageTransponder.class);

    public AbstractMessageTransponder(CanalConnector connector,
                                      Map.Entry<String, CanalConfig.Instance> config,
                                      List<CanalEventListener> listeners,
                                      List<ListenerPoint> annoListeners) {
        Objects.requireNonNull(connector, "connector can not be null!");
        Objects.requireNonNull(config, "config can not be null!");
        this.connector = connector;
        this.destination = config.getKey();
        this.config = config.getValue();
        if (listeners != null)
            this.listeners.addAll(listeners);
        if (annoListeners != null)
            this.annoListeners.addAll(annoListeners);
    }

    @Override
    public void run() {
        // 在 run 时 才进行连接
        connect();
        int errorCount = config.getRetryCount();
        final long interval = config.getAcquireInterval();
        final String threadName = Thread.currentThread().getName();
        boolean interrupted = Thread.currentThread().isInterrupted();
        do {
            try {
                Message message = connector.getWithoutAck(config.getBatchSize());
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (logger.isDebugEnabled()) {
                    logger.debug("{}: Get message from canal server >>>>> size:{}", threadName, size);
                }
                //empty message
                if (batchId == -1 || size == 0) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("{}: Empty message... sleep for {} millis", threadName, interval);
                    }
                    Thread.sleep(interval);
                } else {
                    distributeEvent(message.getEntries());
                }

                // commit ack
                connector.ack(batchId);
                if (logger.isDebugEnabled()) {
                    logger.debug("{}: Ack message. batchId:{}", threadName, batchId);
                }
            } catch (CanalClientException e) {
                errorCount--;
                logger.error(threadName + ": Error occurred!! ", e);
                try {
                    Thread.sleep(interval);
                } catch (InterruptedException e1) {
                    errorCount = 0;
                }
            } catch (InterruptedException e) {
                errorCount = 0;
                connector.rollback();
            } finally {
                // 重试次数 为0, 不在进行重试, 等待重新连接
                if (errorCount <= 0) {
                    stop();
                    logger.info("{}: Topping the client.. ", Thread.currentThread().getName());
                }
            }
        } while ((running && !interrupted));
    }

    protected abstract void distributeEvent(List<CanalEntry.Entry> entryList);

    private void connect() {
        connector.connect();
        // 此处 添加 过滤 ->
        if (StringUtils.isNotEmpty(config.getFilter())) {
            connector.subscribe(config.getFilter());
        } else {
            connector.subscribe();
        }
        connector.rollback();

        logger.info("connector is connect");
    }

    /**
     * stop running
     */
    void stop() {
        // 此处应当就是失败了, 需要停止进行重试
        logger.info("{}: client stopped. ", Thread.currentThread().getName());
        running = false;
        if (null == connector) {
            return;
        }
        // 停止时, 关闭连接
        connector.disconnect();
        // 说明失败重试
        if (config.isErrorRetry()) {
            logger.info("connector restart");

            long errorRetryTime = config.getErrorRetryTime();
            running = true;
            // 延时执行
            timer.schedule(this, errorRetryTime);
        }
    }
}
