from __future__ import annotations

from pathlib import Path
import json
import re


ROOT_DIR = Path(__file__).resolve().parent.parent
CONTENT_DIR = ROOT_DIR / "havelseite"
LEGACY_CONTENT_DIR = ROOT_DIR / "inhalt"
OLD_CONTENT_DIR = ROOT_DIR / "content"
GENERATOR_DIR = ROOT_DIR / "generator"
SETTINGS_JSON_FILE = GENERATOR_DIR / "settings.json"
SETTINGS_MARKDOWN_FILE = CONTENT_DIR / "einstellungen.md"
LEGACY_SETTINGS_MARKDOWN_FILE = LEGACY_CONTENT_DIR / "einstellungen.md"
OLD_SETTINGS_MARKDOWN_FILE = OLD_CONTENT_DIR / "settings.md"
THEMES_DIR = GENERATOR_DIR / "themes"
MEDIA_DIR_NAME = "medien"
LEGACY_MEDIA_DIR_NAME = "assets"

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


def load_color_themes() -> dict:
    themes = {}

    if not THEMES_DIR.exists():
        return themes

    for file in sorted(THEMES_DIR.glob("*.json")):
        with file.open(encoding="utf-8") as f:
            themes[file.stem] = json.load(f)

    return themes


COLOR_THEMES = load_color_themes()

THEME_ORDER = [
    "havel",
    "wasser",
    "wald",
    "sonnenuntergang",
    "dunkel",
    "kueste",
    "segel",
    "leuchtturm"
]

THEME_LABELS = {
    "dunkel": "Dunkel",
    "havel": "Havel",
    "kueste": "Küste",
    "leuchtturm": "Leuchtturm",
    "segel": "Segel",
    "sonnenuntergang": "Sonnenuntergang",
    "wald": "Wald",
    "wasser": "Wasser"
}

THEME_ALIASES = {
    "ocean": "wasser",
    "forest": "wald",
    "sunset": "sonnenuntergang",
    "dark": "dunkel"
}


DEFAULT_SETTINGS = {
    "title": "Havelseiten",
    "language": "de",
    "validator_language": "de",
    "theme": "havel",
    "navigation": {
        "height": "medium",
        "position": "sticky",
        "align": "center"
    },
    "mobile_menu": {
        "align": "auto",
        "submenus": "closed"
    },
    "logo": {
        "enabled": True,
        "header": True,
        "footer": False,
        "align": "left",
        "folder": "logo",
        "src": "",
        "alt": "Märkischer Seglerverein Beetzsee",
        "text": "Havelseiten"
    },
    "gallery": {
        "layout": "square",
        "captions": True,
        "rounded": True
    },
    "icons": {
        "style": "round"
    },
    "privacy": {
        "external_content": False
    },
    "typography": {
        "size": "medium"
    },
    "social": {},
    "colors": {}
}


SETTING_ALIASES = {
    "seite": "site",
    "website": "site",
    "allgemein": "site",
    "sprache": "language",
    "language": "language",
    "pruefung": "language",
    "pruefsprache": "language",
    "prüfung": "language",
    "design": "theme",
    "farbpalette": "theme",
    "theme": "theme",
    "farben": "colors",
    "farbe": "colors",
    "navigation": "navigation",
    "nav": "navigation",
    "mobilmenue": "mobile_menu",
    "mobilmenu": "mobile_menu",
    "mobile_menu": "mobile_menu",
    "burger": "mobile_menu",
    "logo": "logo",
    "logo_datei": "logo_file",
    "logodatei": "logo_file",
    "logo-datei": "logo_file",
    "logo_file": "logo_file",
    "galerie": "gallery",
    "gallery": "gallery",
    "icons": "icons",
    "symbole": "icons",
    "schrift": "typography",
    "schriftart": "typography",
    "typography": "typography",
    "social": "social",
    "soziales": "social",
    "soziale_medien": "social",
    "bilder": "images",
    "images": "images",
    "datenschutz": "privacy",
    "privacy": "privacy",
    "externe_inhalte": "privacy"
}

