#!/usr/bin/env bash
#
# rd.sh - bezpečne premenuje PDF súbory podľa jednotnej konvencie.
# Parser a logika pomenovania sú nezmenené oproti analyzačnej verzii.
# Pred každým premenovaním sa kontroluje: existujúci cieľ, kolízia dvoch
# zdrojov na ten istý cieľ, a neurčiteľný ("???") návrh.
#
set -uo pipefail

# =============================================================================
# KONFIGURÁCIA
# =============================================================================
# Sem sa pridávajú nové mestá / ulice / skratky - bez zásahu do logiky parsera.

# --- Známe mestá (kanonický tvar, viacslovné mená použi podčiarkovník) ------
CITIES=(
    Sabinov
    Lipany
    Krivany
    Rozkovany
    Pecovska_Nova_Ves
    Sarisske_Michalany
)

# --- Textové aliasy: kanonická (interpunkčne normalizovaná) fráza ----------
# -> kanonický tvar. Aplikujú sa AŽ PO odstránení diakritiky a normalizácii
# interpunkcie, takže jeden kľúč pokryje všetky zápisy tej istej skratky:
# "Šar.Mich.", "Sar.Mich.", "Sar Mich", "Sar-Mich" sa po normalizácii
# interpunkcie všetky zhodujú s "Sar Mich".
declare -A TEXT_ALIASES=(
    ["Sar Mich"]="Sarisske_Michalany"
)

# --- Skratky/celé slová -> kanonický tvar (nahrádzajú sa iba celé slová, --
# --- oddeľovače v texte ostávajú nedotknuté) --------------------------------
declare -A ABBREVIATIONS=(
    [Goj]="Gojdica"
    [Jakub]="Jakubovanska"
    [MS]="Matice_Slovenskej"
    [NS]="Namestie_Slobody"
    [PNV]="Pecovska_Nova_Ves"
    [SB]="Sabinov"
    [TELEK]="Telek"
    [ZT]="ZT"
)

# --- Typy dokumentov: celá fráza (slová oddelené medzerou) -> kanonický typ -
declare -A DOC_TYPE_PATTERNS=(
    ["Vypis uctov"]="Vypis_uctov"
    ["Vypis uctu"]="Vypis_uctov"
    ["Zoznam dodavatelskych faktur"]="Zoznam_faktur"
    ["Zoznam faktur"]="Zoznam_faktur"
    ["Faktury oprav"]="Faktury_oprav"
    ["Faktury opravy"]="Faktury_oprav"
)

# --- Rozpoznávané objekty (ulice/lokality); viacslovné s podčiarkovníkom ---
# POZOR: "PO" tu úmyselne NIE JE - jeho význam nie je známy (pozri
# UNKNOWN_OBJECT_TOKENS nižšie).
OBJECTS=(
    Gojdica
    Jakubovanska
    Mlynska
    Komenskeho
    Hviezdoslavova
    Sturova
    Kvetna
    Michalska
    Hlavna
    Nachajky
    Zahradny
    Matice_Slovenskej
    Namestie_Slobody
    Telek
    ZT
)

# --- Tokeny s neznámym/nejednoznačným významom - nikdy sa automaticky ------
# --- nepomenúvajú, iba sa nahlási upozornenie. ------------------------------
UNKNOWN_OBJECT_TOKENS=(PO)

# --- Slovenské mesiace -> číslo (v kalendárnom poradí kvôli detekcii -------
# --- rozsahov typu "januar_februar") ----------------------------------------
MONTH_ORDER=(januar februar marec april maj jun jul august september oktober november december)
declare -A MONTHS=(
    [januar]=01 [februar]=02 [marec]=03 [april]=04
    [maj]=05    [jun]=06     [jul]=07    [august]=08
    [september]=09 [oktober]=10 [november]=11 [december]=12
)

# --- Rozpoznávané agendy (výhradne z adresárovej štruktúry) ----------------
AGENDAS=(Fond_oprav Faktury)

