from __future__ import annotations

from pathlib import Path
import difflib
import json
import re
import sys
from urllib.parse import urlparse

import settings_config


ROOT_DIR = Path(__file__).resolve().parent.parent
CONTENT_DIR = settings_config.active_content_dir()
GENERATOR_DIR = ROOT_DIR / "generator"
COMPONENTS_FILE = GENERATOR_DIR / "components.json"
SETTINGS_FILE = settings_config.SETTINGS_JSON_FILE
SETTINGS_MARKDOWN_FILE = settings_config.active_settings_file() or settings_config.SETTINGS_MARKDOWN_FILE

MEDIA_DIR_NAME = "medien"
LEGACY_MEDIA_DIR_NAME = "assets"
GALLERY_DIR_NAME = "galerie"
LEGACY_GALLERY_DIR_NAME = "gallery"
FOOTER_DIR_NAME = "fusszeile"
LEGACY_FOOTER_DIR_NAME = "footer"
FOOTER_FILE_NAME = "fusszeile.md"
LEGACY_FOOTER_FILE_NAME = "footer.md"

IMAGE_EXTENSIONS = {
    ".jpg",
    ".jpeg",
    ".png",
    ".gif",
    ".webp",
    ".svg"
}

VIDEO_EXTENSIONS = {
    ".mp4",
    ".webm",
    ".ogg",
    ".mov"
}


