package org.kar.karideo;

import java.net.URI;
import java.util.logging.LogManager;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.kar.archidata.UpdateJwtPublicKey;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.catcher.GenericCatcher;
import org.kar.archidata.db.DbConfig;
import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter;
import org.kar.archidata.migration.MigrationEngine;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.ContextGenericTools;
import org.kar.karideo.api.Front;
import org.kar.karideo.api.HealthCheck;
import org.kar.karideo.api.MediaResource;
import org.kar.karideo.api.SeasonResource;
import org.kar.karideo.api.SeriesResource;
import org.kar.karideo.api.TypeResource;
import org.kar.karideo.api.UserMediaAdvancementResource;
import org.kar.karideo.api.UserResource;
import org.kar.karideo.filter.KarideoAuthenticationFilter;
import org.kar.karideo.migration.Initialization;
import org.kar.karideo.migration.Migration20230810;
import org.kar.karideo.migration.Migration20231015;
import org.kar.karideo.migration.Migration20231126;
import org.kar.karideo.migration.Migration20240226;
import org.kar.karideo.migration.Migration20240611;
import org.kar.karideo.migration.Migration20250214;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

import jakarta.ws.rs.core.UriBuilder;

public class WebLauncher {
	final static Logger LOGGER = LoggerFactory.getLogger(WebLauncher.class);
	protected UpdateJwtPublicKey keyUpdater = null;
	protected HttpServer server = null;

	public WebLauncher() {
		ConfigBaseVariable.bdDatabase = "karideo";
	}

	private static URI getBaseURI() {
		return UriBuilder.fromUri(ConfigBaseVariable.getlocalAddress()).build();
	}

	public void migrateDB() throws Exception {
		WebLauncher.LOGGER.info("Create migration engine");
		final MigrationEngine migrationEngine = new MigrationEngine();
		WebLauncher.LOGGER.info("Add initialization");
		migrationEngine.setInit(new Initialization());
		WebLauncher.LOGGER.info("Add migration since last version");
		migrationEngine.add(new Migration20230810());
		migrationEngine.add(new Migration20231015());
		migrationEngine.add(new Migration20231126());
		migrationEngine.add(new Migration20240226());
		migrationEngine.add(new Migration20240611());
		migrationEngine.add(new Migration20250214());
		WebLauncher.LOGGER.info("Migrate the DB [START]");
		migrationEngine.migrateWaitAdmin(new DbConfig());
		WebLauncher.LOGGER.info("Migrate the DB [STOP]");
	}

	public static void main(final String[] args) throws Exception {
		// Loop-back of logger JDK logging API to SLF4J
		LogManager.getLogManager().reset();
		SLF4JBridgeHandler.install();

		WebLauncher.LOGGER.info("[START] application wake UP");
		final WebLauncher launcher = new WebLauncher();
		launcher.migrateDB();
		launcher.process();
		WebLauncher.LOGGER.info("end-configure the server & wait finish process:");
		Thread.currentThread().join();
		WebLauncher.LOGGER.info("STOP Key updater");
		launcher.stopOther();
		WebLauncher.LOGGER.info("STOP the REST server");
	}

	public void process() throws InterruptedException {

		// ===================================================================
		// Configure resources
		// ===================================================================
		final ResourceConfig rc = new ResourceConfig();

		// add multi-part models ..
		rc.register(MultiPartFeature.class);
		// global authentication system
		rc.register(OptionFilter.class);
		// remove cors ==> all time called by an other system...
		rc.register(CORSFilter.class);
		// global authentication system
		rc.register(KarideoAuthenticationFilter.class);
		// register exception catcher
		GenericCatcher.addAll(rc);
		// add default resource:
		rc.register(UserResource.class);
		rc.register(SeriesResource.class);
		rc.register(DataResource.class);
		rc.register(SeasonResource.class);
		rc.register(TypeResource.class);
		rc.register(MediaResource.class);
		rc.register(UserMediaAdvancementResource.class);

		rc.register(HealthCheck.class);
		rc.register(Front.class);
		ContextGenericTools.addJsr310(rc);
		// add jackson to be discover when we are ins stand-alone server
		rc.register(JacksonFeature.class);
		// enable this to show low level request
		// rc.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, Level.WARNING.getName());

		this.server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc);
		final HttpServer serverLink = this.server;
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("Stopping server..");
				serverLink.shutdownNow();
			}
		}, "shutdownHook"));

		// ===================================================================
		// start periodic update of the token ...
		// ===================================================================
		this.keyUpdater = new UpdateJwtPublicKey();
		this.keyUpdater.start();

		// ===================================================================
		// run JERSEY
		// ===================================================================
		try {
			this.server.start();
			LOGGER.info("Jersey app started at {}", getBaseURI());
		} catch (final Exception e) {
			LOGGER.error("There was an error while starting Grizzly HTTP server.");
			e.printStackTrace();
		}
	}

	public void stop() {
		if (this.server != null) {
			this.server.shutdownNow();
			this.server = null;
		}
	}

	public void stopOther() {
		this.keyUpdater.kill();
		try {
			this.keyUpdater.join(4000, 0);
		} catch (final InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
