package com.airbnb.billow;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.health.HealthCheck;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.codahale.metrics.jetty9.InstrumentedHandler;
import com.codahale.metrics.servlets.AdminServlet;
import com.codahale.metrics.servlets.HealthCheckServlet;
import com.codahale.metrics.servlets.MetricsServlet;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigFactory;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;

import javax.servlet.ServletContext;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import static com.google.common.io.Resources.getResource;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

@Slf4j
public class Main {

    private Main(Config config) throws Exception {
        log.info("Startup...");

        try {
            System.out.println(Resources.toString(getResource("banner.txt"), Charsets.UTF_8));
        } catch (IllegalArgumentException | IOException e) {
            log.debug("No banner.txt", e);
        }

        final MetricRegistry metricRegistry = new MetricRegistry();
        final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();

        log.info("Creating database");

        final Config awsConfig = config.getConfig("aws");
        final Long refreshRate = awsConfig.getDuration("refreshRate", TimeUnit.MILLISECONDS);

        final AWSDatabaseHolder dbHolder = new AWSDatabaseHolder(awsConfig);

        final Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.getContext().put(AWSDatabaseHolderRefreshJob.DB_KEY, dbHolder);
        scheduler.start();

        final SimpleTrigger trigger = newTrigger().
                withIdentity(AWSDatabaseHolderRefreshJob.NAME).
                startNow().
                withSchedule(simpleSchedule().withIntervalInMilliseconds(refreshRate).repeatForever()).
                build();

        final JobDetail jobDetail = newJob(AWSDatabaseHolderRefreshJob.class).
                withIdentity(AWSDatabaseHolderRefreshJob.NAME).
                build();

        scheduler.scheduleJob(jobDetail, trigger);

        log.info("Creating age health check");
        healthCheckRegistry.register("DB", new HealthCheck() {
            @Override
            protected Result check() throws Exception {
                return dbHolder.healthy();
            }
        });

        log.info("Creating HTTP servers");
        final Server mainServer = new Server(config.getInt("mainPort"));
        final Server adminServer = new Server(config.getInt("adminPort"));

        configureConnectors(mainServer);
        configureConnectors(adminServer);

        log.info("Creating HTTP handlers");
        final Handler mainHandler = new Handler(metricRegistry, dbHolder, refreshRate);
        final InstrumentedHandler instrumentedHandler =
                new InstrumentedHandler(metricRegistry);
        instrumentedHandler.setHandler(mainHandler);

        mainServer.setHandler(instrumentedHandler);

        final ServletContextHandler adminHandler = new ServletContextHandler();
        adminHandler.addServlet(new ServletHolder(new AdminServlet()), "/*");

        final ServletContext adminContext = adminHandler.getServletContext();
        adminContext.setAttribute(MetricsServlet.METRICS_REGISTRY, metricRegistry);
        adminContext.setAttribute(HealthCheckServlet.HEALTH_CHECK_REGISTRY, healthCheckRegistry);
        adminServer.setHandler(adminHandler);

        log.info("Starting HTTP servers");

        adminServer.start();
        mainServer.start();

        log.info("Joining...");

        mainServer.join();
        adminServer.join();

        log.info("Shutting down scheduler...");

        scheduler.shutdown();

        log.info("We're done!");
    }

    public static void main(String[] args) {
        try {
            final Config config = ConfigFactory.load().getConfig("billow");
            try {
                System.setProperty("aws.accessKeyId", config.getString("aws.accessKeyId"));
                System.setProperty("aws.secretKey", config.getString("aws.secretKeyId"));
            } catch (ConfigException.Missing _) {
                System.clearProperty("aws.accessKeyId");
                System.clearProperty("aws.secretKey");
            }
            Main.log.debug("Loaded config: {}", config);
            new Main(config);
        } catch (Throwable t) {
            Main.log.error("Failure in main thread, getting out!", t);
            System.exit(1);
        }
    }

    private static void configureConnectors(Server server) {
        for (Connector c : server.getConnectors()) {
            for (ConnectionFactory f : c.getConnectionFactories())
                if (f instanceof HttpConnectionFactory) {
                    final HttpConfiguration httpConf =
                            ((HttpConnectionFactory) f).getHttpConfiguration();
                    httpConf.setSendServerVersion(false);
                    httpConf.setSendDateHeader(false);
                }
        }
    }
}