package ru.yandex.qe.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.SmartLifecycle;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Listens at given port and stops application when anybody connects to this port.
 *
 * @author lvovich
 */

public class ApplicationStopMonitor implements SmartLifecycle, ApplicationContextAware {

    private final static Logger LOG = LoggerFactory.getLogger(ApplicationStopMonitor.class);

    private ApplicationContext applicationContext;

    public void setApplicationContext(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    private StopMonitoringThread monitoringThread;

    @Override
    public boolean isAutoStartup() {
        return false;
    }

    @Override
    public void stop(final Runnable callback) {
        stop();
        callback.run();
    }

    @Override
    public void start() {
        final String stopPort = System.getProperty("application.stop.port");
        if (stopPort != null) {
            monitoringThread = new StopMonitoringThread(Integer.valueOf(stopPort));
            monitoringThread.start();
        }
    }

    @Override
    public void stop() {
        if (monitoringThread != null) {
            monitoringThread.interrupt();
        }
    }

    @Override
    public boolean isRunning() {
        return monitoringThread != null && monitoringThread.isAlive();
    }

    @Override
    public int getPhase() {
        return 1;
    }

    private class StopMonitoringThread extends Thread {

        private ServerSocket socket;
        private final int port;

        public StopMonitoringThread(final int stopPort) {
            this.port = stopPort;
            setDaemon(true);
            setName("StopMonitor");
            try {
                socket = new ServerSocket(stopPort, 1, InetAddress.getByName("127.0.0.1"));
            } catch(Exception e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void run() {
            Socket accept;
            try {
                LOG.info("Application stop monitor listening on {}", port);
                accept = socket.accept();
                BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
                reader.readLine();
                LOG.info("*** stopping application");
                accept.close();
                socket.close();
                if (applicationContext instanceof ConfigurableApplicationContext) {
                    LOG.info("Closing applicationContext");
                    ((ConfigurableApplicationContext) applicationContext).close();
                } else {
                    LOG.warn("applicationContext is not ConfigurableWebApplicationContext, cannot close: ");
                }
            } catch(Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

}
