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

import java.time.LocalDate;
import java.util.Objects;

import de.gecheckt.pdf.umbenenner.application.port.out.ProcessingAttempt;

/**
 * Stateless service for building the base target filename from a leading naming proposal.
 * <p>
 * The base filename follows the verbindliches Zielformat:
 * <pre>
 *   YYYY-MM-DD - Titel.pdf
 * </pre>
 *
 * <h2>Input source</h2>
 * <p>
 * The sole authoritative source for date and title is the most recent
 * {@code PROPOSAL_READY} processing attempt. This service reads directly from a
 * {@link ProcessingAttempt} whose
 * {@link de.gecheckt.pdf.umbenenner.domain.model.ProcessingStatus#PROPOSAL_READY}
 * status was confirmed by the caller.
 *
 * <h2>Consistency checks</h2>
 * <p>
 * This service does not silently heal inconsistent persistence states. If the proposal
 * attempt carries a title or date that violates the rules that were enforced during
 * AI response validation, the state is treated as an inconsistent persistence state
 * and the caller receives an {@link InconsistentProposalState} result. Such states
 * must be surfaced as document-level technical errors.
 *
 * <h2>No new fachliche interpretation</h2>
 * <p>
 * This service never re-evaluates or reinterprets the title: it uses the already-validated
 * title from the proposal attempt unchanged.
 */
public final class TargetFilenameBuildingService {

    private TargetFilenameBuildingService() {
        // static utility, no instances
    }

    // -------------------------------------------------------------------------
    // Result type
    // -------------------------------------------------------------------------

    /**
     * Sealed result of {@link #buildBaseFilename(ProcessingAttempt, int)}.
     */
    public sealed interface BaseFilenameResult
            permits BaseFilenameReady, InconsistentProposalState {
    }

    /**
     * Successful result containing the ready base filename.
     *
     * @param baseFilename the filename in {@code YYYY-MM-DD - Titel.pdf} format;
     *                     never null or blank
     */
    public record BaseFilenameReady(String baseFilename) implements BaseFilenameResult {
        public BaseFilenameReady {
            Objects.requireNonNull(baseFilename, "baseFilename must not be null");
            if (baseFilename.isBlank()) {
                throw new IllegalArgumentException("baseFilename must not be blank");
            }
        }
    }

    /**
     * Failure result indicating that the loaded proposal attempt contains data that
     * violates the rules that were applied during naming-proposal validation, making
     * the persistence state inconsistent.
     *
     * @param reason human-readable description of the inconsistency; never null
     */
    public record InconsistentProposalState(String reason) implements BaseFilenameResult {
        public InconsistentProposalState {
            Objects.requireNonNull(reason, "reason must not be null");
        }
    }

    // -------------------------------------------------------------------------
    // Main method
    // -------------------------------------------------------------------------

    /**
     * Builds the base target filename from the resolved date and validated title stored
     * in the given {@code PROPOSAL_READY} attempt.
     * <p>
     * Validation rules applied defensively (already enforced during AI response validation):
     * <ul>
     *   <li>Resolved date must be non-null.</li>
     *   <li>Validated title must be non-null and non-blank.</li>
     *   <li>Validated title must not exceed the configured maximum length
     *       (before Windows cleaning).</li>
     *   <li>After Windows-character cleaning, title must contain only letters, digits, spaces, and hyphens.</li>
     * </ul>
     * If any rule is violated, the state is treated as an
     * {@link InconsistentProposalState}.
     * <p>
     * Windows compatibility: Windows-incompatible characters
     * (e.g., {@code < > : " / \ | ? *}) are removed from the title before final validation.
     * This ensures the resulting filename can be created on Windows systems.
     * The maximum-length rule is applied to the original title before cleaning.
     * <p>
     * The configured maximum length applies exclusively to the base title. A duplicate-avoidance
     * suffix (e.g., {@code (1)}) may be appended by the target folder adapter after this
     * method returns and is not counted against the maximum.
     *
     * @param proposalAttempt the leading {@code PROPOSAL_READY} attempt; must not be null
     * @param maxTitleLength  the configured maximum base title length; must be &gt;= 1
     * @return a {@link BaseFilenameReady} with the complete filename, or an
     *         {@link InconsistentProposalState} describing the consistency violation
     * @throws IllegalArgumentException if {@code maxTitleLength} is less than 1
     */
    public static BaseFilenameResult buildBaseFilename(ProcessingAttempt proposalAttempt, int maxTitleLength) {
        Objects.requireNonNull(proposalAttempt, "proposalAttempt must not be null");
        if (maxTitleLength < 1) {
            throw new IllegalArgumentException(
                    "maxTitleLength must be >= 1, but was: " + maxTitleLength);
        }

        LocalDate date = proposalAttempt.resolvedDate();
        String title = proposalAttempt.validatedTitle();

        if (date == null) {
            return new InconsistentProposalState(
                    "Leading PROPOSAL_READY attempt has no resolved date");
        }

        if (title == null || title.isBlank()) {
            return new InconsistentProposalState(
                    "Leading PROPOSAL_READY attempt has no validated title");
        }

        if (title.length() > maxTitleLength) {
            return new InconsistentProposalState(
                    "Leading PROPOSAL_READY attempt has title exceeding " + maxTitleLength
                            + " characters: '" + title + "'");
        }

        // Remove Windows-incompatible characters to enable technical Windows compatibility
        String cleanedTitle = removeWindowsIncompatibleCharacters(title);

        if (cleanedTitle.isBlank()) {
            return new InconsistentProposalState(
                    "Title becomes empty after Windows-compatibility cleaning: '"
                            + title + "'");
        }

        // After cleaning, verify that only letters, digits, spaces, and hyphens remain
        if (!TitleCharacterRule.isAllowed(cleanedTitle)) {
            return new InconsistentProposalState(
                    "After Windows-compatibility cleaning, title contains disallowed characters "
                            + "(only letters, digits, spaces, and hyphens are permitted): '"
                            + cleanedTitle + "'");
        }

        // Build: YYYY-MM-DD - Titel.pdf
        String baseFilename = date + " - " + cleanedTitle + ".pdf";
        return new BaseFilenameReady(baseFilename);
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    /**
     * Removes characters that are incompatible with Windows filenames.
     * <p>
     * Windows-incompatible characters are: {@code < > : " / \ | ? *}
     * <p>
     * This is a defensive measure for ensuring Windows compatibility. The characters are
     * simply removed; no replacement is performed. Unicode letters (including Umlauts and ß)
     * and spaces are retained.
     *
     * @param title the title to clean; must not be null
     * @return the cleaned title with Windows-incompatible characters removed
     */
    private static String removeWindowsIncompatibleCharacters(String title) {
        // Windows-incompatible characters: < > : " / \ | ? *
        return title.replaceAll("[<>:\"/\\\\|?*]", "");
    }
}