TRANSLATIONS = {
    "de": {
        "json_error": "FEHLER: {path} ist keine gueltige JSON-Datei.",
        "json_position": "  Zeile {line}, Spalte {column}: {message}",
        "navigation_height": "FEHLER: generator/settings.json: navigation.height muss small, medium oder large sein.",
        "navigation_align": "FEHLER: Einstellungen: Navigationsausrichtung muss links, mittig oder rechts sein.",
        "mobile_menu_align": "FEHLER: Einstellungen: Mobilmenü muss automatisch, links, mittig oder rechts sein.",
        "mobile_menu_submenus": "FEHLER: Einstellungen: Mobilmenü-Untermenüs müssen offen oder eingeklappt sein.",
        "language": "FEHLER: generator/settings.json: language muss de oder en sein.",
        "validator_language": "FEHLER: generator/settings.json: validator_language muss de oder en sein.",
        "theme": "FEHLER: Einstellungen: theme muss eine bekannte Farbpalette sein: {themes}.",
        "theme_multiple": "WARNUNG: havelseite/einstellungen.md: Mehrere Farbpaletten sind angekreuzt ({selected}). Ich verwende die erste: {first}.",
        "navigation_position": "FEHLER: Einstellungen: Navigation muss sticky/bleibt oben oder static/scrollt mit sein.",
        "navigation_multiple": "WARNUNG: havelseite/einstellungen.md: Navigation hat mehrere Optionen angekreuzt ({selected}). Ich verwende die erste: {first}.",
        "logo_align": "FEHLER: Einstellungen: Wenn das Logo angezeigt wird, muss links oder rechts angekreuzt sein.",
        "logo_align_center": "FEHLER: Einstellungen: Das Logo kann nur links oder rechts stehen.",
        "logo_missing": "FEHLER: Logo nicht gefunden: havelseite/medien/{folder}/{src}",
        "logo_folder_missing": "FEHLER: Logoordner nicht gefunden oder leer: havelseite/medien/{folder}",
        "logo_multiple": "WARNUNG: havelseite/einstellungen.md: Im Logoordner liegen mehrere Bilder ({files}). Ich verwende alphabetisch das erste: {first}.",
        "gallery_layout": "FEHLER: Einstellungen: Galerie muss quadratisch, breit oder kacheln sein.",
        "icon_style": "FEHLER: Einstellungen: Icons muss rund, schlicht oder text sein.",
        "font": "FEHLER: Einstellungen: Schriftgröße muss klein, mittel oder groß sein.",
        "unknown_block": "FEHLER: {path}:{line}: @{annotation} kenne ich nicht.{hint}",
        "did_you_mean": " Meintest du @{suggestion}?",
        "gallery_missing": "FEHLER: {path}:{line}: Galerieordner fehlt: havelseite/medien/galerie/{parameter}.{hint}",
        "image_missing": "FEHLER: {path}:{line}: Bild nicht gefunden: {src}.{hint}",
        "internal_link_missing": "WARNUNG: {path}:{line}: Interner Link zeigt auf keine Seite: [[{label}]].{hint}",
        "hero_missing": "FEHLER: {path}:{line}: Aufmacher-Datei nicht gefunden: {parameter}",
        "event_single_multiple": "WARNUNG: {path}:{line}: @{annotation} sieht nach mehreren Veranstaltungen aus. Nutze dafuer @veranstaltungen.",
        "events_list_single": "WARNUNG: {path}:{line}: @{annotation} ist fuer mehrere Veranstaltungen gedacht. Fuer eine einzelne Veranstaltung nutze @veranstaltung.",
        "person_single_multiple": "WARNUNG: {path}:{line}: @{annotation} sieht nach mehreren Personen aus. Nutze dafuer @personen.",
        "people_list_single": "WARNUNG: {path}:{line}: @{annotation} ist fuer mehrere Personen gedacht. Fuer eine einzelne Person nutze @person.",
        "nested_page": "FEHLER: {path}: Seiten duerfen nur eine Ordnerebene tief liegen. Bitte verschiebe die Datei direkt nach havelseite/ oder in einen Ordner direkt unter havelseite/.",
        "found_header": "Ich habe ein paar Dinge gefunden:\n",
        "found_footer": "\nBitte korrigiere diese Punkte und pruefe danach nochmal.",
        "ok": "Alles gut. Keine offensichtlichen Probleme gefunden."
    },
    "en": {
        "json_error": "ERROR: {path} is not a valid JSON file.",
        "json_position": "  Line {line}, column {column}: {message}",
        "navigation_height": "ERROR: generator/settings.json: navigation.height must be small, medium, or large.",
        "navigation_align": "ERROR: Settings: navigation alignment must be left, center, or right.",
        "mobile_menu_align": "ERROR: Settings: mobile menu must be auto, left, center, or right.",
        "mobile_menu_submenus": "ERROR: Settings: mobile submenu behavior must be open or closed.",
        "language": "ERROR: generator/settings.json: language must be de or en.",
        "validator_language": "ERROR: generator/settings.json: validator_language must be de or en.",
        "theme": "ERROR: Settings: theme must be a known color theme: {themes}.",
        "theme_multiple": "WARNING: havelseite/einstellungen.md: Multiple color themes are checked ({selected}). I will use the first one: {first}.",
        "navigation_position": "ERROR: Settings: navigation.position must be sticky or static.",
        "navigation_multiple": "WARNING: havelseite/einstellungen.md: Navigation has multiple checked options ({selected}). I will use the first one: {first}.",
        "logo_align": "ERROR: Settings: if the logo is shown, left or right must be checked.",
        "logo_align_center": "ERROR: Settings: the logo can only be left or right.",
        "logo_missing": "ERROR: Logo not found: havelseite/medien/{folder}/{src}",
        "logo_folder_missing": "ERROR: Logo folder is missing or empty: havelseite/medien/{folder}",
        "logo_multiple": "WARNING: havelseite/einstellungen.md: Multiple images are in the logo folder ({files}). I will use the alphabetically first one: {first}.",
        "gallery_layout": "ERROR: Settings: gallery layout must be square, wide, or masonry.",
        "icon_style": "ERROR: Settings: icons must be round, simple, or text.",
        "font": "ERROR: Settings: font size must be small, medium, or large.",
        "unknown_block": "ERROR: {path}:{line}: I do not know @{annotation}.{hint}",
        "did_you_mean": " Did you mean @{suggestion}?",
        "gallery_missing": "ERROR: {path}:{line}: Gallery folder is missing: havelseite/medien/galerie/{parameter}.{hint}",
        "image_missing": "ERROR: {path}:{line}: Image not found: {src}.{hint}",
        "internal_link_missing": "WARNING: {path}:{line}: Internal link does not point to a known page: [[{label}]].{hint}",
        "hero_missing": "ERROR: {path}:{line}: Hero file not found: {parameter}",
        "event_single_multiple": "WARNING: {path}:{line}: @{annotation} looks like multiple events. Use @events for that.",
        "events_list_single": "WARNING: {path}:{line}: @{annotation} is meant for multiple events. Use @event for a single event.",
        "person_single_multiple": "WARNING: {path}:{line}: @{annotation} looks like multiple people. Use @people for that.",
        "people_list_single": "WARNING: {path}:{line}: @{annotation} is meant for multiple people. Use @person for one person.",
        "nested_page": "ERROR: {path}: Pages may only be one folder deep. Please move this file directly to havelseite/ or into a folder directly below havelseite/.",
        "found_header": "I found a few things:\n",
        "found_footer": "\nPlease fix these points and check again.",
        "ok": "All good. No obvious problems found."
    }
}