SIZE_ALIASES = {
    "small": "small",
    "klein": "small",
    "medium": "medium",
    "mittel": "medium",
    "large": "large",
    "gross": "large",
    "groß": "large"
}

POSITION_ALIASES = {
    "sticky": "sticky",
    "bleibt_oben": "sticky",
    "oben_bleiben": "sticky",
    "klebt_oben": "sticky",
    "static": "static",
    "scrollt_mit": "static",
    "normal": "static"
}

ALIGN_ALIASES = {
    "left": "left",
    "links": "left",
    "center": "center",
    "mitte": "center",
    "mittig": "center",
    "right": "right",
    "rechts": "right"
}

MOBILE_MENU_ALIASES = {
    "auto": "auto",
    "automatisch": "auto",
    "left": "left",
    "links": "left",
    "center": "center",
    "mitte": "center",
    "mittig": "center",
    "right": "right",
    "rechts": "right"
}

MOBILE_SUBMENU_ALIASES = {
    "open": "open",
    "offen": "open",
    "aufgeklappt": "open",
    "ausgeklappt": "open",
    "untermenues_offen": "open",
    "untermenus_offen": "open",
    "untermenues_ausgeklappt": "open",
    "untermenus_ausgeklappt": "open",
    "expanded": "open",
    "closed": "closed",
    "geschlossen": "closed",
    "eingeklappt": "closed",
    "einklappbar": "closed",
    "untermenues_einklappen": "closed",
    "untermenus_einklappen": "closed",
    "untermenues_einklappbar": "closed",
    "untermenus_einklappbar": "closed",
    "collapsed": "closed"
}

GALLERY_LAYOUT_ALIASES = {
    "square": "square",
    "quadratisch": "square",
    "wide": "wide",
    "breit": "wide",
    "masonry": "masonry",
    "kacheln": "masonry"
}

ICON_STYLE_ALIASES = {
    "round": "round",
    "rund": "round",
    "simple": "simple",
    "schlicht": "simple",
    "text": "text"
}

FONT_ALIASES = {
    "system": "system",
    "standard": "system",
    "modern": "modern",
    "klassisch": "classic",
    "classic": "classic",
    "freundlich": "friendly",
    "friendly": "friendly"
}

FONT_SIZE_ALIASES = {
    "small": "small",
    "klein": "small",
    "medium": "medium",
    "mittel": "medium",
    "normal": "medium",
    "large": "large",
    "gross": "large",
    "groß": "large"
}

CHOICE_SETTINGS = {
    "language",
    "navigation",
    "logo_file",
    "mobile_menu",
    "icons",
    "typography"
}

OPTIONAL_CHECKBOX_OPTIONS = {
    "untermenues_ausgeklappt",
    "untermenus_ausgeklappt"
}


def merge_settings(defaults: dict, custom: dict) -> dict:
    merged = defaults.copy()

    for key, value in custom.items():
        if (
            isinstance(value, dict)
            and isinstance(merged.get(key), dict)
        ):
            merged[key] = merge_settings(merged[key], value)
        else:
            merged[key] = value

    return merged


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

    return value.strip("_")


def normalize_choice(value: str, aliases: dict[str, str]) -> str:
    value_slug = slug(value)

    return aliases.get(value_slug, value_slug)


def active_content_dir() -> Path:
    if CONTENT_DIR.exists():
        return CONTENT_DIR

    if LEGACY_CONTENT_DIR.exists():
        return LEGACY_CONTENT_DIR

    return OLD_CONTENT_DIR


def active_settings_file() -> Path | None:
    if SETTINGS_MARKDOWN_FILE.exists():
        return SETTINGS_MARKDOWN_FILE

    if LEGACY_SETTINGS_MARKDOWN_FILE.exists():
        return LEGACY_SETTINGS_MARKDOWN_FILE

    if OLD_SETTINGS_MARKDOWN_FILE.exists():
        return OLD_SETTINGS_MARKDOWN_FILE

    return None


