package de.gecheckt.pdf.umbenenner.bootstrap.startup;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.io.TempDir;

import de.gecheckt.pdf.umbenenner.bootstrap.startup.EarlyLogDirectoryInitializer.LogDirectorySetupResult;

/**
 * Tests für {@link EarlyLogDirectoryInitializer}.
 *
 * <p>Deckt insbesondere die Regression ab, dass Windows-Pfade mit Backslashes nicht durch
 * die Escape-Interpretation von {@link java.util.Properties} verstümmelt werden, sowie das
 * Best-Effort-Anlegen des Verzeichnisses und den sichtbaren Fallback, wenn das konfigurierte
 * Verzeichnis nicht nutzbar ist.
 *
 * <p>Die System-Property {@code log.directory} ist globaler Prozesszustand; sie wird pro Test
 * gesichert, geleert und anschließend wiederhergestellt.
 */
class EarlyLogDirectoryInitializerTest {

    private static final String KEY = "log.directory";

    private String originalProperty;

    @TempDir
    Path tempDir;

    @BeforeEach
    void clearLogDirectoryProperty() {
        originalProperty = System.getProperty(KEY);
        System.clearProperty(KEY);
    }

    @AfterEach
    void restoreLogDirectoryProperty() {
        if (originalProperty == null) {
            System.clearProperty(KEY);
        } else {
            System.setProperty(KEY, originalProperty);
        }
    }

    /**
     * Regression: Ein Windows-Pfad mit einfachen Backslashes darf beim frühen Einlesen nicht
     * verstümmelt werden; das Verzeichnis muss angelegt und exakt referenziert werden.
     */
    @Test
    @EnabledOnOs(OS.WINDOWS)
    void windowsBackslashPfad_wirdNichtVerstuemmeltUndAngelegt() throws IOException {
        Path expected = tempDir.resolve("winlogs");
        String backslashValue = expected.toString().replace('/', '\\');
        Path config = writeConfig("log.directory=" + backslashValue);

        LogDirectorySetupResult result = EarlyLogDirectoryInitializer.applyFromArgs(
                new String[]{"--config", config.toString()});

        assertFalse(result.fallbackUsed(), "Ein anlegbares Verzeichnis darf keinen Fallback auslösen");
        assertTrue(Files.isDirectory(expected), "Konfiguriertes Log-Verzeichnis muss angelegt worden sein");
        String property = System.getProperty(KEY);
        assertNotNull(property, "System-Property log.directory muss gesetzt sein");
        assertTrue(Files.isSameFile(expected, Paths.get(property)),
                "System-Property muss auf das konfigurierte Verzeichnis zeigen, nicht auf einen verstümmelten Pfad");
    }

    /**
     * Ein noch nicht existentes, aber anlegbares Verzeichnis wird angelegt und als
     * System-Property gesetzt.
     */
    @Test
    void anlegbaresVerzeichnis_wirdAngelegtUndGesetzt() throws IOException {
        Path expected = tempDir.resolve("logs-sub");
        String value = expected.toString().replace('\\', '/');
        Path config = writeConfig("log.directory=" + value);

        LogDirectorySetupResult result = EarlyLogDirectoryInitializer.applyFromArgs(
                new String[]{"--config", config.toString()});

        assertFalse(result.fallbackUsed());
        assertTrue(Files.isDirectory(expected));
        String property = System.getProperty(KEY);
        assertNotNull(property);
        assertTrue(Files.isSameFile(expected, Paths.get(property)));
    }

    /**
     * Ein nicht anlegbares Verzeichnis (Unterverzeichnis unterhalb einer regulären Datei)
     * darf keine System-Property setzen und muss den Fallback signalisieren.
     */
    @Test
    void nichtAnlegbaresVerzeichnis_signalisiertFallbackOhneProperty() throws IOException {
        Path blockingFile = tempDir.resolve("blocker.txt");
        Files.writeString(blockingFile, "x", StandardCharsets.UTF_8);
        String value = blockingFile.toString().replace('\\', '/') + "/sub";
        Path config = writeConfig("log.directory=" + value);

        LogDirectorySetupResult result = EarlyLogDirectoryInitializer.applyFromArgs(
                new String[]{"--config", config.toString()});

        assertTrue(result.fallbackUsed(), "Nicht anlegbares Verzeichnis muss Fallback signalisieren");
        assertNull(System.getProperty(KEY), "Bei Fallback darf keine System-Property gesetzt sein");
    }

    /**
     * Ist {@code log.directory} nicht konfiguriert, bleibt die System-Property ungesetzt und es
     * wird kein Fallback signalisiert.
     */
    @Test
    void keinLogVerzeichnis_setztKeineProperty() throws IOException {
        Path config = writeConfig("source.folder=/tmp");

        LogDirectorySetupResult result = EarlyLogDirectoryInitializer.applyFromArgs(
                new String[]{"--config", config.toString()});

        assertFalse(result.fallbackUsed());
        assertNull(result.appliedDirectory());
        assertNull(System.getProperty(KEY));
    }

    /**
     * Eine bereits gesetzte System-Property wird respektiert und nicht überschrieben.
     */
    @Test
    void bereitsGesetztePropertyWirdRespektiert() {
        System.setProperty(KEY, "C:/vorgegeben/logs");

        LogDirectorySetupResult result = EarlyLogDirectoryInitializer.applyFromArgs(new String[]{});

        assertFalse(result.fallbackUsed());
        assertEquals("C:/vorgegeben/logs", result.appliedDirectory());
        assertEquals("C:/vorgegeben/logs", System.getProperty(KEY));
    }

    /**
     * Zeigt {@code --config} auf eine nicht existente Datei, bleibt die System-Property ungesetzt.
     */
    @Test
    void fehlendeKonfigdatei_setztKeineProperty() {
        LogDirectorySetupResult result = EarlyLogDirectoryInitializer.applyFromArgs(
                new String[]{"--config", tempDir.resolve("does-not-exist.properties").toString()});

        assertFalse(result.fallbackUsed());
        assertNull(System.getProperty(KEY));
    }

    private Path writeConfig(String content) throws IOException {
        Path config = tempDir.resolve("application.properties");
        Files.writeString(config, content + "\n", StandardCharsets.UTF_8);
        return config;
    }
}
