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

import java.time.Instant;
import java.util.Objects;

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

/**
 * Application-facing representation of the document master record (Dokument-Stammsatz).
 * <p>
 * One {@code DocumentRecord} exists per unique {@link DocumentFingerprint}. It carries
 * the current overall status, failure counters, and the most recently known source
 * location of the document.
 * <p>
 * <strong>Architecture boundary:</strong> This type contains no SQLite or JDBC types.
 * Mapping between {@code DocumentRecord} and the persistence layer is performed
 * exclusively by the repository adapter in {@code adapter-out}.
 * <p>
 * <strong>Field semantics:</strong>
 * <ul>
 *   <li>{@link #fingerprint()} — primary identity; never changes for a given record.</li>
 *   <li>{@link #lastKnownSourceLocator()} — opaque locator used by adapters; the
 *       application passes it through without interpreting the value.</li>
 *   <li>{@link #lastKnownSourceFileName()} — human-readable file name for logging and
 *       diagnostics; not used for identity.</li>
 *   <li>{@link #overallStatus()} — the current terminal or active status of the document
 *       across all runs. See {@link ProcessingStatus} for semantics.</li>
 *   <li>{@link #failureCounters()} — independent counters for content and transient errors;
 *       never increased by skip events.</li>
 *   <li>{@link #lastFailureInstant()} — timestamp of the most recent failure; {@code null}
 *       if no failure has been recorded yet.</li>
 *   <li>{@link #lastSuccessInstant()} — timestamp of the successful processing; {@code null}
 *       if the document has never been processed successfully.</li>
 *   <li>{@link #createdAt()} — timestamp when this master record was first created.</li>
 *   <li>{@link #updatedAt()} — timestamp of the most recent update to this master record.</li>
 * </ul>
 * <p>
 * <strong>Target location fields:</strong> {@link #lastTargetPath()} and
 * {@link #lastTargetFileName()} are populated only after the document reaches
 * {@link de.gecheckt.pdf.umbenenner.domain.model.ProcessingStatus#SUCCESS}. Both
 * fields are {@code null} for documents that have not yet been successfully copied
 * to the target folder.
 *
 * @param fingerprint             content-based identity; never null
 * @param lastKnownSourceLocator  opaque locator to the physical source file; never null
 * @param lastKnownSourceFileName file name at the time of the last known access; never null or blank
 * @param overallStatus           current processing status; never null
 * @param failureCounters         counters for content and transient errors; never null
 * @param lastFailureInstant      timestamp of the most recent failure, or {@code null}
 * @param lastSuccessInstant      timestamp of the successful processing, or {@code null}
 * @param createdAt               timestamp when this record was first created; never null
 * @param updatedAt               timestamp of the most recent update; never null
 * @param lastTargetPath          opaque locator of the target folder where the last
 *                                successful copy was written, or {@code null}
 * @param lastTargetFileName      filename of the last successfully written target copy
 *                                (including any duplicate suffix), or {@code null}
 */
public record DocumentRecord(
        DocumentFingerprint fingerprint,
        SourceDocumentLocator lastKnownSourceLocator,
        String lastKnownSourceFileName,
        ProcessingStatus overallStatus,
        FailureCounters failureCounters,
        Instant lastFailureInstant,
        Instant lastSuccessInstant,
        Instant createdAt,
        Instant updatedAt,
        String lastTargetPath,
        String lastTargetFileName) {

    /**
     * Compact constructor validating mandatory non-null fields.
     *
     * @throws NullPointerException     if any mandatory field is null
     * @throws IllegalArgumentException if {@code lastKnownSourceFileName} is blank
     */
    public DocumentRecord {
        Objects.requireNonNull(fingerprint, "fingerprint must not be null");
        Objects.requireNonNull(lastKnownSourceLocator, "lastKnownSourceLocator must not be null");
        Objects.requireNonNull(lastKnownSourceFileName, "lastKnownSourceFileName must not be null");
        if (lastKnownSourceFileName.isBlank()) {
            throw new IllegalArgumentException("lastKnownSourceFileName must not be blank");
        }
        Objects.requireNonNull(overallStatus, "overallStatus must not be null");
        Objects.requireNonNull(failureCounters, "failureCounters must not be null");
        Objects.requireNonNull(createdAt, "createdAt must not be null");
        Objects.requireNonNull(updatedAt, "updatedAt must not be null");
    }
}
