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

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

/**
 * Outbound port for reading and writing the document master record (Dokument-Stammsatz).
 * <p>
 * One master record exists per unique {@link DocumentFingerprint}. The repository is
 * responsible for the persistence of {@link DocumentRecord} values; it holds no
 * business logic about retry rules, skip decisions, or status transitions.
 * <p>
 * <strong>Lookup semantics:</strong>
 * {@link #findByFingerprint(DocumentFingerprint)} returns a sealed
 * {@link DocumentRecordLookupResult} that allows the use case to distinguish exhaustively
 * between an unknown document, a known processable document, a terminal success, a
 * terminal final failure, and a technical persistence failure — without additional
 * assumptions or null checks.
 * <p>
 * <strong>Write semantics:</strong>
 * <ul>
 *   <li>{@link #create(DocumentRecord)} inserts a new record for a previously unknown
 *       document.</li>
 *   <li>{@link #update(DocumentRecord)} replaces the mutable fields of an existing
 *       record identified by its fingerprint.</li>
 * </ul>
 * Both write methods throw {@link DocumentPersistenceException} on technical failure.
 * <p>
 * <strong>Architecture boundary:</strong> No JDBC, SQLite, or filesystem types appear
 * in this interface or in any type it references. Mapping to and from the persistence
 * schema is the exclusive responsibility of the adapter implementation.
 *
 */
public interface DocumentRecordRepository {

    /**
     * Looks up the master record for the given fingerprint.
     * <p>
     * Returns a {@link DocumentRecordLookupResult} that encodes all possible outcomes
     * including technical failures; this method never throws.
     *
     * @param fingerprint the content-based document identity to look up; must not be null
     * @return {@link DocumentUnknown} if no record exists,
     *         {@link DocumentKnownProcessable} if the document is known but not terminal,
     *         {@link DocumentTerminalSuccess} if the document succeeded,
     *         {@link DocumentTerminalFinalFailure} if the document finally failed, or
     *         {@link PersistenceLookupTechnicalFailure} if the lookup itself failed
     */
    DocumentRecordLookupResult findByFingerprint(DocumentFingerprint fingerprint);

    /**
     * Persists a new master record for a previously unknown document.
     * <p>
     * The fingerprint within {@code record} must not yet exist in the persistence store.
     *
     * @param record the new master record to persist; must not be null
     * @throws DocumentPersistenceException if the insert fails due to a technical error
     */
    void create(DocumentRecord record);

    /**
     * Updates the mutable fields of an existing master record.
     * <p>
     * The record is identified by its {@link DocumentFingerprint}; the fingerprint
     * itself is never changed. Mutable fields include the overall status, failure
     * counters, last known source location, and all timestamp fields.
     *
     * @param record the updated master record; must not be null; fingerprint must exist
     * @throws DocumentPersistenceException if the update fails due to a technical error
     */
    void update(DocumentRecord record);

    /**
     * Deletes the master record for the given fingerprint.
     * <p>
     * This operation is idempotent: if no record exists 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 master record should be removed;
     *                    must not be null
     * @throws DocumentPersistenceException if the delete fails due to a technical error
     */
    void deleteByFingerprint(DocumentFingerprint fingerprint);
}
