package de.gecheckt.pdf.umbenenner.application.port.out;

import java.util.List;

import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;
import de.gecheckt.pdf.umbenenner.domain.model.ProcessingStatus;

/**
 * Outbound port for writing and reading the processing attempt history
 * (Versuchshistorie).
 * <p>
 * Every historisable processing attempt for an <em>identified</em> document results
 * in exactly one {@link ProcessingAttempt} record written via {@link #save(ProcessingAttempt)}.
 * <p>
 * <strong>Historisation boundary:</strong> Only attempts with a successfully computed
 * {@link de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint} are historised.
 * Failures that occur before the fingerprint is available are <em>not</em> recorded
 * through this port.
 * <p>
 * <strong>Attempt number semantics:</strong>
 * Attempt numbers start at&nbsp;1 per fingerprint and increase monotonically by&nbsp;1
 * for every saved attempt, including skip attempts. The use case calls
 * {@link #loadNextAttemptNumber(DocumentFingerprint)} to obtain the correct sequence
 * number before constructing a {@link ProcessingAttempt}.
 * <p>
 * <strong>Architecture boundary:</strong> No JDBC, SQLite, or filesystem types appear
 * in this interface. Mapping to and from the persistence schema is the exclusive
 * responsibility of the adapter implementation.
 *
 */
public interface ProcessingAttemptRepository {

    /**
     * Returns the attempt number to assign to the <em>next</em> attempt for the given
     * fingerprint.
     * <p>
     * If no prior attempts exist for the fingerprint, returns&nbsp;1.
     * Otherwise returns the current maximum attempt number plus&nbsp;1.
     *
     * @param fingerprint the document identity; must not be null
     * @return the next monotonic attempt number; always &gt;= 1
     * @throws DocumentPersistenceException if the query fails due to a technical error
     */
    int loadNextAttemptNumber(DocumentFingerprint fingerprint);

    /**
     * Persists exactly one processing attempt record.
     * <p>
     * The {@link ProcessingAttempt#attemptNumber()} must have been obtained from
     * {@link #loadNextAttemptNumber(DocumentFingerprint)} in the same run to guarantee
     * monotonic ordering.
     *
     * @param attempt the attempt to persist; must not be null
     * @throws DocumentPersistenceException if the insert fails due to a technical error
     */
    void save(ProcessingAttempt attempt);

    /**
     * Returns all historised attempts for the given fingerprint, ordered by
     * {@link ProcessingAttempt#attemptNumber()} ascending.
     * <p>
     * Returns an empty list if no attempts have been recorded yet.
     * Intended for use in tests and diagnostics; not required on the primary batch path.
     *
     * @param fingerprint the document identity; must not be null
     * @return immutable list of attempts, ordered by attempt number; never null
     * @throws DocumentPersistenceException if the query fails due to a technical error
     */
    List<ProcessingAttempt> findAllByFingerprint(DocumentFingerprint fingerprint);

    /**
     * Returns the most recent attempt with status {@link ProcessingStatus#PROPOSAL_READY}
     * for the given fingerprint, or {@code null} if no such attempt exists.
     * <p>
     * <strong>Leading source for subsequent processing stages:</strong>
     * The most recent {@code PROPOSAL_READY} attempt is the authoritative source for
     * the validated naming proposal (resolved date, date source, validated title, and
     * AI reasoning) consumed by subsequent stages. The document master record does not
     * carry redundant proposal data; this method is the only correct way to retrieve it.
     * <p>
     * If the overall document status is {@code PROPOSAL_READY} but this method returns
     * {@code null}, or if the returned attempt is missing mandatory proposal fields, the
     * state is considered an inconsistent persistence state and must be treated as a
     * document-level technical error — not silently healed.
     *
     * @param fingerprint the document identity; must not be null
     * @return the most recent {@code PROPOSAL_READY} attempt, or {@code null} if none exists
     * @throws DocumentPersistenceException if the query fails due to a technical error
     */
    ProcessingAttempt findLatestProposalReadyAttempt(DocumentFingerprint fingerprint);

    /**
     * Deletes all attempt history entries for the given fingerprint.
     * <p>
     * This operation is idempotent: if no attempts exist for the fingerprint, the method
     * returns without error. A {@link DocumentPersistenceException} is thrown only on
     * technical failures such as database connectivity errors.
     *
     * @param fingerprint the document identity whose attempt records should be removed;
     *                    must not be null
     * @throws DocumentPersistenceException if the delete fails due to a technical error
     */
    void deleteAllByFingerprint(DocumentFingerprint fingerprint);
}
