package de.gecheckt.pdf.umbenenner.application.validation.technicaltest;

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

import org.junit.jupiter.api.Test;

/**
 * Tests für das versiegelte Interface {@link CheckpointResult} und seine Untertypen.
 */
class CheckpointResultTest {

    // --- Success ---

    @Test
    void success_storesCheckpointIdAndMessage() {
        var result = new CheckpointResult.Success(CheckpointId.SOURCE_FOLDER_PRESENT, "Ordner vorhanden");
        assertThat(result.checkpointId()).isEqualTo(CheckpointId.SOURCE_FOLDER_PRESENT);
        assertThat(result.message()).isEqualTo("Ordner vorhanden");
    }

    @Test
    void success_nullCheckpointIdThrows() {
        assertThatNullPointerException()
                .isThrownBy(() -> new CheckpointResult.Success(null, "ok"));
    }

    @Test
    void success_nullMessageThrows() {
        assertThatNullPointerException()
                .isThrownBy(() -> new CheckpointResult.Success(CheckpointId.API_KEY_PRESENT, null));
    }

    @Test
    void success_equality() {
        var a = new CheckpointResult.Success(CheckpointId.PROMPT_FILE_PRESENT, "ok");
        var b = new CheckpointResult.Success(CheckpointId.PROMPT_FILE_PRESENT, "ok");
        assertThat(a).isEqualTo(b);
    }

    // --- Failure ---

    @Test
    void failure_of_noCorrectionSuggestion() {
        var result = CheckpointResult.Failure.of(
                CheckpointId.TARGET_FOLDER_USABLE, CheckpointSeverity.ERROR, "Ordner fehlt");
        assertThat(result.checkpointId()).isEqualTo(CheckpointId.TARGET_FOLDER_USABLE);
        assertThat(result.severity()).isEqualTo(CheckpointSeverity.ERROR);
        assertThat(result.message()).isEqualTo("Ordner fehlt");
        assertThat(result.correctionSuggestion()).isEmpty();
        assertThat(result.hasCorrectionSuggestion()).isFalse();
    }

    @Test
    void failure_withCorrection_storesSuggestion() {
        var suggestion = new CorrectionSuggestion.CreateDirectory("/some/path", "Ordner anlegen");
        var result = CheckpointResult.Failure.withCorrection(
                CheckpointId.TARGET_FOLDER_USABLE, CheckpointSeverity.ERROR, "Ordner fehlt", suggestion);
        assertThat(result.hasCorrectionSuggestion()).isTrue();
        assertThat(result.correctionSuggestion()).contains(suggestion);
    }

    @Test
    void failure_nullCorrectionSuggestionInConstructorBecomesEmpty() {
        var result = new CheckpointResult.Failure(
                CheckpointId.SQLITE_PATH_USABLE, CheckpointSeverity.WARNING, "Pfad auffällig", null);
        assertThat(result.correctionSuggestion()).isEmpty();
    }

    @Test
    void failure_nullCheckpointIdThrows() {
        assertThatNullPointerException()
                .isThrownBy(() -> CheckpointResult.Failure.of(null, CheckpointSeverity.ERROR, "msg"));
    }

    @Test
    void failure_warningLevel() {
        var result = CheckpointResult.Failure.of(
                CheckpointId.SELECTED_MODEL_PLAUSIBLE, CheckpointSeverity.WARNING, "Modell unbekannt");
        assertThat(result.severity()).isEqualTo(CheckpointSeverity.WARNING);
    }

    // --- NotApplicable ---

    @Test
    void notApplicable_storesCheckpointIdAndReason() {
        var result = new CheckpointResult.NotApplicable(
                CheckpointId.API_KEY_ACCEPTED, "Kein API-Key vorhanden");
        assertThat(result.checkpointId()).isEqualTo(CheckpointId.API_KEY_ACCEPTED);
        assertThat(result.reason()).isEqualTo("Kein API-Key vorhanden");
    }

    @Test
    void notApplicable_nullCheckpointIdThrows() {
        assertThatNullPointerException()
                .isThrownBy(() -> new CheckpointResult.NotApplicable(null, "reason"));
    }

    @Test
    void notApplicable_nullReasonThrows() {
        assertThatNullPointerException()
                .isThrownBy(() -> new CheckpointResult.NotApplicable(CheckpointId.MODEL_LIST_AVAILABLE, null));
    }

    // --- Pattern Matching ---

    @Test
    void patternMatching_coversAllPermittedTypes() {
        CheckpointResult success = new CheckpointResult.Success(CheckpointId.CONFIGURATION_BASIC_VALIDATION, "ok");
        CheckpointResult failure = CheckpointResult.Failure.of(CheckpointId.BASE_URL_REACHABLE, CheckpointSeverity.ERROR, "nicht erreichbar");
        CheckpointResult notApplicable = new CheckpointResult.NotApplicable(CheckpointId.API_KEY_ACCEPTED, "kein key");

        assertThat(classify(success)).isEqualTo("success");
        assertThat(classify(failure)).isEqualTo("failure");
        assertThat(classify(notApplicable)).isEqualTo("notApplicable");
    }

    private String classify(CheckpointResult result) {
        return switch (result) {
            case CheckpointResult.Success s -> "success";
            case CheckpointResult.Failure f -> "failure";
            case CheckpointResult.NotApplicable n -> "notApplicable";
        };
    }
}
