package Zookeeper;

import ch.qos.logback.classic.Logger;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

public class ServiceRegistry implements Watcher {


    private static final String REGISTRY_ZNODE = "/service_registry";
    private final ZooKeeper zooKeeper;

    private String currentZnode = null;
    private static final Logger logger = (Logger) LoggerFactory.getLogger(ServiceRegistry.class);
    private static List<Worker> workers= new ArrayList<>();
    private Autohealer autohealer;
    public ServiceRegistry(ZooKeeper zooKeeper, Autohealer autohealer) {
        this.autohealer=autohealer;
        this.zooKeeper = zooKeeper;
        createServiceRegistryZnode();
    }

    private void createServiceRegistryZnode() {
        try {
            if (zooKeeper.exists(REGISTRY_ZNODE, false) == null) {
                zooKeeper.create(REGISTRY_ZNODE, new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void registerToCluster(String metadata) throws KeeperException, InterruptedException {
        if (this.currentZnode != null) {
            logger.info("Already registered to service registry");
            return;
        }
        this.currentZnode = zooKeeper.create(REGISTRY_ZNODE + "/n_", metadata.getBytes(),
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        logger.info("Registered to service registry");
    }

    public void registerForUpdates() {
        try {
            updateAddresses();
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void unregisterFromCluster() {
        try {
            if (currentZnode != null && zooKeeper.exists(currentZnode, false) != null) {
                zooKeeper.delete(currentZnode, -1);
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private synchronized void updateAddresses() throws KeeperException, InterruptedException {
        System.out.println("Enter");
        List<String> workerZnodes = zooKeeper.getChildren(REGISTRY_ZNODE, this);
//        SortNodes(workerZnodes);
//        List<String> addresses = new ArrayList<>();
//        List<String> names = new ArrayList<>();
        List<Worker> workerList= new ArrayList<>();
        Thread.sleep(100);
        for (String workerZnode : workerZnodes) {
            String workerFullPath = REGISTRY_ZNODE + "/" + workerZnode;
            Worker newWorker = new Worker(workerZnode);
            Stat stat = zooKeeper.exists(workerFullPath, false);
            if (stat == null) {
                continue;
            }

            byte[] addressBytes = zooKeeper.getData(workerFullPath, false, stat);
            String data = new String(addressBytes);
            String[] parts = data.split(",");
            String address = parts[0];
            newWorker.setIpAddress(address);
            newWorker.setUserName(parts[1]);
            workerList.add(newWorker);
        }
        Worker.SortNodes(workerList);
        System.out.println(workers);
        if(workers!=null)
            workers.removeAll(workers);
        if(workerList!=null)
            workers.addAll(workerList);
        logger.info("The cluster addresses are: " + Worker.getIpAddress(workers));
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        try {
            updateAddresses();
            System.out.println(workers);
            Thread.sleep(1000);
            if(workers!=null)
                autohealer.startWatchingWorkers();
        } catch (KeeperException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


    public static List<Worker> getWorkers() {
        return workers;
    }
}