def language_from_settings() -> str:
    try:
        settings = settings_config.load_settings()
    except (json.JSONDecodeError, OSError):
        return "de"

    language = settings.get(
        "validator_language",
        settings.get("language", "de")
    )

    if language not in TRANSLATIONS:
        return "de"

    return language


def tr(language: str, key: str, **values) -> str:
    return TRANSLATIONS[language][key].format(**values)


def load_json(
    file_path: Path,
    exit_on_error: bool = True
):
    try:
        with file_path.open(encoding="utf-8") as f:
            return json.load(f)
    except json.JSONDecodeError as error:
        if not exit_on_error:
            raise

        language = "de"

        print(tr(
            language,
            "json_error",
            path=file_path.relative_to(ROOT_DIR)
        ))
        print(tr(
            language,
            "json_position",
            line=error.lineno,
            column=error.colno,
            message=error.msg
        ))
        sys.exit(1)


def is_external_url(value: str) -> bool:
    return (
        value.startswith("http://")
        or value.startswith("https://")
    )


def is_footer_file(md_file: Path) -> bool:
    relative = md_file.relative_to(CONTENT_DIR)

    return (
        len(relative.parts) >= 2
        and relative.parts[0] in {FOOTER_DIR_NAME, LEGACY_FOOTER_DIR_NAME}
        and md_file.name in {FOOTER_FILE_NAME, LEGACY_FOOTER_FILE_NAME}
    )


def title_from_file(md_file: Path) -> str:
    stem = display_name(md_file.stem)

    if stem == "index":
        if md_file.parent == CONTENT_DIR:
            return "Start"

        return display_name(md_file.parent.name).replace("-", " ").title()

    return stem.replace("-", " ").title()


def display_name(name: str) -> str:
    return re.sub(r"^\d+[_-]+", "", name)


def slugify_title(title: str) -> str:
    slug = title.strip().lower()
    slug = slug.replace("ä", "ae")
    slug = slug.replace("ö", "oe")
    slug = slug.replace("ü", "ue")
    slug = slug.replace("ß", "ss")
    slug = re.sub(r"[^a-z0-9]+", "-", slug)

    return slug.strip("-")


def page_keys() -> set[str]:
    keys = set()

    for md_file in CONTENT_DIR.rglob("*.md"):
        if is_footer_file(md_file):
            continue

        if md_file == SETTINGS_MARKDOWN_FILE:
            continue

        keys.add(md_file.stem.lower())
        keys.add(display_name(md_file.stem).lower())
        keys.add(slugify_title(title_from_file(md_file)))

    return keys


def page_suggestions() -> list[str]:
    suggestions = []

    for md_file in CONTENT_DIR.rglob("*.md"):
        if is_footer_file(md_file):
            continue

        if md_file == SETTINGS_MARKDOWN_FILE:
            continue

        suggestions.append(title_from_file(md_file))
        suggestions.append(display_name(md_file.stem))

    return sorted(set(suggestions))


def gallery_suggestion(parameter: str) -> str:
    gallery_dir = CONTENT_DIR / MEDIA_DIR_NAME / GALLERY_DIR_NAME

    if not gallery_dir.exists():
        gallery_dir = CONTENT_DIR / LEGACY_MEDIA_DIR_NAME / LEGACY_GALLERY_DIR_NAME

    if not gallery_dir.exists():
        return ""

    candidates = [
        path.name for path in gallery_dir.iterdir()
        if path.is_dir()
    ]
    suggestion = difflib.get_close_matches(parameter, candidates, n=1)

    if not suggestion:
        return ""

    return f" Meintest du {suggestion[0]}?"


def media_suggestion(src: str) -> str:
    media_dir = CONTENT_DIR / MEDIA_DIR_NAME

    if not media_dir.exists():
        media_dir = CONTENT_DIR / LEGACY_MEDIA_DIR_NAME

    if not media_dir.exists():
        return ""

    file_name = Path(src).name
    candidates = [
        path.name for path in media_dir.rglob("*")
        if path.is_file()
    ]
    suggestion = difflib.get_close_matches(file_name, candidates, n=1)

    if not suggestion:
        return ""

    matches = [
        path for path in media_dir.rglob(suggestion[0])
        if path.is_file()
    ]

    if not matches:
        return f" Meintest du {suggestion[0]}?"

    try:
        suggested_path = matches[0].relative_to(CONTENT_DIR).as_posix()
    except ValueError:
        suggested_path = matches[0].name

    return f" Meintest du {suggested_path}?"