# --- Mapa diakritiky -> ASCII (nezávislá od locale, na rozdiel od iconv) ---
declare -A DIACRITICS=(
    ["á"]="a" ["ä"]="a" ["č"]="c" ["ď"]="d" ["é"]="e" ["í"]="i"
    ["ľ"]="l" ["ĺ"]="l" ["ň"]="n" ["ó"]="o" ["ô"]="o" ["ŕ"]="r"
    ["š"]="s" ["ť"]="t" ["ú"]="u" ["ý"]="y" ["ž"]="z"
    ["Á"]="A" ["Ä"]="A" ["Č"]="C" ["Ď"]="D" ["É"]="E" ["Í"]="I"
    ["Ľ"]="L" ["Ĺ"]="L" ["Ň"]="N" ["Ó"]="O" ["Ô"]="O" ["Ŕ"]="R"
    ["Š"]="S" ["Ť"]="T" ["Ú"]="U" ["Ý"]="Y" ["Ž"]="Z"
)

# =============================================================================
# POMOCNÉ FUNKCIE
# =============================================================================

# strip_diacritics <text> -> ASCII text, oddeľovače (_ - . medzera) ostávajú
# presne také, aké boli - žiadna informácia sa nestráca.
strip_diacritics() {
    local s="$1" dia
    for dia in "${!DIACRITICS[@]}"; do
        s="${s//$dia/${DIACRITICS[$dia]}}"
    done
    s=$(printf '%s' "$s" | tr -s '[:space:]' ' ')
    s="${s# }"
    s="${s% }"
    printf '%s' "$s"
}

