package de.deadlocker8.budgetmaster;

import de.deadlocker8.budgetmaster.utils.DatabaseConfigurationProperties;
import de.thecodelabs.utils.io.PathUtils;
import de.thecodelabs.utils.util.Localization;
import de.thecodelabs.utils.util.SystemUtils;
import de.thecodelabs.utils.util.localization.LocalizationMessageFormatter;
import de.thecodelabs.utils.util.localization.formatter.JavaMessageFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.scheduling.annotation.EnableScheduling;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.*;


@EnableScheduling
@SpringBootApplication
@EnableConfigurationProperties(DatabaseConfigurationProperties.class)
public class BudgetMasterServerMain extends SpringBootServletInitializer implements ApplicationRunner
{
	private static final Logger LOGGER = LoggerFactory.getLogger(BudgetMasterServerMain.class);

	static
	{
		prepare(new String[0]);
	}

	@SuppressWarnings("ConstantConditions")
	private static Path prepare(String[] args)
	{
		Localization.setDelegate(new Localization.LocalizationDelegate()
		{
			@Override
			public Locale getLocale()
			{
				return Locale.ENGLISH;
			}

			@Override
			public String[] getBaseResources()
			{
				return new String[]{"languages/base", "languages/news", "languages/hints"};
			}

			@Override
			public LocalizationMessageFormatter messageFormatter()
			{
				return new JavaMessageFormatter();
			}

			@Override
			public boolean useMultipleResourceBundles()
			{
				return true;
			}
		});
		Localization.load();

		ProgramArgs.setArgs(Arrays.asList(args));
		LOGGER.debug(MessageFormat.format("Starting with ProgramArgs: {0}", ProgramArgs.getArgs()));

		Path applicationSupportFolder = getApplicationSupportFolder(false);
		PathUtils.createDirectoriesIfNotExists(applicationSupportFolder);

		Path settingsPath = applicationSupportFolder.resolve("settings.properties");
		if(Files.notExists(settingsPath))
		{
			try
			{
				LOGGER.warn("BudgetMaster settings file ({}) is missing. A default file will be created. Please fill in your settings.", settingsPath);
				Files.copy(BudgetMasterServerMain.class.getClassLoader().getResourceAsStream("config/templates/settings.properties"), settingsPath, StandardCopyOption.REPLACE_EXISTING);
				System.exit(1);
			}
			catch(IOException e)
			{
				LOGGER.error("Could not create BudgetMaster default settings file", e);
			}
		}

		return applicationSupportFolder;
	}

	public static Path getApplicationSupportFolder(boolean suppressLog)
	{
		if(System.getProperties().containsKey("testProfile"))
		{
			RunMode.currentRunMode = RunMode.TEST;
		}
		else if(ProgramArgs.isDebug())
		{
			RunMode.currentRunMode = RunMode.DEBUG;
		}

		Path savePath = SystemUtils.getApplicationSupportDirectoryPath(Localization.getString("folder"));

		final Optional<String> customFolderOptional = ProgramArgs.getCustomFolder();
		if(customFolderOptional.isPresent())
		{
			final Path customFolder = Paths.get(customFolderOptional.get());
			if(customFolder.isAbsolute())
			{
				savePath = customFolder;
			}
			else
			{
				LOGGER.error(MessageFormat.format("Ignoring option --customFolder: provided path ''{0}'' is not absolute", customFolder));
			}
		}

		savePath = determineFolder(savePath, suppressLog);
		if(!suppressLog)
		{
			LOGGER.info(MessageFormat.format("Used save path: {0}", savePath));
		}
		return savePath;
	}

	private static Path determineFolder(Path baseFolder, boolean suppressLog)
	{
		switch(RunMode.currentRunMode)
		{
			case NORMAL ->
			{
				if(!suppressLog)
				{
					LOGGER.info("Starting in NORMAL Mode");
				}
				return baseFolder;
			}
			case DEBUG ->
			{
				if(!suppressLog)
				{
					LOGGER.info("Starting in DEBUG Mode");
				}
				return baseFolder.resolve("debug");
			}
			case TEST ->
			{
				if(!suppressLog)
				{
					LOGGER.info("Starting in TEST Mode");
				}
				return baseFolder.resolve("test");
			}
			default ->
			{
				if(!suppressLog)
				{
					LOGGER.warn("Mode not supported! Starting in NORMAL Mode");
				}
				return baseFolder;
			}
		}
	}

	public static void main(String[] args)
	{
		Path applicationSupportFolder = prepare(args);
		Path logPath = applicationSupportFolder.resolve("error.log");

		String loggingArgument = "--logging.file.name=" + logPath;
		List<String> arguments = new ArrayList<>(ProgramArgs.getArgs());
		if(!arguments.contains(loggingArgument))
		{
			arguments.add(loggingArgument);
		}

		args = new String[arguments.size()];
		args = arguments.toArray(args);

		SpringApplication.run(BudgetMasterServerMain.class, args);
	}

	@Override
	public void run(ApplicationArguments args)
	{
		Build build = Build.getInstance();
		logAppInfo(build.getAppName(), build.getVersionName(), build.getVersionCode(), build.getVersionDate());

		switch(RunMode.currentRunMode)
		{
			case NORMAL:
				LOGGER.info("=============================");
				LOGGER.info("+++ BUDGETMASTER STARTED +++");
				LOGGER.info("=============================");
				break;
			case DEBUG:
				LOGGER.info("==================================");
				LOGGER.info("+++ BUDGETMASTER DEBUG STARTED +++");
				LOGGER.info("==================================");
				break;
			case TEST:
				LOGGER.info("=================================");
				LOGGER.info("+++ BUDGETMASTER TEST STARTED +++");
				LOGGER.info("=================================");
				break;
		}
	}

	private static void logAppInfo(String appName, String versionName, String versionCode, String versionDate)
	{
		LOGGER.info(MessageFormat.format("{0} - v{1} - (versioncode: {2}) from {3})", appName, versionName, versionCode, versionDate));
	}
}