def content_relative_from_src(src: str, source_file: Path) -> Path:
    src = src.strip().strip("/")

    variants = [src]

    if src.startswith(f"{LEGACY_MEDIA_DIR_NAME}/"):
        variants.append(
            f"{MEDIA_DIR_NAME}/{src.removeprefix(f'{LEGACY_MEDIA_DIR_NAME}/')}"
        )

    variants = [
        variant.replace(
            f"{MEDIA_DIR_NAME}/{LEGACY_GALLERY_DIR_NAME}/",
            f"{MEDIA_DIR_NAME}/{GALLERY_DIR_NAME}/"
        )
        for variant in variants
    ]

    candidates = []

    for variant in dict.fromkeys(variants):
        candidates.extend([
            source_file.parent / variant,
            CONTENT_DIR / variant
        ])

    for candidate in candidates:
        if candidate.exists():
            return candidate

    assets_dir = CONTENT_DIR / MEDIA_DIR_NAME

    if not assets_dir.exists():
        assets_dir = CONTENT_DIR / LEGACY_MEDIA_DIR_NAME

    if assets_dir.exists():
        matches = sorted([
            path for path in assets_dir.rglob(Path(src).name)
            if path.is_file()
        ])

        if matches:
            return matches[0]

    return CONTENT_DIR / src


def line_number(text: str, offset: int) -> int:
    return text.count("\n", 0, offset) + 1


def annotated_blocks(text: str) -> list[dict]:
    matches = list(re.finditer(r"(?m)^@([^\s:]+)(?::([^\n]+))?", text))
    blocks = []

    for index, match in enumerate(matches):
        content_start = match.end()
        content_end = (
            matches[index + 1].start()
            if index + 1 < len(matches)
            else len(text)
        )
        blocks.append({
            "annotation": match.group(1).strip(),
            "parameter": (match.group(2) or "").strip(),
            "content": text[content_start:content_end].strip(),
            "line": line_number(text, match.start())
        })

    return blocks


