<?php

declare(strict_types=1);

namespace App\Ingest;

/**
 * DocumentSanitizer
 *
 * Deterministic, minimal-invasive preprocessing BEFORE chunking.
 *
 * Removes typical PDF/DOC artefacts:
 *  - Table of contents blocks
 *  - Page numbers
 *  - Repeated headers/footers
 *  - Dot-leader lines (e.g. "...... 12")
 *
 * Design principles:
 *  - No semantic rewriting
 *  - No randomness
 *  - No removal of real paragraphs
 *  - Type-aware sanitizing (PDF/DOC != MD/TXT)
 */
final class DocumentSanitizer
{
    private const MAX_HEADER_LEN = 120;
    private const REPEAT_HEADER_MIN_COUNT = 3;

    public function sanitize(string $text, string $fileExtension): string
    {
        if ($text === '') {
            return '';
        }

        $text = $this->normalizeLineEndings($text);
        $fileExtension = strtolower($fileExtension);

        // Nur PDF-/DOC-artige Formate aggressiver behandeln
        if (in_array($fileExtension, ['pdf', 'doc', 'docx'], true)) {
            $text = $this->sanitizePdfLike($text);
        }

        return trim($this->cleanupWhitespace($text));
    }

    // =========================================================
    // PIPELINE
    // =========================================================

    private function sanitizePdfLike(string $text): string
    {
        $text = $this->removeToc($text);
        $text = $this->removePageNumbers($text);
        $text = $this->removeDotLeaderLines($text);
        $text = $this->removeRepeatedHeaders($text);

        return $text;
    }

    private function normalizeLineEndings(string $text): string
    {
        return str_replace(["\r\n", "\r"], "\n", $text);
    }

    // =========================================================
    // TOC REMOVAL
    // =========================================================

    private function removeToc(string $text): string
    {
        $lines = explode("\n", $text);
        $filtered = [];

        $inToc = false;

        foreach ($lines as $line) {
            $trim = trim($line);

            // TOC Start
            if (!$inToc && $trim !== '' && stripos($trim, 'inhaltsverzeichnis') !== false) {
                $inToc = true;
                continue;
            }

            if ($inToc) {
                if ($trim === '') {
                    continue;
                }

                if (
                    $this->looksLikeDotLeaderLine($trim) ||
                    $this->looksLikeNumberedTocLine($trim)
                ) {
                    continue;
                }

                // Ende TOC sobald normale Satzstruktur erkannt wird
                if (preg_match('/[a-zäöüß]\.\s*$/iu', $trim)) {
                    $inToc = false;
                    $filtered[] = $line;
                    continue;
                }

                continue;
            }

            $filtered[] = $line;
        }

        return implode("\n", $filtered);
    }

    // =========================================================
    // PAGE NUMBERS
    // =========================================================

    private function removePageNumbers(string $text): string
    {
        $lines = explode("\n", $text);
        $filtered = [];

        foreach ($lines as $line) {
            $trim = trim($line);

            if ($trim === '') {
                $filtered[] = $line;
                continue;
            }

            // "Seite 3" / "Seite 3 von 20"
            if (preg_match('/^seite\s+\d{1,4}(\s+von\s+\d{1,4})?$/iu', $trim)) {
                continue;
            }

            // "Page 12" / "Page 12 of 34"
            if (preg_match('/^page\s+\d{1,4}(\s+of\s+\d{1,4})?$/iu', $trim)) {
                continue;
            }

            // Isolierte Seitenmarker: "- 4 -" oder "– 4 –"
            if (preg_match('/^[-–]\s?\d{1,4}\s?[-–]$/u', $trim)) {
                continue;
            }

            // Nur reine Zahl (max 3 Stellen, um IDs nicht zu killen)
            if (preg_match('/^\d{1,3}$/u', $trim)) {
                continue;
            }

            $filtered[] = $line;
        }

        return implode("\n", $filtered);
    }

    // =========================================================
    // DOT LEADER
    // =========================================================

    private function removeDotLeaderLines(string $text): string
    {
        $lines = explode("\n", $text);
        $filtered = [];

        foreach ($lines as $line) {
            $trim = trim($line);

            if ($trim !== '' && $this->looksLikeDotLeaderLine($trim)) {
                continue;
            }

            $filtered[] = $line;
        }

        return implode("\n", $filtered);
    }

    // =========================================================
    // REPEATED HEADERS
    // =========================================================

    private function removeRepeatedHeaders(string $text): string
    {
        $lines = explode("\n", $text);

        $trimmed = array_map('trim', $lines);
        $counts = array_count_values($trimmed);

        $filtered = [];

        foreach ($lines as $line) {
            $trim = trim($line);

            if (
                $trim !== '' &&
                strlen($trim) < self::MAX_HEADER_LEN &&
                ($counts[$trim] ?? 0) >= self::REPEAT_HEADER_MIN_COUNT
            ) {
                continue;
            }

            $filtered[] = $line;
        }

        return implode("\n", $filtered);
    }

    // =========================================================
    // WHITESPACE
    // =========================================================

    private function cleanupWhitespace(string $text): string
    {
        // Maximal 2 Leerzeilen
        return preg_replace("/\n{3,}/", "\n\n", $text);
    }

    // =========================================================
    // HEURISTICS
    // =========================================================

    private function looksLikeDotLeaderLine(string $trimmedLine): bool
    {
        return (bool)preg_match('/^.+\.{4,}\s*\d+$/u', $trimmedLine);
    }

    private function looksLikeNumberedTocLine(string $trimmedLine): bool
    {
        return (bool)preg_match('/^\d+(\.\d+)*\s+.+\s+\d+$/u', $trimmedLine);
    }
}