# apply_word_abbreviations <text> -> nahradí iba CELÉ alfabetické slová
# (runy písmen) podľa ABBREVIATIONS. Čísla, podčiarkovníky, pomlčky, bodky
# a medzery ostávajú nedotknuté, takže "3_2026" alebo "41-42" sa nezmenia.
apply_word_abbreviations() {
    local text="$1"
    local result="" cur="" c
    local i len=${#text}
    for (( i=0; i<len; i++ )); do
        c="${text:i:1}"
        if [[ "$c" =~ [A-Za-z] ]]; then
            cur+="$c"
        else
            [[ -n "$cur" ]] && { result+="$(expand_abbreviation "$cur")"; cur=""; }
            result+="$c"
        fi
    done
    [[ -n "$cur" ]] && result+="$(expand_abbreviation "$cur")"
    printf '%s' "$result"
}

# expand_abbreviation <slovo> -> kanonický tvar, ak je slovo presne v
# ABBREVIATIONS (case-sensitive, presne podľa konfigurácie), inak nezmenené.
expand_abbreviation() {
    local word="$1" key
    for key in "${!ABBREVIATIONS[@]}"; do
        [[ "$word" == "$key" ]] && { printf '%s' "${ABBREVIATIONS[$key]}"; return; }
    done
    printf '%s' "$word"
}

# phrase_to_pattern <fráza so slovami oddelenými medzerou>
# -> regex, kde medzery pripúšťajú medzeru/podčiarkovník/pomlčku ako
# oddeľovač medzi slovami (napr. "Vypis uctov" -> "Vypis[ _-]+uctov").
phrase_to_pattern() {
    local phrase="$1"
    printf '%s' "${phrase// /[ _-]+}"
}

# text_has_phrase <text> <fráza> -> 0 ak sa fráza nájde kdekoľvek v texte
# (ohraničená nepísmenovými znakmi), inak 1. Case-insensitive.
text_has_phrase() {
    local text="$1" phrase="$2" pat
    pat=$(phrase_to_pattern "$phrase")
    local result=1
    shopt -s nocasematch
    [[ "$text" =~ (^|[^A-Za-z])$pat($|[^A-Za-z]) ]] && result=0
    shopt -u nocasematch
    return $result
}

# normalize_punctuation <text> -> POUŽÍVA SA VÝHRADNE na porovnávanie s
# TEXT_ALIASES. Nahradí "." "-" "_" medzerou a zlúči viacnásobné medzery.
# Táto (stratová) verzia textu sa nikde inde nepoužíva - detekcia mesiaca
# a objektu naďalej pracuje s pôvodným, bezstratovým textom.
normalize_punctuation() {
    local s="$1"
    s="${s//./ }"
    s="${s//-/ }"
    s="${s//_/ }"
    s=$(printf '%s' "$s" | tr -s '[:space:]' ' ')
    s="${s# }"
    s="${s% }"
    printf '%s' "$s"
}

# detect_year <text> -> prvý štvorciferný rok v tvare 20YY, alebo ""
# Používa sa iba ako záložný zdroj, ak rok nebolo možné určiť z cesty.
detect_year() {
    local text="$1"
    if [[ "$text" =~ (^|[^0-9])(20[0-9]{2})($|[^0-9]) ]]; then
        printf '%s' "${BASH_REMATCH[2]}"
        return
    fi
    printf ''
}

# normalize_output_dir <adresár> -> rovnaký adresár, ale každá jeho zložka
# normalizovaná rovnako ako názvy súborov (bez diakritiky, medzery a
# pomlčky -> "_", viac "_" -> jedno). POUŽÍVA SA IBA na zostavenie
# navrhovaného výstupu - na disku sa nič nemení a nič sa nevytvára.
normalize_output_dir() {
    local dir="$1"
    local -a parts
    IFS='/' read -r -a parts <<< "$dir"

    local -a out_parts=()
    local part norm
    for part in "${parts[@]}"; do
        if [[ -z "$part" || "$part" == "." || "$part" == ".." ]]; then
            out_parts+=("$part")
            continue
        fi
        norm=$(strip_diacritics "$part")
        norm="${norm// /_}"
        norm="${norm//-/_}"
        norm=$(to_underscore "$norm")
        out_parts+=("$norm")
    done

    local IFS='/'
    printf '%s' "${out_parts[*]}"
}

# to_underscore <text> -> nahradí medzery podčiarkovníkom a zlúči duplicity
to_underscore() {
    local s="$1"
    s="${s// /_}"
    while [[ "$s" == *__* ]]; do
        s="${s//__/_}"
    done
    s="${s#_}"
    s="${s%_}"
    printf '%s' "$s"
}

# =============================================================================
# PARSER CESTY (primárny zdroj: agenda, rok, mesto)
# =============================================================================

# parse_path <adresár> -> "agenda|rok|mesto" (prázdne, ak sa nenašlo;
# žiadne predvolené hodnoty sa nedosádzajú).
parse_path() {
    local dir="$1"
    local agenda="" year="" city=""
    local -a parts
    IFS='/' read -r -a parts <<< "$dir"

    local part norm_part cand cand_disp
    for part in "${parts[@]}"; do
        [[ -z "$part" || "$part" == "." ]] && continue
        norm_part=$(strip_diacritics "$part")

        if [[ -z "$agenda" ]]; then
            for cand in "${AGENDAS[@]}"; do
                cand_disp="${cand//_/ }"
                text_has_phrase "$norm_part" "$cand_disp" && { agenda="$cand"; break; }
            done
        fi

        if [[ -z "$year" && "$norm_part" =~ (^|[^0-9])(20[0-9]{2})($|[^0-9]) ]]; then
            year="${BASH_REMATCH[2]}"
        fi

        if [[ -z "$city" ]]; then
            for cand in "${CITIES[@]}"; do
                cand_disp="${cand//_/ }"
                text_has_phrase "$norm_part" "$cand_disp" && { city="$cand"; break; }
            done
        fi
    done

    printf '%s|%s|%s' "$agenda" "$year" "$city"
}

# =============================================================================
# PARSER NÁZVU SÚBORU (iba: typ dokumentu, mesiac, objekt; prípadne mesto,
# ak ho cesta neurčila)
# =============================================================================

# detect_city <text> -> kanonický názov mesta alebo prázdne
# Postup: text prichádza už bez diakritiky (strip_diacritics) -> tu sa
# najskôr normalizuje interpunkcia -> až potom sa aplikujú TEXT_ALIASES
# a napokon sa hľadajú kanonické názvy miest.
detect_city() {
    local text="$1" alias_text cand cand_disp
    alias_text=$(normalize_punctuation "$text")

    local alias
    for alias in "${!TEXT_ALIASES[@]}"; do
        text_has_phrase "$alias_text" "$alias" && { printf '%s' "${TEXT_ALIASES[$alias]}"; return; }
    done

    for cand in "${CITIES[@]}"; do
        cand_disp="${cand//_/ }"
        text_has_phrase "$text" "$cand_disp" && { printf '%s' "$cand"; return; }
    done
    printf ''
}

# detect_month <text> -> "01".."12", "01_02" (rozsah) alebo ""
detect_month() {
    local text="$1" i m1 m2

    for (( i=0; i<${#MONTH_ORDER[@]}-1; i++ )); do
        m1="${MONTH_ORDER[$i]}"
        m2="${MONTH_ORDER[$((i+1))]}"
        if text_has_phrase "$text" "$m1 $m2"; then
            printf '%s_%s' "${MONTHS[$m1]}" "${MONTHS[$m2]}"
            return
        fi
    done

    local m
    for m in "${MONTH_ORDER[@]}"; do
        text_has_phrase "$text" "$m" && { printf '%s' "${MONTHS[$m]}"; return; }
    done

    # číselný vzor "<1-12>_<20YY>" alebo "<1-12>-<20YY>", napr. "3_2026"
    if [[ "$text" =~ (^|[^0-9])([0-9]{1,2})[_-](20[0-9]{2})($|[^0-9]) ]]; then
        local n="${BASH_REMATCH[2]}"
        if (( 10#$n >= 1 && 10#$n <= 12 )); then
            printf '%02d' "$((10#$n))"
            return
        fi
    fi
    printf ''
}

# detect_type <text> -> kanonický typ dokumentu alebo ""
detect_type() {
    local text="$1" phrase
    for phrase in "${!DOC_TYPE_PATTERNS[@]}"; do
        text_has_phrase "$text" "$phrase" && { printf '%s' "${DOC_TYPE_PATTERNS[$phrase]}"; return; }
    done
    printf ''
}

# detect_object <text> -> "objekt|upozornenie"
# Objekt sa hľadá KDEKOĽVEK v texte, nielen na začiatku. Tokeny s neznámym
# významom (napr. "PO") sa nikdy automaticky nepomenujú.
detect_object() {
    local text="$1" tok

    for tok in "${UNKNOWN_OBJECT_TOKENS[@]}"; do
        if [[ "$text" =~ (^|[^A-Za-z])$tok($|[^A-Za-z]) ]]; then
            printf '|objekt "%s" ma neznamy vyznam' "$tok"
            return
        fi
    done

    local cand cand_disp cand_pat n1 n2

    # 1. prechod: objekt s číslom/rozsahom (napr. "Michalska 41-42")
    for cand in "${OBJECTS[@]}"; do
        cand_disp="${cand//_/ }"
        cand_pat=$(phrase_to_pattern "$cand_disp")
        shopt -s nocasematch
        if [[ "$text" =~ (^|[^A-Za-z])$cand_pat[\ _-]*([0-9]+)([\ _-]+([0-9]+))?($|[^0-9]) ]]; then
            n1="${BASH_REMATCH[2]}"
            n2="${BASH_REMATCH[4]}"
            shopt -u nocasematch
            local result="$cand"
            [[ -n "$n1" ]] && result+="_$n1"
            [[ -n "$n2" ]] && result+="_$n2"
            printf '%s|' "$result"
            return
        fi
        shopt -u nocasematch
    done

    # 2. prechod: objekt bez čísla (napr. "ZT", "Telek")
    for cand in "${OBJECTS[@]}"; do
        cand_disp="${cand//_/ }"
        cand_pat=$(phrase_to_pattern "$cand_disp")
        shopt -s nocasematch
        if [[ "$text" =~ (^|[^A-Za-z])$cand_pat($|[^A-Za-z]) ]]; then
            shopt -u nocasematch
            printf '%s|' "$cand"
            return
        fi
        shopt -u nocasematch
    done

    printf '|'
}

# parse_filename <názov_bez_prípony> <mesto_z_cesty> <rok_z_cesty>
# -> "rok|mesiac|typ|objekt|upozornenie_objektu|mesto"
# Agenda sa v názve súboru NEHĽADÁ vôbec. Rok a mesto, ak ich už určila
# cesta, sa v názve znovu nehľadajú - použijú sa iba ako záložný zdroj,
# keď ich cesta neobsahovala.
parse_filename() {
    local name="$1" path_city="$2" path_year="$3"
    local raw expanded
    raw=$(strip_diacritics "$name")
    expanded=$(apply_word_abbreviations "$raw")

    local year month type city obj_result object obj_warning
    month=$(detect_month "$expanded")
    type=$(detect_type "$expanded")

    if [[ -n "$path_year" ]]; then
        year="$path_year"
    else
        year=$(detect_year "$expanded")
    fi

    if [[ -n "$path_city" ]]; then
        city="$path_city"
    else
        city=$(detect_city "$expanded")
    fi

    object="" obj_warning=""
    if [[ -z "$type" ]]; then
        obj_result=$(detect_object "$expanded")
        IFS='|' read -r object obj_warning <<< "$obj_result"
    fi

    printf '%s|%s|%s|%s|%s|%s' "$year" "$month" "$type" "$object" "$obj_warning" "$city"
}

# =============================================================================
# ZOSTAVENIE NÁZVU
# =============================================================================

# build_filename agenda rok mesiac mesto typ objekt objekt_upozornenie
# -> "navrh|dovod_chyby"
build_filename() {
    local agenda="$1" year="$2" month="$3" city="$4" type="$5" object="$6" warning="$7"
    local proposal=""

    if [[ -z "$agenda" ]]; then
        printf '???|agenda nerozpoznana'
        return
    fi
    if [[ -z "$year" ]]; then
        printf '???|rok nerozpoznany'
        return
    fi
    if [[ -n "$warning" ]]; then
        printf '???|%s' "$warning"
        return
    fi

    if [[ -n "$type" ]]; then
        # Mesačný dokument: ROK[_MESIAC]_AGENDA_[MESTO_]TYP
        proposal="$year"
        [[ -n "$month" ]] && proposal+="_$month"
        proposal+="_${agenda}"
        [[ -n "$city" ]] && proposal+="_$city"
        proposal+="_$type"
    elif [[ -n "$object" ]]; then
        # Ročný dokument: ROK_AGENDA_[MESTO_]OBJEKT
        proposal="${year}_${agenda}"
        [[ -n "$city" ]] && proposal+="_$city"
        proposal+="_$object"
    elif [[ -n "$city" ]]; then
        # Mesto bez samostatného objektu - celá lokalita je "objekt"
        # (napr. Fond_oprav-2025-Rozkovany.pdf -> 2025_Fond_oprav_Rozkovany.pdf)
        proposal="${year}_${agenda}_${city}"
    else
        printf '???|neznamy typ dokumentu'
        return
    fi

    printf '%s|' "$(to_underscore "$proposal")"
}

# validate <navrh> -> 0 ak je navrh platny (neobsahuje len "???")
validate() {
    local proposal="$1"
    [[ -n "$proposal" && "$proposal" != "???" ]]
}

# =============================================================================
# HLAVNÁ LOGIKA
# =============================================================================

# compute_target <file> -> "cieľ|dôvod_chyby"
# Presne tá istá logika ako predtým (parse_path, parse_filename,
# build_filename, validate) - iba vracia výsledok namiesto výpisu.
# Cieľ je prázdny, ak parser nevie určiť jednoznačný názov (dôvod v $2).
compute_target() {
    local file="$1"
    local dir base name
    dir=$(dirname "$file")
    base=$(basename "$file")
    name="${base%.[pP][dD][fF]}"

    local path_info agenda year path_city
    path_info=$(parse_path "$dir")
    IFS='|' read -r agenda year path_city <<< "$path_info"

    local fname_info fname_year month type object obj_warning city
    fname_info=$(parse_filename "$name" "$path_city" "$year")
    IFS='|' read -r fname_year month type object obj_warning city <<< "$fname_info"
    year="$fname_year"

    local result proposal reason
    result=$(build_filename "$agenda" "$year" "$month" "$city" "$type" "$object" "$obj_warning")
    IFS='|' read -r proposal reason <<< "$result"

    if validate "$proposal"; then
        local out_dir target
        out_dir=$(normalize_output_dir "$dir")
        target="${out_dir}/${proposal}.pdf"
        printf '%s|' "$target"
    else
        printf '|%s' "$reason"
    fi
}

main() {
    local -a files=()
    local f
    while IFS= read -r f; do
        files+=("$f")
    done < <(find . -type f -iname "*.pdf" | sort)

    # 1. prechod: pre každý súbor vypočítaj cieľ (parser sa volá iba raz na
    # súbor) a zisti, koľko rôznych zdrojov vedie na ten istý cieľ.
    local -a targets=() reasons=()
    local -A target_count=() target_first=()
    local i info target reason
    for i in "${!files[@]}"; do
        info=$(compute_target "${files[$i]}")
        IFS='|' read -r target reason <<< "$info"
        targets[$i]="$target"
        reasons[$i]="$reason"
        if [[ -n "$target" ]]; then
            target_count["$target"]=$(( ${target_count["$target"]:-0} + 1 ))
            if [[ -z "${target_first["$target"]:-}" ]]; then
                target_first["$target"]="$i"
            fi
        fi
    done

    # 2. prechod: bezpečné premenovanie s kontrolami pred každým "mv".
    local processed=0 renamed=0 skipped=0 collisions=0 errors=0
    local old new first_idx rc
    for i in "${!files[@]}"; do
        old="${files[$i]}"
        target="${targets[$i]}"
        reason="${reasons[$i]}"
        processed=$((processed + 1))

        if [[ -z "$target" ]]; then
            skipped=$((skipped + 1))
            echo "PRESKOCENE: ??? (${reason:-neznamy dovod})"
            echo "$old"
            echo
            continue
        fi

        if [[ "${target_count["$target"]}" -gt 1 ]]; then
            collisions=$((collisions + 1))
            first_idx="${target_first["$target"]}"
            if [[ "$i" != "$first_idx" ]]; then
                echo "KOLIZIA:"
                echo "Zdroj 1:"
                echo "${files[$first_idx]}"
                echo "Zdroj 2:"
                echo "$old"
                echo "Ciel:"
                echo "$target"
                echo
            fi
            continue
        fi

        if [[ -e "$target" ]]; then
            errors=$((errors + 1))
            echo "CHYBA: cielovy subor uz existuje"
            echo "$old"
            echo " ->"
            echo "$target"
            echo
            continue
        fi

        new="$target"
        if mv -- "$old" "$new"; then
            renamed=$((renamed + 1))
            echo "OK"
            echo "$old"
            echo " ->"
            echo "$new"
            echo
        else
            errors=$((errors + 1))
            echo "CHYBA: premenovanie zlyhalo"
            echo "$old"
            echo
        fi
    done

    echo "=================================="
    echo "Spracovanych: $processed"
    echo "Premenovanych: $renamed"
    echo "Preskocenych: $skipped"
    echo "Kolizii: $collisions"
    echo "Chyb: $errors"
    echo "=================================="
}

main "$@"