def parse_bool(value: str) -> bool:
    return slug(value) in {"true", "yes", "ja", "enabled", "aktiv", "an", "anzeigen"}


def selected_options(lines: list[str]) -> list[str]:
    selected = []

    for line in lines:
        match = re.match(r"^-\s*\[(.*?)\]\s*(.+)$", line.strip())

        if match and "x" in match.group(1).lower():
            selected.append(match.group(2).strip())

    return selected


def setting_sections(text: str) -> list[tuple[str, list[str]]]:
    sections = []
    current_name = None
    current_lines = []

    for line in text.splitlines():
        match = re.match(r"^@(setting|einstellung):(.+)$", line.strip(), re.I)

        if match:
            if current_name:
                sections.append((current_name, current_lines))

            current_name = slug(match.group(2))
            current_lines = []
        elif current_name:
            current_lines.append(line)

    if current_name:
        sections.append((current_name, current_lines))

    return sections


def selected_by_section(text: str) -> dict[str, list[str]]:
    return {
        SETTING_ALIASES.get(name, name): selected_options(lines)
        for name, lines in setting_sections(text)
    }


def theme_keys() -> list[str]:
    known = [key for key in THEME_ORDER if key in COLOR_THEMES]
    extra = sorted(key for key in COLOR_THEMES if key not in THEME_ORDER)

    return known + extra


def display_theme_name(key: str) -> str:
    if key in THEME_LABELS:
        return THEME_LABELS[key]

    return key.replace("_", " ").title()


def normalize_checkbox_lines(text: str) -> str:
    lines = []

    for line in text.splitlines():
        match = re.match(r"^(\s*-\s*)\[(.*?)\](.*)$", line)

        if match:
            marker = "x" if "x" in match.group(2).lower() else " "
            lines.append(f"{match.group(1)}[{marker}]{match.group(3)}")
        else:
            lines.append(line.rstrip())

    return "\n".join(lines).rstrip() + "\n"


def refresh_theme_options(text: str) -> str:
    lines = text.splitlines()
    formatted = []
    index = 0

    while index < len(lines):
        line = lines[index]
        match = re.match(r"^@(setting|einstellung):(.+)$", line.strip(), re.I)
        setting_name = None

        if match:
            setting_name = SETTING_ALIASES.get(slug(match.group(2)), slug(match.group(2)))

        if setting_name != "theme":
            formatted.append(line)
            index += 1
            continue

        formatted.append(line)
        index += 1
        section_lines = []

        while index < len(lines):
            next_line = lines[index]

            if re.match(r"^@(setting|einstellung):(.+)$", next_line.strip(), re.I):
                break

            section_lines.append(next_line)
            index += 1

        selected_themes = {
            normalize_theme(option)
            for option in selected_options(section_lines)
        }

        if not selected_themes:
            selected_themes = {DEFAULT_SETTINGS["theme"]}

        for key in theme_keys():
            marker = "x" if key in selected_themes else " "
            formatted.append(f"- [{marker}] {display_theme_name(key)}")

        if index < len(lines):
            formatted.append("")

    return "\n".join(formatted).rstrip() + "\n"


def logo_folder_from_text(text: str) -> str:
    for raw_name, lines in setting_sections(text):
        name = SETTING_ALIASES.get(raw_name, raw_name)

        if name != "logo":
            continue

        values = key_values(lines)

        for key in ["logoordner", "logo_ordner", "ordner"]:
            if key in values:
                return values[key].strip().strip("/") or DEFAULT_SETTINGS["logo"]["folder"]

    return DEFAULT_SETTINGS["logo"]["folder"]


def logo_files(folder: str) -> list[str]:
    content_dir = active_content_dir()
    logo_dir = content_dir / MEDIA_DIR_NAME / folder

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

    if not logo_dir.exists():
        return []

    return sorted([
        file.name for file in logo_dir.iterdir()
        if file.is_file() and file.suffix.lower() in IMAGE_EXTENSIONS
    ])


