package de.gecheckt.pdf.umbenenner.adapter.out.sqlite;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

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

import de.gecheckt.pdf.umbenenner.application.port.out.DatabaseCreationPort;
import de.gecheckt.pdf.umbenenner.application.port.out.DatabaseCreationPort.DatabaseCreationResult;

/**
 * Tests für {@link SqliteDatabaseCreationAdapter}.
 * <p>
 * Prüft, dass eine neue, leere SQLite-Datei am übergebenen Temp-Pfad angelegt und
 * vollständig per Flyway migriert wird, dass der Verbindungstest die Flyway-History
 * verifiziert und dass Fehler im Verlauf zur Bereinigung der Temp-Datei führen.
 */
class SqliteDatabaseCreationAdapterTest {

    @Test
    void createAndInitialize_shouldRejectNullPath() {
        SqliteDatabaseCreationAdapter adapter = new SqliteDatabaseCreationAdapter();
        assertThatThrownBy(() -> adapter.createAndInitialize(null))
                .isInstanceOf(NullPointerException.class);
    }

    @Test
    void createAndInitialize_shouldCreateAndMigrateNewSqliteFile(@TempDir Path tempDir) throws Exception {
        Path tempFile = tempDir.resolve("new-db.sqlite.tmp");
        SqliteDatabaseCreationAdapter adapter = new SqliteDatabaseCreationAdapter();

        DatabaseCreationResult result = adapter.createAndInitialize(tempFile);

        assertThat(result).isInstanceOf(DatabaseCreationResult.Success.class);
        assertThat(Files.exists(tempFile)).isTrue();
        assertThat(Files.size(tempFile)).isGreaterThan(0);

        // Schema verifizieren: Flyway-History und fachliche Tabellen müssen existieren
        String jdbcUrl = "jdbc:sqlite:" + tempFile.toAbsolutePath().toString().replace('\\', '/');
        try (Connection conn = DriverManager.getConnection(jdbcUrl);
             Statement stmt = conn.createStatement()) {
            try (ResultSet rs = stmt.executeQuery(
                    "SELECT count(*) FROM sqlite_master WHERE type='table' "
                            + "AND name IN ('flyway_schema_history','document_record','processing_attempt')")) {
                assertThat(rs.next()).isTrue();
                assertThat(rs.getInt(1)).isEqualTo(3);
            }
            try (ResultSet rs = stmt.executeQuery(
                    "SELECT count(*) FROM flyway_schema_history WHERE success = 1")) {
                assertThat(rs.next()).isTrue();
                assertThat(rs.getInt(1)).isGreaterThanOrEqualTo(1);
            }
        }
    }

    @Test
    void createAndInitialize_shouldOverwriteExistingTempFileBeforeMigration(@TempDir Path tempDir) throws Exception {
        Path tempFile = tempDir.resolve("existing.tmp");
        Files.writeString(tempFile, "rest-zustand");

        SqliteDatabaseCreationAdapter adapter = new SqliteDatabaseCreationAdapter();
        DatabaseCreationResult result = adapter.createAndInitialize(tempFile);

        assertThat(result).isInstanceOf(DatabaseCreationResult.Success.class);
        // Die Datei wurde durch eine leere SQLite-Datei ersetzt — der ursprüngliche Inhalt darf nicht mehr
        // sichtbar sein.
        assertThat(Files.size(tempFile)).isGreaterThan(0);
        assertThat(Files.readString(tempFile, java.nio.charset.StandardCharsets.ISO_8859_1))
                .doesNotContain("rest-zustand");
    }

    @Test
    void createAndInitialize_shouldFailAndCleanup_whenParentDirectoryDoesNotExist(@TempDir Path tempDir)
            throws SQLException {
        Path missingParent = tempDir.resolve("does-not-exist").resolve("child.tmp");

        SqliteDatabaseCreationAdapter adapter = new SqliteDatabaseCreationAdapter();
        DatabaseCreationResult result = adapter.createAndInitialize(missingParent);

        assertThat(result).isInstanceOf(DatabaseCreationResult.Failure.class);
        DatabaseCreationResult.Failure failure = (DatabaseCreationResult.Failure) result;
        assertThat(failure.phase())
                .isIn(DatabaseCreationPort.DatabaseCreationResult.Phase.SCHEMA_MIGRATION,
                        DatabaseCreationPort.DatabaseCreationResult.Phase.CONNECTION_TEST,
                        DatabaseCreationPort.DatabaseCreationResult.Phase.FILE_CREATION);
        assertThat(Files.exists(missingParent)).isFalse();
    }
}
