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

import java.util.Objects;

import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
import de.gecheckt.pdf.umbenenner.domain.model.PdfExtractionSuccess;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckPassed;
import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentCandidate;

/**
 * Evaluates whether a successfully extracted PDF passes pre-checks.
 * <p>
 * Pre-checks verify that:
 * <ul>
 *   <li>The extracted text contains at least one meaningful character after normalization</li>
 *   <li>The document's page count does not exceed the configured limit</li>
 * </ul>
 * <p>
 * A document that passes both pre-checks is ready to proceed to further processing steps.
 * A document that fails a pre-check is classified with a specific deterministic failure reason
 * and will not proceed further in the current batch run.
 * <p>
 * This service is stateless and thread-safe.
 */
public class PreCheckEvaluator {

    /**
     * Evaluates pre-checks for a successfully extracted PDF document.
     * <p>
     * Pre-check logic:
     * <ol>
     *   <li>Check if extracted text contains at least one letter or digit after normalization</li>
     *   <li>Check if document page count does not exceed the configured limit</li>
     * </ol>
     * <p>
     * Returns {@link PreCheckPassed} if both checks pass, or {@link PreCheckFailed}
     * with a specific reason if any check fails.
     * <p>
     * Note: Returns {@link DocumentProcessingOutcome} to integrate cleanly with the complete
     * document processing pipeline.
     *
     * @param candidate the source document metadata
     * @param extraction the successfully extracted PDF content
     * @param runtimeConfig the runtime configuration (used for maxPages limit)
     * @return the pre-check outcome: passed or failed with reason (both implement {@link DocumentProcessingOutcome})
     * @throws NullPointerException if any parameter is null
     */
    public static DocumentProcessingOutcome evaluate(
            SourceDocumentCandidate candidate,
            PdfExtractionSuccess extraction,
            RuntimeConfiguration runtimeConfig) {

        Objects.requireNonNull(candidate, "candidate must not be null");
        Objects.requireNonNull(extraction, "extraction must not be null");
        Objects.requireNonNull(runtimeConfig, "runtimeConfig must not be null");

        // Pre-check 1: Verify document has usable text
        if (!hasUsableText(extraction.extractedText())) {
            return new PreCheckFailed(
                    candidate,
                    PreCheckFailureReason.NO_USABLE_TEXT
            );
        }

        // Pre-check 2: Verify document page count does not exceed configured limit
        if (extraction.pageCount().exceedsLimit(runtimeConfig.maxPages())) {
            return new PreCheckFailed(
                    candidate,
                    PreCheckFailureReason.PAGE_LIMIT_EXCEEDED
            );
        }

        // All pre-checks passed
        return new PreCheckPassed(candidate, extraction);
    }

    /**
     * Determines whether the extracted text contains at least one meaningful character.
     * <p>
     * Definition of "usable text":
     * <ul>
     *   <li>After normalization (trimming whitespace), at least one letter or digit remains</li>
     *   <li>Pure whitespace or only special characters do not qualify as usable text</li>
     *   <li>Letters and digits include Unicode characters (e.g., ÄÖÜß, äöüß, etc.)</li>
     * </ul>
     * <p>
     * Normalization process:
     * <ol>
     *   <li>Trim leading and trailing whitespace</li>
     *   <li>Scan for at least one character where {@link Character#isLetterOrDigit(char)} returns true</li>
     *   <li>Unicode-aware character classification (not limited to ASCII)</li>
     * </ol>
     *
     * @param text the extracted text from the PDF (non-null, may be empty)
     * @return true if text contains at least one letter or digit (Unicode-aware) after normalization
     */
    private static boolean hasUsableText(String text) {
        Objects.requireNonNull(text, "text must not be null");

        // Trim whitespace first
        String trimmed = text.strip();

        // Check if text contains at least one letter or digit (Unicode-aware)
        for (char c : trimmed.toCharArray()) {
            if (Character.isLetterOrDigit(c)) {
                return true;
            }
        }

        // No letter or digit found
        return false;
    }

    private PreCheckEvaluator() {
        // Static utility class – no instances
    }
}