"""Shared configuration constants for the application."""

from __future__ import annotations

import contextlib
from pathlib import Path
from typing import Any, Callable

try:  # Python 3.11 and above
    import tomllib  # type: ignore[attr-defined]
except ModuleNotFoundError:  # pragma: no cover - fallback for <3.11
    with contextlib.suppress(ModuleNotFoundError):
        import tomli as tomllib  # type: ignore[assignment]
    if "tomllib" not in globals():
        tomllib = None  # type: ignore[assignment]

PREVIEW_MAX_SIZE = (900, 660)

BASE_DIR = Path(__file__).resolve().parents[2]
IMAGES_DIR = BASE_DIR / "images"
CONFIG_FILE = BASE_DIR / "config.toml"
LANG_DIR = BASE_DIR / "app" / "lang"

_DEFAULTS_BASE = {
    "hue_min": 250.0,
    "hue_max": 310.0,
    "sat_min": 15.0,
    "val_min": 15.0,
    "val_max": 100.0,
    "alpha": 120,
}

SUPPORTED_IMAGE_EXTENSIONS = (".webp", ".png", ".jpg", ".jpeg", ".bmp")
LANGUAGE_DEFAULT = "en"

_DEFAULT_TYPES: dict[str, Callable[[Any], Any]] = {
    "hue_min": float,
    "hue_max": float,
    "sat_min": float,
    "val_min": float,
    "val_max": float,
    "alpha": int,
}


def _load_config_data() -> dict[str, Any]:
    """Read the optional config file once and return its parsed data."""
    if tomllib is None or not CONFIG_FILE.exists():
        return {}
    decode_error = getattr(tomllib, "TOMLDecodeError", ValueError)  # type: ignore[attr-defined]
    try:
        with CONFIG_FILE.open("rb") as handle:
            data = tomllib.load(handle)
    except (OSError, AttributeError, decode_error, TypeError):  # type: ignore[arg-type]
        return {}
    if not isinstance(data, dict):
        return {}
    return data


def _extract_default_overrides(data: dict[str, Any]) -> dict[str, Any]:
    settings = data.get("defaults")
    if not isinstance(settings, dict):
        return {}
    overrides: dict[str, Any] = {}
    for key, caster in _DEFAULT_TYPES.items():
        if key not in settings:
            continue
        try:
            overrides[key] = caster(settings[key])
        except (TypeError, ValueError):
            continue
    return overrides


def _available_languages() -> set[str]:
    languages = {path.stem.lower() for path in LANG_DIR.glob("*.toml")}
    if not languages:
        languages.add(LANGUAGE_DEFAULT)
    return languages


def _extract_language(data: dict[str, Any]) -> str:
    value = data.get("language")
    supported = _available_languages()
    if isinstance(value, str):
        normalised = value.strip().lower()
        if normalised in supported:
            return normalised
    if LANGUAGE_DEFAULT in supported:
        return LANGUAGE_DEFAULT
    return sorted(supported)[0]


_CONFIG_DATA = _load_config_data()

_OPTION_DEFAULTS = {"reset_exclusions_on_image_change": False, "overlay_color": "#ff0000"}


def _extract_options(data: dict[str, Any]) -> dict[str, Any]:
    section = data.get("options")
    if not isinstance(section, dict):
        return {}
    result: dict[str, Any] = {}
    value = section.get("reset_exclusions_on_image_change")
    if isinstance(value, bool):
        result["reset_exclusions_on_image_change"] = value
    color = section.get("overlay_color")
    if isinstance(color, str) and color.startswith("#") and len(color) in (7, 9):
        result["overlay_color"] = color
    return result


DEFAULTS = {**_DEFAULTS_BASE, **_extract_default_overrides(_CONFIG_DATA)}
LANGUAGE = _extract_language(_CONFIG_DATA)
OPTIONS = {**_OPTION_DEFAULTS, **_extract_options(_CONFIG_DATA)}
RESET_EXCLUSIONS_ON_IMAGE_CHANGE = OPTIONS["reset_exclusions_on_image_change"]
OVERLAY_COLOR = OPTIONS["overlay_color"]
