package de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun;

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

import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.junit.jupiter.api.Test;

import de.gecheckt.pdf.umbenenner.application.port.in.DocumentCompletionStatus;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;

/**
 * Unit-Tests für {@link BatchRunSummaryBanner}.
 * <p>
 * Geprüft werden die Aggregationslogik und die Textgenerierung unabhängig von JavaFX.
 * Die GUI-Integrationsmethoden ({@code clear()}, {@code update()}, {@code getNode()})
 * erfordern eine JavaFX-Runtime und werden durch Smoke-Tests abgedeckt.
 */
class BatchRunSummaryBannerTest {

    // -------------------------------------------------------------------------
    // Hilfsmethoden für Testdaten
    // -------------------------------------------------------------------------

    private static GuiBatchRunResultRow row(DocumentCompletionStatus status) {
        return new GuiBatchRunResultRow(
                "test.pdf",
                new DocumentFingerprint("a".repeat(64)),
                status,
                Optional.empty(),
                Optional.empty(),
                Optional.empty(),
                Optional.empty(),
                Optional.empty(),
                Duration.ZERO,
                false,
                Optional.empty());
    }

    private static GuiBatchRunResultRow resetPendingRow() {
        GuiBatchRunResultRow base = row(DocumentCompletionStatus.SUCCESS);
        return GuiBatchRunResultRow.resetMarker(base);
    }

    // -------------------------------------------------------------------------
    // aggregateCounts
    // -------------------------------------------------------------------------

    @Test
    void aggregateCounts_leereListe_alleZaehlerNull() {
        Map<DocumentCompletionStatus, Integer> counts =
                BatchRunSummaryBanner.aggregateCounts(Collections.emptyList());

        assertEquals(0, counts.getOrDefault(DocumentCompletionStatus.SUCCESS, 0));
        assertEquals(0, counts.getOrDefault(DocumentCompletionStatus.FAILED_RETRYABLE, 0));
        assertEquals(0, counts.getOrDefault(DocumentCompletionStatus.FAILED_PERMANENT, 0));
        assertEquals(0, counts.getOrDefault(DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED, 0));
        assertEquals(0, counts.getOrDefault(DocumentCompletionStatus.SKIPPED_FINAL_FAILURE, 0));
    }

    @Test
    void aggregateCounts_nurErfolgreiche_zaehltNurSuccess() {
        List<GuiBatchRunResultRow> rows = List.of(
                row(DocumentCompletionStatus.SUCCESS),
                row(DocumentCompletionStatus.SUCCESS),
                row(DocumentCompletionStatus.SUCCESS));

        Map<DocumentCompletionStatus, Integer> counts =
                BatchRunSummaryBanner.aggregateCounts(rows);

        assertEquals(3, counts.get(DocumentCompletionStatus.SUCCESS));
        assertEquals(0, counts.get(DocumentCompletionStatus.FAILED_RETRYABLE));
        assertEquals(0, counts.get(DocumentCompletionStatus.FAILED_PERMANENT));
        assertEquals(0, counts.get(DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED));
        assertEquals(0, counts.get(DocumentCompletionStatus.SKIPPED_FINAL_FAILURE));
    }

    @Test
    void aggregateCounts_gemischterLauf_alleKategorienKorrekt() {
        List<GuiBatchRunResultRow> rows = List.of(
                row(DocumentCompletionStatus.SUCCESS),
                row(DocumentCompletionStatus.SUCCESS),
                row(DocumentCompletionStatus.FAILED_RETRYABLE),
                row(DocumentCompletionStatus.FAILED_PERMANENT),
                row(DocumentCompletionStatus.FAILED_PERMANENT),
                row(DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED),
                row(DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED),
                row(DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED),
                row(DocumentCompletionStatus.SKIPPED_FINAL_FAILURE));

        Map<DocumentCompletionStatus, Integer> counts =
                BatchRunSummaryBanner.aggregateCounts(rows);

        assertEquals(2, counts.get(DocumentCompletionStatus.SUCCESS));
        assertEquals(1, counts.get(DocumentCompletionStatus.FAILED_RETRYABLE));
        assertEquals(2, counts.get(DocumentCompletionStatus.FAILED_PERMANENT));
        assertEquals(3, counts.get(DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED));
        assertEquals(1, counts.get(DocumentCompletionStatus.SKIPPED_FINAL_FAILURE));
    }