def validate_settings(
    messages: list[str],
    language: str
):
    settings = settings_config.load_settings()
    selected_settings = {}

    if SETTINGS_MARKDOWN_FILE.exists():
        selected_settings = settings_config.selected_by_section(
            SETTINGS_MARKDOWN_FILE.read_text(encoding="utf-8")
        )

    if settings.get("language", "de") not in {"de", "en"}:
        messages.append(tr(language, "language"))

    if settings.get("validator_language", "de") not in {"de", "en"}:
        messages.append(tr(language, "validator_language"))

    if settings.get("theme", "havel") not in settings_config.COLOR_THEMES:
        messages.append(tr(
            language,
            "theme",
            themes=", ".join(settings_config.COLOR_THEMES.keys())
        ))

    selected_themes = selected_settings.get("theme", [])

    if len(selected_themes) > 1:
        messages.append(tr(
            language,
            "theme_multiple",
            selected=", ".join(selected_themes),
            first=selected_themes[0]
        ))

    navigation_height = (
        settings
        .get("navigation", {})
        .get("height", "medium")
    )

    if navigation_height not in {"small", "medium", "large"}:
        messages.append(tr(language, "navigation_height"))

    navigation_position = (
        settings
        .get("navigation", {})
        .get("position", "sticky")
    )

    if navigation_position not in {"sticky", "static"}:
        messages.append(tr(language, "navigation_position"))

    navigation_align = (
        settings
        .get("navigation", {})
        .get("align", "center")
    )

    if navigation_align not in {"left", "center", "right"}:
        messages.append(tr(language, "navigation_align"))

    mobile_menu_align = (
        settings
        .get("mobile_menu", {})
        .get("align", "auto")
    )

    if mobile_menu_align not in {"auto", "left", "center", "right"}:
        messages.append(tr(language, "mobile_menu_align"))

    mobile_menu_submenus = (
        settings
        .get("mobile_menu", {})
        .get("submenus", "open")
    )

    if mobile_menu_submenus not in {"open", "closed"}:
        messages.append(tr(language, "mobile_menu_submenus"))

    selected_navigation = selected_settings.get("navigation", [])
    selected_navigation_heights = [
        value for value in selected_navigation
        if settings_config.normalize_choice(
            value,
            settings_config.SIZE_ALIASES
        ) in {"small", "medium", "large"}
    ]
    selected_navigation_positions = [
        value for value in selected_navigation
        if settings_config.normalize_choice(
            value,
            settings_config.POSITION_ALIASES
        ) in {"sticky", "static"}
    ]
    selected_navigation_aligns = [
        value for value in selected_navigation
        if settings_config.normalize_choice(
            value,
            settings_config.ALIGN_ALIASES
        ) in {"left", "center", "right"}
    ]

    if len(selected_navigation_heights) > 1:
        messages.append(tr(
            language,
            "navigation_multiple",
            selected=", ".join(selected_navigation_heights),
            first=selected_navigation_heights[0]
        ))

    if len(selected_navigation_positions) > 1:
        messages.append(tr(
            language,
            "navigation_multiple",
            selected=", ".join(selected_navigation_positions),
            first=selected_navigation_positions[0]
        ))

    if len(selected_navigation_aligns) > 1:
        messages.append(tr(
            language,
            "navigation_multiple",
            selected=", ".join(selected_navigation_aligns),
            first=selected_navigation_aligns[0]
        ))

    logo = settings.get("logo", {})

    if logo.get("enabled", False):
        align = logo.get("align", "left")
        selected_logo = selected_settings.get("logo", [])
        selected_logo_aligns = [
            value for value in selected_logo
            if settings_config.normalize_choice(
                value,
                settings_config.ALIGN_ALIASES
            ) in {"left", "right"}
        ]

        if not selected_logo_aligns:
            messages.append(tr(language, "logo_align"))

        elif align not in {"left", "center", "right"}:
            messages.append(tr(language, "logo_align"))
        elif align == "center":
            messages.append(tr(language, "logo_align_center"))

        src = logo.get("src", "").strip()
        folder = logo.get("folder", "logo").strip().strip("/") or "logo"

        if src:
            logo_file = CONTENT_DIR / MEDIA_DIR_NAME / folder / src

            if not logo_file.exists():
                logo_file = CONTENT_DIR / LEGACY_MEDIA_DIR_NAME / folder / src

            if not logo_file.exists():
                messages.append(tr(
                    language,
                    "logo_missing",
                    folder=folder,
                    src=src
                ))
        else:
            logo_dir = CONTENT_DIR / MEDIA_DIR_NAME / folder

            if not logo_dir.exists():
                logo_dir = CONTENT_DIR / LEGACY_MEDIA_DIR_NAME / folder

            logo_files = sorted([
                file.name for file in logo_dir.iterdir()
                if file.is_file() and file.suffix.lower() in IMAGE_EXTENSIONS
            ]) if logo_dir.exists() else []

            if not logo_files:
                messages.append(tr(
                    language,
                    "logo_folder_missing",
                    folder=folder
                ))
            elif len(logo_files) > 1:
                messages.append(tr(
                    language,
                    "logo_multiple",
                    files=", ".join(logo_files),
                    first=logo_files[0]
                ))

    gallery_layout = (
        settings
        .get("gallery", {})
        .get("layout", "square")
    )

    if gallery_layout not in {"square", "wide", "masonry"}:
        messages.append(tr(language, "gallery_layout"))

    icon_style = (
        settings
        .get("icons", {})
        .get("style", "round")
    )

    if icon_style not in {"round", "simple", "text"}:
        messages.append(tr(language, "icon_style"))

    font_size = (
        settings
        .get("typography", {})
        .get("size", "medium")
    )

    if font_size not in {"small", "medium", "large"}:
        messages.append(tr(language, "font"))


