package vassistent.util;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Unit tests for {@link AppConfigValidator}.
 */
class AppConfigValidatorTest {

    @TempDir
    Path tempDir;

    /**
     * Verifies that a minimal core configuration passes validation.
     */
    @Test
    void validate_withCoreSettingsOnly_doesNotThrow() {
        Properties config = baseConfig();

        assertDoesNotThrow(() -> AppConfigValidator.validate(config));
    }

    /**
     * Verifies strict boolean validation for feature toggles.
     */
    @Test
    void validate_withInvalidBooleanToggle_throws() {
        Properties config = baseConfig();
        config.setProperty("mqtt_sim.enabled", "yes");

        IllegalStateException exception =
                assertThrows(
                        IllegalStateException.class,
                        () -> AppConfigValidator.validate(config)
                );

        assertTrue(exception.getMessage().contains("mqtt_sim.enabled"));
    }

    /**
     * Verifies strict boolean validation for startup health-check toggles.
     */
    @Test
    void validate_withInvalidStartupHealthcheckBoolean_throws() {
        Properties config = baseConfig();
        config.setProperty("process.startup.healthcheck.enabled", "enabled");

        IllegalStateException exception =
                assertThrows(
                        IllegalStateException.class,
                        () -> AppConfigValidator.validate(config)
                );

        assertTrue(
                exception.getMessage().contains("process.startup.healthcheck.enabled")
        );
    }

    /**
     * Verifies startup health-check timeout must be a positive integer.
     */
    @Test
    void validate_withNonPositiveStartupHealthcheckTimeout_throws() {
        Properties config = baseConfig();
        config.setProperty("process.startup.healthcheck.timeout.millis", "0");

        IllegalStateException exception =
                assertThrows(
                        IllegalStateException.class,
                        () -> AppConfigValidator.validate(config)
                );

        assertTrue(
                exception.getMessage().contains("process.startup.healthcheck.timeout.millis")
        );
    }

    /**
     * Verifies simulator-specific numeric validation when simulator is enabled.
     *
     * @throws Exception if test files cannot be created
     */
    @Test
    void validate_withMqttSimulatorInvalidPort_throws() throws Exception {
        Properties config = baseConfig();
        Path pythonExe = Files.createFile(tempDir.resolve("python.exe"));
        Path simulatorScript = Files.createFile(tempDir.resolve("mqtt_simulator.py"));

        config.setProperty("mqtt_sim.enabled", "true");
        config.setProperty("python.path", pythonExe.toString());
        config.setProperty("mqtt_sim.script", simulatorScript.toString());
        config.setProperty("mqtt_sim.broker", "localhost");
        config.setProperty("mqtt_sim.port", "99999");

        IllegalStateException exception =
                assertThrows(
                        IllegalStateException.class,
                        () -> AppConfigValidator.validate(config)
                );

        assertTrue(exception.getMessage().contains("mqtt_sim.port"));
    }

    /**
     * Verifies Unreal validation detects paths with mismatched quote wrappers.
     *
     * @throws Exception if test files cannot be created
     */
    @Test
    void validate_withUnrealSignallingPathTrailingQuote_throws() throws Exception {
        Properties config = baseConfig();
        Path launcherScript = Files.createFile(tempDir.resolve("start_avatar.ps1"));
        Path signallingScript = Files.createFile(tempDir.resolve("start_with_stun.bat"));
        Path unrealExe = Files.createFile(tempDir.resolve("Prototyp1.exe"));
        Path unrealWorkDir = Files.createDirectory(tempDir.resolve("Windows"));
        Path pidDir = Files.createDirectory(tempDir.resolve("pids"));

        config.setProperty("unreal.enabled", "true");
        config.setProperty("unreal.executable", launcherScript.toString());
        config.setProperty(
                "unreal.signalling_server.script",
                signallingScript.toString() + "\""
        );
        config.setProperty("unreal.target.executable", unrealExe.toString());
        config.setProperty("unreal.target.working_dir", unrealWorkDir.toString());
        config.setProperty("unreal.pid.dir", pidDir.toString());

        IllegalStateException exception =
                assertThrows(
                        IllegalStateException.class,
                        () -> AppConfigValidator.validate(config)
                );

        assertTrue(exception.getMessage().contains("unreal.signalling_server.script"));
        assertTrue(exception.getMessage().toLowerCase().contains("quote"));
    }

    /**
     * Verifies Unreal validation accepts a complete and valid local setup.
     *
     * @throws Exception if test files cannot be created
     */
    @Test
    void validate_withUnrealEnabledAndValidPaths_doesNotThrow() throws Exception {
        Properties config = baseConfig();
        Path launcherScript = Files.createFile(tempDir.resolve("start_avatar.ps1"));
        Path signallingScript = Files.createFile(tempDir.resolve("start_with_stun.bat"));
        Path unrealExe = Files.createFile(tempDir.resolve("Prototyp1.exe"));
        Path unrealWorkDir = Files.createDirectory(tempDir.resolve("Windows"));
        Path pidDir = Files.createDirectory(tempDir.resolve("pids"));

        config.setProperty("unreal.enabled", "true");
        config.setProperty("unreal.executable", launcherScript.toString());
        config.setProperty("unreal.signalling_server.script", signallingScript.toString());
        config.setProperty("unreal.target.executable", unrealExe.toString());
        config.setProperty("unreal.target.working_dir", unrealWorkDir.toString());
        config.setProperty("unreal.pid.dir", pidDir.toString());
        config.setProperty("unreal.startup.delay.seconds", "5");
        config.setProperty("unreal.pid.file", pidDir.resolve("unreal.pid").toString());
        config.setProperty(
                "unreal.signalling.pid.file",
                pidDir.resolve("signalling.pid").toString()
        );

        assertDoesNotThrow(() -> AppConfigValidator.validate(config));
    }

    /**
     * Creates a baseline config with optional integrations disabled.
     *
     * @return baseline application configuration
     */
    private Properties baseConfig() {
        Properties config = new Properties();
        config.setProperty("mqtt.topic", "PREDICTION");
        config.setProperty("mqtt.broker.url", "tcp://localhost:1883");
        config.setProperty("mqtt.client.id", "JavaClientPublisherSubscriber");
        config.setProperty("streaming.url", "http://localhost");
        config.setProperty("animation.output.path", "data/animation.json");
        config.setProperty("mqtt_sim.enabled", "false");
        config.setProperty("unreal.enabled", "false");
        return config;
    }
}