    @Test
    void aggregateCounts_resetPendingZeilenWerdenNichtGezaehlt() {
        // Reset-Pending-Zeilen haben noch keinen abgeschlossenen Status und
        // dürfen nicht ins Summary einfließen
        List<GuiBatchRunResultRow> rows = List.of(
                row(DocumentCompletionStatus.SUCCESS),
                resetPendingRow(),
                resetPendingRow());

        Map<DocumentCompletionStatus, Integer> counts =
                BatchRunSummaryBanner.aggregateCounts(rows);

        assertEquals(1, counts.get(DocumentCompletionStatus.SUCCESS));
        assertEquals(0, counts.get(DocumentCompletionStatus.FAILED_RETRYABLE));
        assertEquals(0, counts.get(DocumentCompletionStatus.FAILED_PERMANENT));
    }

    // -------------------------------------------------------------------------
    // buildBannerText
    // -------------------------------------------------------------------------

    @Test
    void buildBannerText_alleZaehlerNull_leerString() {
        Map<DocumentCompletionStatus, Integer> counts = Map.of(
                DocumentCompletionStatus.SUCCESS, 0,
                DocumentCompletionStatus.FAILED_RETRYABLE, 0,
                DocumentCompletionStatus.FAILED_PERMANENT, 0,
                DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED, 0,
                DocumentCompletionStatus.SKIPPED_FINAL_FAILURE, 0);

        String text = BatchRunSummaryBanner.buildBannerText(counts);

        assertTrue(text.isEmpty(), "Leere Zähler ergeben leeren Text: '" + text + "'");
    }

    @Test
    void buildBannerText_nurErfolgreiche_nurSuccessSegment() {
        Map<DocumentCompletionStatus, Integer> counts = Map.of(
                DocumentCompletionStatus.SUCCESS, 17,
                DocumentCompletionStatus.FAILED_RETRYABLE, 0,
                DocumentCompletionStatus.FAILED_PERMANENT, 0,
                DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED, 0,
                DocumentCompletionStatus.SKIPPED_FINAL_FAILURE, 0);

        String text = BatchRunSummaryBanner.buildBannerText(counts);

        assertTrue(text.contains("17"), "Anzahl 17 muss im Text erscheinen: " + text);
        assertTrue(text.contains("erfolgreich"), "Kategorie 'erfolgreich' muss erscheinen: " + text);
        assertTrue(text.contains("✓"), "Icon ✓ muss erscheinen: " + text);
        assertFalse(text.contains("↻"), "Kein ↻ wenn FAILED_RETRYABLE = 0: " + text);
        assertFalse(text.contains("×"), "Kein × wenn FAILED_PERMANENT = 0: " + text);
        assertFalse(text.contains("≡"), "Kein ≡ wenn SKIPPED_ALREADY_PROCESSED = 0: " + text);
        assertFalse(text.contains("⊘"), "Kein ⊘ wenn SKIPPED_FINAL_FAILURE = 0: " + text);
    }

