package de.gecheckt.pdf.umbenenner.application.usecase;

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

import java.time.Instant;
import java.util.Optional;

import org.junit.jupiter.api.Test;

import de.gecheckt.pdf.umbenenner.application.port.out.DocumentKnownProcessable;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentPersistenceException;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecord;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecordLookupResult;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecordRepository;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentTerminalFinalFailure;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentTerminalSuccess;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentUnknown;
import de.gecheckt.pdf.umbenenner.application.port.out.FailureCounters;
import de.gecheckt.pdf.umbenenner.application.port.out.PersistenceLookupTechnicalFailure;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;
import de.gecheckt.pdf.umbenenner.domain.model.ProcessingStatus;
import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentLocator;

/**
 * Unit tests for {@link DefaultResolveHistoricalFileNameUseCase}.
 */
class DefaultResolveHistoricalFileNameUseCaseTest {

    private static final DocumentFingerprint FP = new DocumentFingerprint("a".repeat(64));

    @Test
    void constructor_withNullRepository_throws() {
        assertThatNullPointerException()
                .isThrownBy(() -> new DefaultResolveHistoricalFileNameUseCase(null))
                .withMessageContaining("documentRecordRepository");
    }

    @Test
    void resolveHistoricalFileName_withNullFingerprint_throws() {
        var useCase = new DefaultResolveHistoricalFileNameUseCase(stubRepo(new DocumentUnknown()));
        assertThatNullPointerException()
                .isThrownBy(() -> useCase.resolveHistoricalFileName(null))
                .withMessageContaining("fingerprint");
    }

    @Test
    void resolveHistoricalFileName_forSuccessRecord_returnsLastTargetFileName() {
        DocumentRecord record = buildRecord(ProcessingStatus.SUCCESS, "2026-01-01 - Rechnung.pdf");
        var useCase = new DefaultResolveHistoricalFileNameUseCase(
                stubRepo(new DocumentTerminalSuccess(record)));

        Optional<String> result = useCase.resolveHistoricalFileName(FP);
        assertThat(result).contains("2026-01-01 - Rechnung.pdf");
    }

    @Test
    void resolveHistoricalFileName_forSuccessRecordWithNullLastTargetFileName_returnsEmpty() {
        DocumentRecord record = buildRecord(ProcessingStatus.SUCCESS, null);
        var useCase = new DefaultResolveHistoricalFileNameUseCase(
                stubRepo(new DocumentTerminalSuccess(record)));

        assertThat(useCase.resolveHistoricalFileName(FP)).isEmpty();
    }

    @Test
    void resolveHistoricalFileName_forUnknownDocument_returnsEmpty() {
        var useCase = new DefaultResolveHistoricalFileNameUseCase(stubRepo(new DocumentUnknown()));

        assertThat(useCase.resolveHistoricalFileName(FP)).isEmpty();
    }

    @Test
    void resolveHistoricalFileName_forFinalFailure_returnsEmpty() {
        DocumentRecord record = buildRecord(ProcessingStatus.FAILED_FINAL, null);
        var useCase = new DefaultResolveHistoricalFileNameUseCase(
                stubRepo(new DocumentTerminalFinalFailure(record)));

        assertThat(useCase.resolveHistoricalFileName(FP)).isEmpty();
    }

    @Test
    void resolveHistoricalFileName_forProcessableDocument_returnsEmpty() {
        DocumentRecord record = buildRecord(ProcessingStatus.READY_FOR_AI, null);
        var useCase = new DefaultResolveHistoricalFileNameUseCase(
                stubRepo(new DocumentKnownProcessable(record)));

        assertThat(useCase.resolveHistoricalFileName(FP)).isEmpty();
    }

    @Test
    void resolveHistoricalFileName_forPersistenceLookupFailure_returnsEmpty() {
        var useCase = new DefaultResolveHistoricalFileNameUseCase(
                stubRepo(new PersistenceLookupTechnicalFailure("DB-Fehler", null)));

        assertThat(useCase.resolveHistoricalFileName(FP)).isEmpty();
    }

    @Test
    void resolveHistoricalFileName_whenRepositoryThrows_returnsEmpty() {
        DocumentRecordRepository throwingRepo = new DocumentRecordRepository() {
            @Override
            public DocumentRecordLookupResult findByFingerprint(DocumentFingerprint fingerprint) {
                throw new DocumentPersistenceException("Verbindungsfehler", null);
            }
            @Override
            public void create(DocumentRecord record) {
                // intentionally empty
            }
            @Override
            public void update(DocumentRecord record) {
                // intentionally empty
            }
            @Override
            public void deleteByFingerprint(DocumentFingerprint fingerprint) {
                // intentionally empty
            }
        };

        var useCase = new DefaultResolveHistoricalFileNameUseCase(throwingRepo);

        assertThat(useCase.resolveHistoricalFileName(FP)).isEmpty();
    }

    // -------------------------------------------------------------------------
    // Hilfsmethoden
    // -------------------------------------------------------------------------

    private static DocumentRecordRepository stubRepo(DocumentRecordLookupResult result) {
        return new DocumentRecordRepository() {
            @Override
            public DocumentRecordLookupResult findByFingerprint(DocumentFingerprint fingerprint) {
                return result;
            }
            @Override
            public void create(DocumentRecord record) {
                // intentionally empty
            }
            @Override
            public void update(DocumentRecord record) {
                // intentionally empty
            }
            @Override
            public void deleteByFingerprint(DocumentFingerprint fingerprint) {
                // intentionally empty
            }
        };
    }

    private static DocumentRecord buildRecord(ProcessingStatus status, String lastTargetFileName) {
        return new DocumentRecord(
                FP,
                new SourceDocumentLocator("quell/pfad"),
                "original.pdf",
                status,
                FailureCounters.zero(),
                null,
                status == ProcessingStatus.SUCCESS ? Instant.now() : null,
                Instant.now(),
                Instant.now(),
                lastTargetFileName != null ? "ziel/ordner" : null,
                lastTargetFileName);
    }
}