def logo_file_lines(files: list[str], selected: list[str]) -> list[str]:
    selected_files = {file for file in selected if file in files}

    if not selected_files and files:
        selected_files = {files[0]}

    return [
        f"- [{'x' if file in selected_files else ' '}] {file}"
        for file in files
    ]


def refresh_logo_file_options(text: str) -> str:
    files = logo_files(logo_folder_from_text(text))

    if not files:
        return text

    lines = text.splitlines()
    formatted = []
    index = 0
    has_logo_file_section = any(
        SETTING_ALIASES.get(raw_name, raw_name) == "logo_file"
        for raw_name, _ in setting_sections(text)
    )

    while index < len(lines):
        line = lines[index]
        match = re.match(r"^@(setting|einstellung):(.+)$", line.strip(), re.I)
        setting_name = None

        if match:
            setting_name = SETTING_ALIASES.get(slug(match.group(2)), slug(match.group(2)))

        if setting_name == "logo_file":
            formatted.append("@einstellung:logo-datei")
            index += 1
            section_lines = []

            while index < len(lines):
                next_line = lines[index]

                if re.match(r"^@(setting|einstellung):(.+)$", next_line.strip(), re.I):
                    break

                section_lines.append(next_line)
                index += 1

            formatted.extend(logo_file_lines(files, selected_options(section_lines)))

            if index < len(lines) and formatted[-1] != "":
                formatted.append("")

            continue

        formatted.append(line)
        index += 1

        if setting_name == "logo" and not has_logo_file_section:
            while index < len(lines):
                next_line = lines[index]

                if re.match(r"^@(setting|einstellung):(.+)$", next_line.strip(), re.I):
                    break

                formatted.append(next_line)
                index += 1

            if formatted[-1] != "":
                formatted.append("")
            formatted.append("@einstellung:logo-datei")
            formatted.extend(logo_file_lines(files, []))

            if index < len(lines) and formatted[-1] != "":
                formatted.append("")

    return "\n".join(formatted).rstrip() + "\n"


def checkbox_group_has_choice(lines: list[str]) -> bool:
    return any(
        re.match(r"^\s*-\s*\[x\]", line, re.I)
        for line in lines
    )


def checkbox_group_is_optional(lines: list[str]) -> bool:
    options = [
        slug(match.group(1))
        for line in lines
        if (match := re.match(r"^\s*-\s*\[(?:.*?)\]\s*(.+)$", line))
    ]

    return bool(options) and all(
        option in OPTIONAL_CHECKBOX_OPTIONS
        for option in options
    )


def default_first_option(text: str) -> str:
    lines = text.splitlines()
    formatted = []
    section_name = None
    checkbox_group = []

    def flush_group():
        nonlocal checkbox_group

        if not checkbox_group:
            return

        should_default = (
            section_name in CHOICE_SETTINGS
            and not checkbox_group_has_choice(checkbox_group)
            and not checkbox_group_is_optional(checkbox_group)
        )

        for offset, group_line in enumerate(checkbox_group):
            if should_default and offset == 0:
                formatted.append(re.sub(r"\[(.*?)\]", "[x]", group_line, count=1))
            else:
                formatted.append(group_line)

        checkbox_group = []

    for line in lines:
        section_match = re.match(r"^@(setting|einstellung):(.+)$", line.strip(), re.I)
        checkbox_match = re.match(r"^\s*-\s*\[(.*?)\]", line)

        if section_match:
            flush_group()
            section_name = SETTING_ALIASES.get(
                slug(section_match.group(2)),
                slug(section_match.group(2))
            )
            formatted.append(line)
            continue

        if checkbox_match:
            checkbox_group.append(line)
            continue

        flush_group()
        formatted.append(line)

    flush_group()

    return "\n".join(formatted).rstrip() + "\n"


