package com.lanyuanxiaoyao.service.sync.utils;

import cn.hutool.core.util.ObjectUtil;
import com.lanyuanxiaoyao.service.common.Constants;
import com.lanyuanxiaoyao.service.common.entity.FlinkJob;
import com.lanyuanxiaoyao.service.common.entity.TableMeta;
import com.lanyuanxiaoyao.service.common.utils.NameHelper;
import org.apache.flink.shaded.curator4.org.apache.curator.framework.CuratorFramework;
import org.apache.flink.shaded.curator4.org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.flink.shaded.curator4.org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.flink.shaded.curator4.org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.flink.shaded.curator4.org.apache.curator.utils.CloseableUtils;
import org.apache.flink.shaded.zookeeper3.org.apache.zookeeper.CreateMode;
import org.apache.flink.shaded.zookeeper3.org.apache.zookeeper.KeeperException;
import org.apache.flink.shaded.zookeeper3.org.apache.zookeeper.client.ZooKeeperSaslClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Zk 操作
 *
 * @author ZhangJiacheng
 * @date 2023-05-10
 */
public class ZkUtils {
    private static final Logger logger = LoggerFactory.getLogger(ZkUtils.class);
    private static CuratorFramework client;

    private static void createClient(String url) {
        System.setProperty(ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY, "false");
        if (ObjectUtil.isNull(client)) {
            client = CuratorFrameworkFactory.builder()
                    .connectString(url)
                    .retryPolicy(new ExponentialBackoffRetry((int) (5 * Constants.SECOND), 5))
                    .sessionTimeoutMs((int) (10 * Constants.SECOND))
                    .connectionTimeoutMs((int) (10 * Constants.SECOND))
                    .build();
        }
        if (!CuratorFrameworkState.STARTED.equals(client.getState())) {
            client.start();
        }
    }

    public static void closeClient() {
        if (ObjectUtil.isNotNull(client)) {
            CloseableUtils.closeQuietly(client);
        }
    }

    public static void createSynchronizerLock(FlinkJob job, String zookeeperUrl, String runMeta) {
        createLock(zookeeperUrl, runMeta, NameHelper.syncRunningLockPath(job.getId()));
    }

    public static void createSynchronizerLock(FlinkJob job, TableMeta meta, String zookeeperUrl, String runMeta) {
        createLock(zookeeperUrl, runMeta, NameHelper.syncRunningLockPath(job.getId(), meta.getAlias()));
    }

    public static void releaseSynchronizerLock(FlinkJob job, TableMeta meta) {
        releaseLock(NameHelper.syncRunningLockPath(job.getId(), meta.getAlias()));
    }

    public static void createCompactionLock(FlinkJob job, TableMeta meta, String zookeeperUrl, String runMeta) {
        createLock(zookeeperUrl, runMeta, NameHelper.compactionRunningLockPath(job.getId(), meta.getAlias()));
    }

    public static void releaseCompactionLock(FlinkJob job, TableMeta meta) {
        releaseLock(NameHelper.compactionRunningLockPath(job.getId(), meta.getAlias()));
    }

    private static void createLock(String zookeeperUrl, String runMeta, String lockPath) {
        try {
            createClient(zookeeperUrl);
            client.create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.EPHEMERAL)
                    .forPath(lockPath, runMeta.getBytes());
        } catch (KeeperException.NodeExistsException e) {
            logger.error("Lock exists for " + lockPath, e);
            throw new RuntimeException(e);
        } catch (Exception e) {
            logger.error("Unknown error", e);
            throw new RuntimeException(e);
        }
    }

    private static void releaseLock(String lockPath) {
        try {
            if (ObjectUtil.isNotNull(client)) {
                if (ObjectUtil.isNotNull(client.checkExists().forPath(lockPath))) {
                    client.delete().forPath(lockPath);
                }
            }
        } catch (Exception e) {
            logger.error("Unknown error", e);
            throw new RuntimeException(e);
        }
    }
}
