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

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult;
import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusUseCase;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentPersistenceException;
import de.gecheckt.pdf.umbenenner.application.port.out.ProcessingLogger;
import de.gecheckt.pdf.umbenenner.application.port.out.UnitOfWorkPort;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;

/**
 * Default implementation of {@link ResetDocumentStatusUseCase}.
 * <p>
 * For each requested fingerprint, this implementation deletes the document master
 * record and all associated attempt history in a single atomic transaction via
 * {@link UnitOfWorkPort}. Deletion order honours the foreign-key constraint:
 * attempt rows are removed before the master record.
 * <p>
 * The operation applies best-effort semantics: every fingerprint is attempted
 * independently. A technical failure for one fingerprint is caught, logged, and
 * recorded in the result's failure map; the remaining fingerprints continue to be
 * processed. The batch never aborts early.
 */
public class DefaultResetDocumentStatusUseCase implements ResetDocumentStatusUseCase {

    private final UnitOfWorkPort unitOfWorkPort;
    private final ProcessingLogger logger;

    /**
     * Creates the use case with the required persistence port and logger.
     *
     * @param unitOfWorkPort port for executing the delete operations atomically;
     *                       must not be null
     * @param logger         for operation-level logging; must not be null
     * @throws NullPointerException if any parameter is null
     */
    public DefaultResetDocumentStatusUseCase(
            UnitOfWorkPort unitOfWorkPort,
            ProcessingLogger logger) {
        this.unitOfWorkPort = Objects.requireNonNull(unitOfWorkPort, "unitOfWorkPort must not be null");
        this.logger = Objects.requireNonNull(logger, "logger must not be null");
    }

    /**
     * Resets the processing status for the supplied set of document fingerprints.
     * <p>
     * Each fingerprint is processed independently. Technical failures for individual
     * fingerprints are caught, logged at error level, and recorded in the result;
     * they do not abort processing of the remaining fingerprints.
     *
     * @param fingerprints the set of document fingerprints to reset; must not be null;
     *                     may be empty
     * @return a {@link ResetDocumentStatusResult} describing the full outcome; never null
     * @throws NullPointerException if {@code fingerprints} is null
     */
    @Override
    public ResetDocumentStatusResult reset(Set<DocumentFingerprint> fingerprints) {
        Objects.requireNonNull(fingerprints, "fingerprints must not be null");

        int requestedCount = fingerprints.size();
        Set<DocumentFingerprint> successfullyReset = new HashSet<>();
        Map<DocumentFingerprint, String> failures = new HashMap<>();

        for (DocumentFingerprint fingerprint : fingerprints) {
            try {
                unitOfWorkPort.executeInTransaction(
                        tx -> tx.resetDocumentByFingerprint(fingerprint));
                successfullyReset.add(fingerprint);
                logger.info("Document status reset for fingerprint: {}", fingerprint.sha256Hex());
            } catch (DocumentPersistenceException e) {
                String errorMessage = e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName();
                failures.put(fingerprint, errorMessage);
                logger.error("Failed to reset document status for fingerprint {}: {}",
                        fingerprint.sha256Hex(), errorMessage, e);
            } catch (RuntimeException e) {
                String errorMessage = e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName();
                failures.put(fingerprint, errorMessage);
                logger.error("Unexpected error resetting document status for fingerprint {}: {}",
                        fingerprint.sha256Hex(), errorMessage, e);
            }
        }

        logger.info("Status-Reset abgeschlossen: {} angefordert, {} erfolgreich, {} fehlgeschlagen.",
                requestedCount, successfullyReset.size(), failures.size());

        return new ResetDocumentStatusResult(requestedCount, successfullyReset, failures);
    }
}