def validate_markdown_files(
    messages: list[str],
    language: str
):
    components = load_json(COMPONENTS_FILE)
    known_annotations = set(components.keys())
    known_pages = page_keys()
    known_page_suggestions = page_suggestions()

    for md_file in sorted(CONTENT_DIR.rglob("*.md")):
        if md_file == SETTINGS_MARKDOWN_FILE:
            continue

        if len(md_file.relative_to(CONTENT_DIR).parts) > 2:
            messages.append(tr(
                language,
                "nested_page",
                path=md_file.relative_to(ROOT_DIR)
            ))
            continue

        text = md_file.read_text(encoding="utf-8")
        display_path = md_file.relative_to(ROOT_DIR)

        for match in re.finditer(r"(?m)^@([^\s:]+)(?::([^\n]+))?", text):
            annotation = match.group(1).strip()
            parameter = (match.group(2) or "").strip()
            line = line_number(text, match.start())

            if annotation not in known_annotations:
                suggestion = difflib.get_close_matches(
                    annotation,
                    known_annotations,
                    n=1
                )

                hint = (
                    tr(
                        language,
                        "did_you_mean",
                        suggestion=suggestion[0]
                    )
                    if suggestion
                    else ""
                )

                messages.append(tr(
                    language,
                    "unknown_block",
                    path=display_path,
                    line=line,
                    annotation=annotation,
                    hint=hint
                ))

            component = components.get(annotation, annotation)

            if component == "gallery" and parameter:
                gallery_dir = CONTENT_DIR / MEDIA_DIR_NAME / GALLERY_DIR_NAME / parameter

                if not gallery_dir.exists():
                    gallery_dir = (
                        CONTENT_DIR
                        / LEGACY_MEDIA_DIR_NAME
                        / LEGACY_GALLERY_DIR_NAME
                        / parameter
                    )

                if not gallery_dir.exists():
                    messages.append(tr(
                        language,
                        "gallery_missing",
                        path=display_path,
                        line=line,
                        parameter=parameter,
                        hint=gallery_suggestion(parameter)
                    ))

            if component == "hero" and parameter:
                validate_media_parameter(
                    messages,
                    display_path,
                    line,
                    parameter,
                    md_file,
                    language
                )

        for block in annotated_blocks(text):
            component = components.get(block["annotation"], block["annotation"])
            h2_count = len(re.findall(r"(?m)^##\s+", block["content"]))

            if component == "event" and h2_count > 1:
                messages.append(tr(
                    language,
                    "event_single_multiple",
                    path=display_path,
                    line=block["line"],
                    annotation=block["annotation"]
                ))

            if component == "events" and h2_count < 2:
                messages.append(tr(
                    language,
                    "events_list_single",
                    path=display_path,
                    line=block["line"],
                    annotation=block["annotation"]
                ))

            if component == "person" and h2_count > 1:
                messages.append(tr(
                    language,
                    "person_single_multiple",
                    path=display_path,
                    line=block["line"],
                    annotation=block["annotation"]
                ))

            if component == "people" and h2_count < 2:
                messages.append(tr(
                    language,
                    "people_list_single",
                    path=display_path,
                    line=block["line"],
                    annotation=block["annotation"]
                ))

        for match in re.finditer(r"!\[(.*?)\]\((.*?)\)", text):
            src = match.group(2).strip()

            if is_external_url(src):
                continue

            file_path = content_relative_from_src(src, md_file)

            if not file_path.exists():
                messages.append(tr(
                    language,
                    "image_missing",
                    path=display_path,
                    line=line_number(text, match.start()),
                    src=src,
                    hint=media_suggestion(src)
                ))

        for match in re.finditer(r"\[\[(.*?)\]\]", text):
            label = match.group(1).strip()
            key = slugify_title(label)

            if key not in known_pages:
                suggestion = difflib.get_close_matches(
                    label,
                    known_page_suggestions,
                    n=1
                )
                hint = (
                    tr(
                        language,
                        "did_you_mean",
                        suggestion=suggestion[0]
                    )
                    if suggestion
                    else ""
                )

                messages.append(tr(
                    language,
                    "internal_link_missing",
                    path=display_path,
                    line=line_number(text, match.start()),
                    label=label,
                    hint=hint
                ))


def validate_media_parameter(
    messages: list[str],
    display_path: Path,
    line: int,
    parameter: str,
    md_file: Path,
    language: str
):
    parsed = urlparse(parameter)
    suffix = Path(parsed.path).suffix.lower()

    if is_external_url(parameter):
        return

    if suffix in IMAGE_EXTENSIONS or suffix in VIDEO_EXTENSIONS:
        file_path = content_relative_from_src(parameter, md_file)

        if not file_path.exists():
            messages.append(tr(
                language,
                "hero_missing",
                path=display_path,
                line=line,
                parameter=parameter
            ))


def collect_messages(format_settings: bool = True) -> tuple[list[str], str]:
    messages: list[str] = []

    if format_settings:
        settings_config.format_settings_file()

    language = language_from_settings()

    validate_settings(messages, language)
    validate_markdown_files(messages, language)

    return messages, language


def main() -> int:
    messages, language = collect_messages()

    if messages:
        print(tr(language, "found_header"))

        for message in messages:
            print(f"- {message}")

        print(tr(language, "found_footer"))

        return 1 if has_errors(messages) else 0

    print(tr(language, "ok"))

    return 0


def has_errors(messages: list[str]) -> bool:
    return any(
        not (
            message.startswith("WARNUNG:")
            or message.startswith("WARNING:")
        )
        for message in messages
    )


if __name__ == "__main__":
    raise SystemExit(main())