    @Test
    void buildBannerText_vollerLauf_alleSegmenteEnthalten() {
        Map<DocumentCompletionStatus, Integer> counts = Map.of(
                DocumentCompletionStatus.SUCCESS, 14,
                DocumentCompletionStatus.FAILED_RETRYABLE, 1,
                DocumentCompletionStatus.FAILED_PERMANENT, 2,
                DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED, 3,
                DocumentCompletionStatus.SKIPPED_FINAL_FAILURE, 1);

        String text = BatchRunSummaryBanner.buildBannerText(counts);

        // Jedes Segment enthält Icon + Anzahl + Kategorie
        assertTrue(text.contains("✓ 14 erfolgreich"), "SUCCESS-Segment: " + text);
        assertTrue(text.contains("↻ 1 wird wiederholt"), "FAILED_RETRYABLE-Segment: " + text);
        assertTrue(text.contains("× 2 fehlgeschlagen"), "FAILED_PERMANENT-Segment: " + text);
        assertTrue(text.contains("≡ 3 übersprungen"), "SKIPPED_ALREADY_PROCESSED-Segment: " + text);
        assertTrue(text.contains("⊘ 1 endgültig übersprungen"), "SKIPPED_FINAL_FAILURE-Segment: " + text);
    }

    @Test
    void buildBannerText_nurSkippedFinalFailure_erscheintImBanner() {
        // Sicherstellung: ⊘ erscheint auch wenn > 0, obwohl es die seltenste Kategorie ist
        Map<DocumentCompletionStatus, Integer> counts = Map.of(
                DocumentCompletionStatus.SUCCESS, 0,
                DocumentCompletionStatus.FAILED_RETRYABLE, 0,
                DocumentCompletionStatus.FAILED_PERMANENT, 0,
                DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED, 0,
                DocumentCompletionStatus.SKIPPED_FINAL_FAILURE, 2);

        String text = BatchRunSummaryBanner.buildBannerText(counts);

        assertTrue(text.contains("⊘"), "Icon ⊘ muss erscheinen: " + text);
        assertTrue(text.contains("2"), "Anzahl 2 muss erscheinen: " + text);
        assertTrue(text.contains("endgültig übersprungen"), "Kategorie muss erscheinen: " + text);
    }

    @Test
    void buildBannerText_nurKategorienMitAnzahlGroesserNull_erscheinen() {
        // Nur SUCCESS=5 ist gesetzt; alle anderen 0 → kein anderes Segment
        Map<DocumentCompletionStatus, Integer> counts = Map.of(
                DocumentCompletionStatus.SUCCESS, 5,
                DocumentCompletionStatus.FAILED_RETRYABLE, 0,
                DocumentCompletionStatus.FAILED_PERMANENT, 0,
                DocumentCompletionStatus.SKIPPED_ALREADY_PROCESSED, 0,
                DocumentCompletionStatus.SKIPPED_FINAL_FAILURE, 0);

        String text = BatchRunSummaryBanner.buildBannerText(counts);

        // Kein Trennzeichen (·) darf erscheinen, wenn nur ein Segment vorhanden ist
        assertFalse(text.contains("·"), "Kein Trenner bei einzelnem Segment: " + text);
        assertTrue(text.contains("✓ 5 erfolgreich"), "Nur SUCCESS-Segment: " + text);
    }

    @Test
    void aggregateCounts_kombinationMitResetPending_nurEchtAbgeschlosseneGezaehlt() {
        // 2 SUCCESS + 1 FAILED_PERMANENT + 1 resetPending(SUCCESS) → nur 2+1 gezählt
        List<GuiBatchRunResultRow> rows = List.of(
                row(DocumentCompletionStatus.SUCCESS),
                row(DocumentCompletionStatus.SUCCESS),
                row(DocumentCompletionStatus.FAILED_PERMANENT),
                resetPendingRow());

        Map<DocumentCompletionStatus, Integer> counts =
                BatchRunSummaryBanner.aggregateCounts(rows);

        assertEquals(2, counts.get(DocumentCompletionStatus.SUCCESS));
        assertEquals(1, counts.get(DocumentCompletionStatus.FAILED_PERMANENT));
        // Summe aller gezählten Einträge = 3, nicht 4
        int total = counts.values().stream().mapToInt(Integer::intValue).sum();
        assertEquals(3, total, "Reset-Pending darf nicht mitgezählt werden");
    }
}
