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

import java.util.Objects;
import java.util.Optional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import de.gecheckt.pdf.umbenenner.application.port.in.HistoricalDocumentContext;
import de.gecheckt.pdf.umbenenner.application.port.in.ResolveHistoricalDocumentContextUseCase;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentPersistenceException;
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.domain.model.DocumentFingerprint;

/**
 * Standardimplementierung von {@link ResolveHistoricalDocumentContextUseCase}.
 * <p>
 * Fragt den {@link DocumentRecordRepository} nach dem Stammsatz des angegebenen
 * Fingerprints ab. Abhängig vom terminalen Zustand des Dokuments wird ein passender
 * {@link HistoricalDocumentContext} zurückgegeben:
 * <ul>
 *   <li>Bei früherem Erfolgsstatus: Zieldateiname und Erfolgszeitpunkt aus dem
 *       Stammsatz ({@code lastTargetFileName}, {@code lastSuccessInstant}).</li>
 *   <li>Bei endgültigem Fehlschlag: Fehlzeitpunkt aus dem Stammsatz
 *       ({@code lastFailureInstant}).</li>
 *   <li>In allen anderen Fällen (unbekannt, verarbeitbar) sowie bei erwarteten technischen
 *       Abfragefehlern: leeres {@link Optional}.</li>
 * </ul>
 * {@link DocumentPersistenceException}s aus dem Repository werden geloggt (WARN) und
 * führen zu einem leeren {@link Optional}. Andere unerwartete Laufzeitfehler
 * propagieren zum Aufrufer. Erwartete Lookup-Fehler werden als
 * {@code PersistenceLookupTechnicalFailure} im Rückgabewert kodiert und führen
 * ebenfalls zu einem leeren {@link Optional}.
 */
public class DefaultResolveHistoricalDocumentContextUseCase
        implements ResolveHistoricalDocumentContextUseCase {

    private static final Logger logger =
            LogManager.getLogger(DefaultResolveHistoricalDocumentContextUseCase.class);

    private final DocumentRecordRepository documentRecordRepository;

    /**
     * Erstellt den Use Case mit dem erforderlichen Dokument-Stammsatz-Repository.
     *
     * @param documentRecordRepository Repository zum Lesen von Dokument-Stammsätzen;
     *                                 darf nicht {@code null} sein
     * @throws NullPointerException wenn {@code documentRecordRepository} {@code null} ist
     */
    public DefaultResolveHistoricalDocumentContextUseCase(
            DocumentRecordRepository documentRecordRepository) {
        this.documentRecordRepository = Objects.requireNonNull(
                documentRecordRepository, "documentRecordRepository must not be null");
    }

    /**
     * Gibt den historischen Verarbeitungskontext für das durch den Fingerprint
     * identifizierte Dokument zurück.
     * <p>
     * Für Dokumente mit früherem Erfolgsstatus enthält der Kontext Zieldateiname und
     * Erfolgszeitpunkt. Für endgültig fehlgeschlagene Dokumente ist der Fehlzeitpunkt
     * belegt. Für alle anderen Zustände oder bei Abfragefehlern wird ein leeres
     * {@link Optional} zurückgegeben.
     *
     * @param fingerprint inhaltsbasierte Dokumentenidentität; darf nicht {@code null} sein
     * @return historischer Kontext des Dokuments, oder leer wenn nicht verfügbar
     * @throws NullPointerException wenn {@code fingerprint} {@code null} ist
     */
    @Override
    public Optional<HistoricalDocumentContext> resolveHistoricalDocumentContext(
            DocumentFingerprint fingerprint) {
        Objects.requireNonNull(fingerprint, "fingerprint must not be null");
        try {
            DocumentRecordLookupResult result =
                    documentRecordRepository.findByFingerprint(fingerprint);
            if (result instanceof DocumentTerminalSuccess success) {
                return Optional.of(HistoricalDocumentContext.ofSuccess(
                        success.record().lastTargetFileName(),
                        success.record().lastSuccessInstant()));
            }
            if (result instanceof DocumentTerminalFinalFailure failure) {
                return Optional.of(HistoricalDocumentContext.ofFinalFailure(
                        failure.record().lastFailureInstant()));
            }
            return Optional.empty();
        } catch (DocumentPersistenceException e) {
            logger.warn("Persistenzfehler beim Lesen des Dokument-Stammsatzes für Fingerprint {}: {}",
                    fingerprint, e.getMessage(), e);
            return Optional.empty();
        }
    }
}
