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

import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import de.gecheckt.pdf.umbenenner.application.port.out.ActiveDatabaseContextPort;

/**
 * Bootstrap-interne Implementierung des {@link ActiveDatabaseContextPort}.
 * <p>
 * Hält einen prozessweiten, threadsicheren Override-Pfad, der in der Bootstrap-Schicht
 * Vorrang vor dem in der {@code .properties}-Datei konfigurierten SQLite-Pfad hat.
 * Solange kein Override gesetzt ist, verhält sich die Anwendung wie bisher: alle
 * SQLite-Adapter werden mit der aus der jeweils geladenen Konfigurationsdatei
 * abgeleiteten JDBC-URL verdrahtet.
 * <p>
 * Setzt der Use-Case
 * {@link de.gecheckt.pdf.umbenenner.application.usecase.DefaultCreateNewDatabaseUseCase}
 * den Override über {@link #switchActiveDatabase(Path)}, verwenden alle nachfolgenden
 * GUI-DB-Operationen (History, Reset, Löschen, Verarbeitungsläufe) die hinterlegte
 * Datei – auch wenn der Benutzer die Konfigurationsdatei noch nicht gespeichert hat.
 *
 * <h2>Lebensdauer</h2>
 * <p>Die gehaltene Referenz lebt für die Dauer des laufenden GUI-Prozesses. Sie ist
 * nicht persistent: nach einem Neustart greift wieder der Wert aus der
 * Konfigurationsdatei.
 *
 * <h2>Threading</h2>
 * <p>Alle Lese- und Schreibzugriffe gehen über eine {@link AtomicReference}; die Klasse
 * ist daher uneingeschränkt threadsicher.
 */
public class SqliteActiveDatabaseContextAdapter implements ActiveDatabaseContextPort {

    private static final Logger LOG = LogManager.getLogger(SqliteActiveDatabaseContextAdapter.class);

    private final AtomicReference<Path> override = new AtomicReference<>();

    /**
     * Standardkonstruktor.
     */
    public SqliteActiveDatabaseContextAdapter() {
        // kein Zustand außer der AtomicReference
    }

    /**
     * Setzt den Override-Pfad auf die übergebene Datei. Nachfolgende DB-Operationen
     * verwenden diese Datei, sofern sie {@link #activeDatabaseOverride()} abfragen,
     * statt direkt die Konfigurationsdatei zu konsultieren.
     *
     * @param newDbFile absoluter Pfad der neuen aktiven Datenbankdatei; darf nicht
     *                  {@code null} sein
     * @throws NullPointerException wenn {@code newDbFile} {@code null} ist
     */
    @Override
    public void switchActiveDatabase(Path newDbFile) {
        Objects.requireNonNull(newDbFile, "newDbFile darf nicht null sein");
        Path absolute = newDbFile.toAbsolutePath().normalize();
        Path previous = override.getAndSet(absolute);
        if (previous == null) {
            LOG.info("Aktive SQLite-Datenbank wurde umgestellt auf: {}", absolute);
        } else {
            LOG.info("Aktive SQLite-Datenbank wurde von {} auf {} umgestellt.",
                    previous, absolute);
        }
    }

    /**
     * Liefert den aktuell gesetzten Override, falls vorhanden.
     *
     * @return der Override-Pfad oder {@link Optional#empty()}
     */
    @Override
    public Optional<Path> activeDatabaseOverride() {
        return Optional.ofNullable(override.get());
    }

    /**
     * Entfernt den Override (für Tests oder kontrolliertes Zurücksetzen). Nach dem
     * Aufruf greift wieder der Wert aus der konfigurierten {@code .properties}-Datei.
     */
    public void clearOverride() {
        Path previous = override.getAndSet(null);
        if (previous != null) {
            LOG.info("Aktiver SQLite-Datenbank-Override wurde entfernt (zuletzt: {}).", previous);
        }
    }
}
