"""Config handling for textual-everforest."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Final, Any

import tomllib

from textual_everforest.themes import EVERFOREST_THEMES

if TYPE_CHECKING:
    from textual.app import App

CONFIG_FILENAME: Final[str] = "textual-everforest.toml"
THEME_SECTION: Final[str] = "theme"
DEFAULT_KEY: Final[str] = "default"


@dataclass(frozen=True)
class ThemeConfig:
    """Theme configuration loaded from disk.

    Attributes:
        default_theme: Theme name to apply (Textual theme name).
    """

    default_theme: str


def resolve_theme_name(value: str) -> str:
    """Resolve a user-provided theme string into a full theme name.

    Accepted values:
    - full theme names: everforest-dark-hard, etc.
    - shorthands: dark, light
    - variants: dark-hard, dark-medium, dark-soft, light-hard, light-medium, light-soft

    Raises:
        ValueError: If the value cannot be resolved to a known theme.
    """
    normalized = value.strip().lower()
    if normalized in {"dark", "light"}:
        normalized = f"{normalized}-hard"
    if not normalized.startswith("everforest-"):
        normalized = f"everforest-{normalized}"

    known = {theme.name for theme in EVERFOREST_THEMES}
    if normalized not in known:
        known_list = ", ".join(sorted(known))
        raise ValueError(f"Unknown theme '{value}'. Expected one of: {known_list}")
    return normalized


def default_config_path(base_dir: Path | None = None) -> Path:
    """Return the default config path for a project directory."""
    root = base_dir if base_dir is not None else Path.cwd()
    return root / CONFIG_FILENAME


def load_theme_config(path: Path) -> ThemeConfig:
    """Load a ThemeConfig from a TOML file.

    Raises:
        FileNotFoundError: If the path does not exist.
        ValueError: If required keys are missing or invalid.
    """
    if not path.exists():
        raise FileNotFoundError(path)
    data = tomllib.loads(path.read_text(encoding="utf-8"))
    section = data.get(THEME_SECTION)
    if not isinstance(section, dict):
        raise ValueError(f"Missing [{THEME_SECTION}] section in {path}")
    raw_default = section.get(DEFAULT_KEY)
    if not isinstance(raw_default, str) or not raw_default.strip():
        raise ValueError(f"Missing '{DEFAULT_KEY}' in [{THEME_SECTION}] in {path}")
    return ThemeConfig(default_theme=resolve_theme_name(raw_default))


def write_theme_config(path: Path, theme_value: str) -> ThemeConfig:
    """Write a ThemeConfig to disk and return the resolved config."""
    theme_name = resolve_theme_name(theme_value)
    contents = f'[{THEME_SECTION}]\n{DEFAULT_KEY} = "{theme_name}"\n'
    path.write_text(contents, encoding="utf-8")
    return ThemeConfig(default_theme=theme_name)


def apply_theme_from_config(
    app: "App[Any]",
    path: Path | None = None,
    *,
    strict: bool = False,
) -> bool:
    """Apply a default theme from config to a Textual app.

    Args:
        app: Textual app instance to update.
        path: Optional config path. Defaults to cwd + CONFIG_FILENAME.
        strict: If True, raise when config is missing or invalid.

    Returns:
        True when a theme was applied, False when skipped.
    """
    config_path = path if path is not None else default_config_path()
    try:
        config = load_theme_config(config_path)
    except (FileNotFoundError, ValueError):
        if strict:
            raise
        return False
    app.theme = config.default_theme
    return True