def format_settings_markdown(text: str) -> str:
    text = normalize_checkbox_lines(text)
    text = refresh_theme_options(text)
    text = refresh_logo_file_options(text)

    return default_first_option(text)


def format_settings_file() -> bool:
    settings_file = active_settings_file()

    if not settings_file:
        return False

    original = settings_file.read_text(encoding="utf-8")
    formatted = format_settings_markdown(original)

    if formatted == original:
        return False

    settings_file.write_text(formatted, encoding="utf-8")
    return True


def key_values(lines: list[str]) -> dict:
    values = {}

    for line in lines:
        match = re.match(r"^([A-Za-zÄÖÜäöüß _-]+):\s*(.+)$", line.strip())

        if match:
            values[slug(match.group(1))] = match.group(2).strip()

    return values


def normalize_theme(value: str) -> str:
    theme = slug(value)

    return THEME_ALIASES.get(theme, theme)


def selected_slugs(selected: list[str]) -> set[str]:
    return {slug(value) for value in selected}


def selected_first(selected: list[str], aliases: dict[str, str]) -> str | None:
    for value in selected:
        normalized = normalize_choice(value, aliases)

        if normalized in aliases.values():
            return normalized

    return None


def parse_settings_markdown(text: str) -> dict:
    settings = {}

    for raw_name, lines in setting_sections(text):
        name = SETTING_ALIASES.get(raw_name, raw_name)
        values = key_values(lines)
        selected = selected_options(lines)
        selected_names = selected_slugs(selected)

        if name == "site":
            if "title" in values:
                settings["title"] = values["title"]
            if "titel" in values:
                settings["title"] = values["titel"]
            if "language" in values:
                settings["language"] = normalize_choice(values["language"], {"deutsch": "de", "english": "en", "englisch": "en"})
            if "sprache" in values:
                settings["language"] = normalize_choice(values["sprache"], {"deutsch": "de", "english": "en", "englisch": "en"})
            if "validator_language" in values:
                settings["validator_language"] = normalize_choice(values["validator_language"], {"deutsch": "de", "english": "en", "englisch": "en"})
            if "pruefsprache" in values:
                settings["validator_language"] = normalize_choice(values["pruefsprache"], {"deutsch": "de", "english": "en", "englisch": "en"})

        elif name == "language":
            language = selected_first(
                selected,
                {"deutsch": "de", "english": "en", "englisch": "en"}
            )

            if language:
                settings["language"] = language
                settings["validator_language"] = language

        elif name == "theme":
            if selected:
                settings["theme"] = normalize_theme(selected[0])

        elif name == "colors":
            settings["colors"] = values

        elif name == "navigation":
            navigation = {}

            height = selected_first(selected, SIZE_ALIASES)
            position = selected_first(selected, POSITION_ALIASES)
            align = selected_first(selected, ALIGN_ALIASES)

            if height:
                navigation["height"] = height
            if position:
                navigation["position"] = position
            if align:
                navigation["align"] = align
            if "height" in values:
                navigation["height"] = normalize_choice(values["height"], SIZE_ALIASES)
            if "hoehe" in values:
                navigation["height"] = normalize_choice(values["hoehe"], SIZE_ALIASES)
            if "position" in values:
                navigation["position"] = normalize_choice(values["position"], POSITION_ALIASES)
            if "verhalten" in values:
                navigation["position"] = normalize_choice(values["verhalten"], POSITION_ALIASES)
            if "ausrichtung" in values:
                navigation["align"] = normalize_choice(values["ausrichtung"], ALIGN_ALIASES)
            legacy_position = normalize_choice(selected[0], POSITION_ALIASES) if selected else None

            if legacy_position in {"sticky", "static"} and not position and not height:
                navigation["position"] = legacy_position

            settings["navigation"] = navigation

        elif name == "logo":
            logo = {}

            header_selected = any(
                value in selected_names
                for value in {"in_der_navigation", "navigation", "header", "oben"}
            )
            footer_selected = any(
                value in selected_names
                for value in {"in_der_fusszeile", "im_footer", "footer", "fusszeile", "unten"}
            )

            logo["enabled"] = header_selected or footer_selected
            logo["header"] = header_selected
            logo["footer"] = footer_selected

            allowed_aligns = {"left", "right"}
            first_allowed = next(
                (
                    normalize_choice(value, ALIGN_ALIASES)
                    for value in selected
                    if normalize_choice(value, ALIGN_ALIASES) in allowed_aligns
                ),
                None
            )

            if first_allowed:
                logo["align"] = first_allowed

            for key in ["src", "alt", "text"]:
                if key in values:
                    logo[key] = values[key]

            if "datei" in values:
                logo["src"] = values["datei"]
            if "logoordner" in values:
                logo["folder"] = values["logoordner"]
            if "logo_ordner" in values:
                logo["folder"] = values["logo_ordner"]
            if "ordner" in values:
                logo["folder"] = values["ordner"]
            if "alternativtext" in values:
                logo["alt"] = values["alternativtext"]
            if "ausrichtung" in values:
                logo["align"] = normalize_choice(values["ausrichtung"], ALIGN_ALIASES)
            if "header" in values:
                logo["header"] = parse_bool(values["header"])
            if "navigation" in values:
                logo["header"] = parse_bool(values["navigation"])
            if "footer" in values:
                logo["footer"] = parse_bool(values["footer"])
            if "fusszeile" in values:
                logo["footer"] = parse_bool(values["fusszeile"])

            settings["logo"] = logo

        elif name == "logo_file":
            if selected:
                settings.setdefault("logo", {})
                settings["logo"]["src"] = selected[0]

        elif name == "mobile_menu":
            mobile_menu = {}
            align = selected_first(selected, MOBILE_MENU_ALIASES)

            if align:
                mobile_menu["align"] = align
            mobile_menu["submenus"] = "open" if any(
                slug(value) in OPTIONAL_CHECKBOX_OPTIONS
                for value in selected
            ) else "closed"
            if "ausrichtung" in values:
                mobile_menu["align"] = normalize_choice(
                    values["ausrichtung"],
                    MOBILE_MENU_ALIASES
                )
            if "untermenues" in values:
                mobile_menu["submenus"] = normalize_choice(
                    values["untermenues"],
                    MOBILE_SUBMENU_ALIASES
                )
            if "untermenus" in values:
                mobile_menu["submenus"] = normalize_choice(
                    values["untermenus"],
                    MOBILE_SUBMENU_ALIASES
                )
            if "submenus" in values:
                mobile_menu["submenus"] = normalize_choice(
                    values["submenus"],
                    MOBILE_SUBMENU_ALIASES
                )

            settings["mobile_menu"] = mobile_menu

        elif name == "gallery":
            gallery = {}

            if selected:
                gallery["layout"] = normalize_choice(selected[0], GALLERY_LAYOUT_ALIASES)
            if "layout" in values:
                gallery["layout"] = normalize_choice(values["layout"], GALLERY_LAYOUT_ALIASES)
            if "darstellung" in values:
                gallery["layout"] = normalize_choice(values["darstellung"], GALLERY_LAYOUT_ALIASES)
            if "bildunterschriften" in values:
                gallery["captions"] = parse_bool(values["bildunterschriften"])

            settings["gallery"] = gallery

        elif name == "images":
            gallery = {}

            if "bildunterschriften" in selected_names:
                gallery["captions"] = True
            if "keine_bildunterschriften" in selected_names:
                gallery["captions"] = False
            if "runde_ecken" in selected_names:
                gallery["rounded"] = True
            if "eckige_bilder" in selected_names or "keine_runden_ecken" in selected_names:
                gallery["rounded"] = False

            if "bildunterschriften" in values:
                gallery["captions"] = parse_bool(values["bildunterschriften"])
            if "runde_ecken" in values:
                gallery["rounded"] = parse_bool(values["runde_ecken"])

            settings["gallery"] = gallery

        elif name == "icons":
            icons = {}

            if selected:
                icons["style"] = normalize_choice(selected[0], ICON_STYLE_ALIASES)
            if "stil" in values:
                icons["style"] = normalize_choice(values["stil"], ICON_STYLE_ALIASES)
            if "style" in values:
                icons["style"] = normalize_choice(values["style"], ICON_STYLE_ALIASES)

            settings["icons"] = icons

        elif name == "privacy":
            privacy = {}

            if "externe_inhalte_laden" in selected_names:
                privacy["external_content"] = True
            if "externe_inhalte_nicht_laden" in selected_names:
                privacy["external_content"] = False
            if "external_content" in values:
                privacy["external_content"] = parse_bool(values["external_content"])
            if "externe_inhalte" in values:
                privacy["external_content"] = parse_bool(values["externe_inhalte"])

            settings["privacy"] = privacy

        elif name == "typography":
            typography = {}
            size = selected_first(selected, FONT_SIZE_ALIASES)

            if size:
                typography["size"] = size
            elif selected:
                typography["font"] = normalize_choice(selected[0], FONT_ALIASES)

            if "groesse" in values:
                typography["size"] = normalize_choice(values["groesse"], FONT_SIZE_ALIASES)
            if "größe" in values:
                typography["size"] = normalize_choice(values["größe"], FONT_SIZE_ALIASES)
            if "size" in values:
                typography["size"] = normalize_choice(values["size"], FONT_SIZE_ALIASES)
            if "schriftart" in values:
                typography["font"] = normalize_choice(values["schriftart"], FONT_ALIASES)
            if "font" in values:
                typography["font"] = normalize_choice(values["font"], FONT_ALIASES)

            settings["typography"] = typography

        elif name == "social":
            settings["social"] = values

    return settings


