package vassistent.bootstrap;

import com.formdev.flatlaf.FlatDarkLaf;
import vassistent.model.AppState;
import vassistent.service.*;
import vassistent.util.AppConfigValidator;
import vassistent.util.ConfigLoader;
import vassistent.util.Logger;

import javax.swing.*;
import java.util.Properties;

/**
 * Bootstraps application services, subscriptions, and UI look-and-feel.
 */
public class ApplicationInitializer {

    private static Properties config =
            ConfigLoader.loadProperties(
                    "config/application.properties"
            );

    /**
     * Builds and wires the full application context including subscriptions and process startup.
     *
     * @return fully initialized application context
     */
    public static ApplicationContext initialize() {

        AppConfigValidator.validate(config);

        ApplicationContext context = new ApplicationContext();

        // ===== Model State =====
        context.setAppState(new AppState());

        // ===== Infrastructure Services =====
        String animationOutputPath =
                normalizeConfigValue(
                        config.getProperty(
                                "animation.output.path",
                                "data/animation.json"
                        )
                );

        context.setAnimationFileService(
                new AnimationFileService(animationOutputPath)
        );

        String mqttBrokerUrl =
                normalizeConfigValue(
                        config.getProperty(
                                "mqtt.broker.url",
                                "tcp://localhost:1883"
                        )
                );

        String mqttClientId =
                normalizeConfigValue(
                        config.getProperty(
                                "mqtt.client.id",
                                "JavaClientPublisherSubscriber"
                        )
                );

        context.setMqttService(
                new MqttClientService(
                        context.getAppState(),
                        mqttBrokerUrl,
                        mqttClientId
                )
        );

        context.setPersistenceService(new DataPersistenceService());

        context.setStatisticsService(
                new StatisticsService(context.getPersistenceService())
        );

        context.setEvaluationService(
                new EvaluationService(
                        context.getStatisticsService(),
                        context.getAppState(),
                        context.getAnimationFileService()
                )
        );

        context.setBinaryEventService(
                new BinaryEventService(
                        context.getPersistenceService(),
                        context.getEvaluationService()
                )
        );

        context.getMqttService().subscribe(
                config.getProperty("mqtt.topic"),
                payload -> context.getBinaryEventService()
                        .handlePayload(payload)
        );

        context.setProcessManagerService(new ProcessManagerService(config));

        Logger.info("PROCESS", "Starting configured external processes");

        ProcessManagerService.StartupReport startupReport =
                context.getProcessManagerService().startProcesses();

        logProcessStartupReport(startupReport);

        if (startupReport.hasFailures()) {
            context.getProcessManagerService().shutdown();
            throw new IllegalStateException(startupReport.buildFailureMessage());
        }

        return context;
    }

    /**
     * Applies the FlatLaf look-and-feel and selected UI defaults.
     */
    public static void initLookAndFeel() {
        try {
            UIManager.setLookAndFeel(new FlatDarkLaf());
            UIManager.put("Component.focusWidth", 1);
            UIManager.put("Button.arc", 8);
            UIManager.put("TextComponent.arc", 8);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Trims config values and removes optional wrapping single/double quotes.
     *
     * @param value raw config value
     * @return normalized config value or {@code null}
     */
    private static String normalizeConfigValue(String value) {
        if (value == null) {
            return null;
        }

        String trimmed = value.trim();

        if ((trimmed.startsWith("\"") && trimmed.endsWith("\""))
                || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
            return trimmed.substring(1, trimmed.length() - 1);
        }

        return trimmed;
    }

    /**
     * Logs startup status for all process components and the global startup result.
     *
     * @param startupReport startup status report from {@link ProcessManagerService}
     */
    private static void logProcessStartupReport(
            ProcessManagerService.StartupReport startupReport
    ) {
        logProcessStartStatus(startupReport.getMqttSimulator());
        logProcessStartStatus(startupReport.getUnreal());

        if (startupReport.hasFailures()) {
            Logger.error("PROCESS", startupReport.buildFailureMessage());
            return;
        }

        Logger.info("PROCESS", "External process startup completed successfully");
    }

    /**
     * Logs one startup component status from the process startup report.
     *
     * @param status startup status for one managed process component
     */
    private static void logProcessStartStatus(
            ProcessManagerService.ProcessStartStatus status
    ) {
        String component = status.getComponentName();

        if (!status.isEnabled()) {
            Logger.info("PROCESS", component + " startup skipped (disabled)");
            return;
        }

        if (status.isStarted()) {
            Logger.info("PROCESS", component + " startup succeeded");
            return;
        }

        String error = status.getErrorMessage();
        if (error == null || error.isBlank()) {
            Logger.error("PROCESS", component + " startup failed");
        } else {
            Logger.error("PROCESS", component + " startup failed: " + error);
        }
    }
}
