package de.gecheckt.pdf.umbenenner.application.port.out;

import java.nio.file.Path;
import java.util.Objects;

/**
 * Outbound-Port zum Anlegen und Initialisieren einer neuen, leeren SQLite-Datenbankdatei
 * gegen eine bereits vom Aufrufer reservierte temporäre Zieldatei.
 * <p>
 * Der Aufrufer (Use-Case) verantwortet die Lebensdauer der temporären Datei: er wählt den
 * Pfad, übergibt ihn an diesen Port und führt nach Erfolg den atomaren Move auf den
 * endgültigen Zieldateipfad selbst aus. Der Adapter beschränkt sich strikt auf:
 * <ol>
 *   <li>Anlage und Migration der temporären SQLite-Datei auf den neuesten Schema-Stand
 *       (z. B. via Flyway {@code migrate()});</li>
 *   <li>technischer Verbindungstest gegen die migrierte Datei (Verbindung öffnen,
 *       Flyway-History prüfen, einfache Leseabfrage gegen Schema-Metadaten);</li>
 *   <li>Aufräumen der temporären Datei im Fehlerfall.</li>
 * </ol>
 * <p>
 * <strong>Architekturgrenze:</strong> Provider- und SQLite-spezifische Details
 * (JDBC-URL-Schema, DataSource-Konfiguration, Flyway-Konfiguration) bleiben
 * ausschließlich im Adapter. Der Port arbeitet mit einem opaken {@link Path} und gibt
 * ein versiegeltes Ergebnis zurück.
 */
public interface DatabaseCreationPort {

    /**
     * Erstellt eine neue, leere SQLite-Datenbankdatei am übergebenen temporären
     * Zielpfad und führt eine vollständige Schema-Migration auf den neuesten Stand aus.
     * <p>
     * Bei Fehlern in einem der Teilschritte (Anlage, Migration, Verbindungstest) wird
     * die temporäre Datei zuverlässig wieder entfernt; aufrufende Komponenten müssen
     * diesen Aufräumschritt nicht selbst durchführen.
     *
     * @param tempFile Pfad der zu erstellenden temporären SQLite-Datei; darf nicht
     *                 {@code null} sein. Die Datei darf vor dem Aufruf noch nicht
     *                 existieren; das Elternverzeichnis muss existieren und schreibbar
     *                 sein.
     * @return ein versiegeltes Ergebnis: {@link DatabaseCreationResult.Success} bei Erfolg
     *         oder {@link DatabaseCreationResult.Failure} mit Fehlerklasse und Meldung
     *         im Fehlerfall; nie {@code null}.
     * @throws NullPointerException wenn {@code tempFile} {@code null} ist
     */
    DatabaseCreationResult createAndInitialize(Path tempFile);

    /**
     * Versiegeltes Ergebnis-Interface für {@link DatabaseCreationPort#createAndInitialize(Path)}.
     */
    sealed interface DatabaseCreationResult
            permits DatabaseCreationResult.Success, DatabaseCreationResult.Failure {

        /**
         * Erfolgsergebnis. Die temporäre Datei wurde erfolgreich erstellt, migriert
         * und durch den Verbindungstest verifiziert. Der Aufrufer kann sie nun atomar
         * an den endgültigen Zielpfad verschieben.
         *
         * @param tempFile der temporäre, erfolgreich migrierte Pfad; nie {@code null}
         */
        record Success(Path tempFile) implements DatabaseCreationResult {

            /**
             * Konstruktor mit Pflichtprüfung.
             *
             * @param tempFile der temporäre, erfolgreich migrierte Pfad; darf nicht
             *                 {@code null} sein
             * @throws NullPointerException wenn {@code tempFile} {@code null} ist
             */
            public Success {
                Objects.requireNonNull(tempFile, "tempFile darf nicht null sein");
            }
        }

        /**
         * Fehlerergebnis. Die temporäre Datei wurde – falls bereits angelegt – wieder
         * entfernt; die aktive DB der Anwendung wurde nicht angetastet.
         *
         * @param phase   die Phase, in der der Fehler auftrat; nie {@code null}
         * @param message kurze, deutsche Fehlerbeschreibung; nie {@code null}
         * @param cause   ursächliche Ausnahme; kann {@code null} sein
         */
        record Failure(Phase phase, String message, Throwable cause) implements DatabaseCreationResult {

            /**
             * Konstruktor mit Pflichtprüfung der nicht-nullbaren Felder.
             *
             * @param phase   die Phase, in der der Fehler auftrat; darf nicht {@code null} sein
             * @param message kurze, deutsche Fehlerbeschreibung; darf nicht {@code null} sein
             * @param cause   ursächliche Ausnahme; kann {@code null} sein
             * @throws NullPointerException wenn {@code phase} oder {@code message} {@code null} ist
             */
            public Failure {
                Objects.requireNonNull(phase, "phase darf nicht null sein");
                Objects.requireNonNull(message, "message darf nicht null sein");
            }
        }

        /**
         * Phase der Erstellung einer neuen Datenbank, in der ein Fehler auftrat.
         */
        enum Phase {
            /** Die temporäre Datei konnte nicht erzeugt oder beschrieben werden. */
            FILE_CREATION,
            /** Die Schema-Migration (Flyway) ist fehlgeschlagen. */
            SCHEMA_MIGRATION,
            /** Der nachgelagerte Verbindungstest ist fehlgeschlagen. */
            CONNECTION_TEST
        }
    }
}