def load_json_settings() -> dict:
    if not SETTINGS_JSON_FILE.exists():
        return {}

    with SETTINGS_JSON_FILE.open(encoding="utf-8") as f:
        return json.load(f)


def load_markdown_settings() -> dict:
    settings_file = active_settings_file()

    if not settings_file:
        return {}

    return parse_settings_markdown(
        settings_file.read_text(encoding="utf-8")
    )


def apply_theme(settings: dict) -> dict:
    theme = normalize_theme(settings.get("theme", "havel"))
    fallback = next(iter(COLOR_THEMES.values()), {})
    theme_colors = COLOR_THEMES.get(theme, fallback)
    custom_colors = settings.get("colors", {})

    settings["theme"] = theme
    settings["colors"] = {
        **theme_colors,
        **custom_colors
    }

    return settings


def apply_typography(settings: dict) -> dict:
    font = settings.get("typography", {}).get("font", "system")
    size = settings.get("typography", {}).get("size", "medium")
    stacks = {
        "system": {
            "body": "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
            "heading": "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
        },
        "modern": {
            "body": "'Avenir Next', Avenir, 'Segoe UI', system-ui, sans-serif",
            "heading": "'Avenir Next', Avenir, 'Segoe UI', system-ui, sans-serif"
        },
        "classic": {
            "body": "Georgia, 'Times New Roman', serif",
            "heading": "Georgia, 'Times New Roman', serif"
        },
        "friendly": {
            "body": "'Trebuchet MS', 'Arial Rounded MT Bold', Verdana, system-ui, sans-serif",
            "heading": "'Trebuchet MS', 'Arial Rounded MT Bold', Verdana, system-ui, sans-serif"
        }
    }

    settings["typography"] = {
        **settings.get("typography", {}),
        "size": size,
        **stacks.get(font, stacks["system"])
    }

    return settings


def load_settings() -> dict:
    if active_settings_file():
        settings = merge_settings(
            DEFAULT_SETTINGS,
            load_markdown_settings()
        )
    else:
        settings = merge_settings(
            DEFAULT_SETTINGS,
            load_json_settings()
        )

    return apply_typography(apply_theme(settings))
