"""PyGitWeb project layout settings without importing pygitweb."""

from __future__ import annotations

import json
import os
from dataclasses import dataclass
from pathlib import Path

_DEFAULT_SETTINGS_PATH = Path.home() / ".pygitweb" / "settings.json"
_ENV_PREFIX = "PYGITWEB_"


@dataclass(frozen=True, slots=True)
class PygitwebLayout:
    projectroot: Path
    projects_list: Path
    project_maxdepth: int
    list_all: bool
    export_ok: str


def load_pygitweb_layout() -> PygitwebLayout:
    """Load project directory settings from ~/.pygitweb/settings.json and PYGITWEB_* env vars."""
    file_values = _load_settings_file(_settings_file_path())
    layout = _layout_from_mapping(file_values)
    return _apply_env_overrides(layout)


def _settings_file_path() -> Path:
    explicit = os.environ.get(f"{_ENV_PREFIX}SETTINGS_CONFIG", "").strip()
    if explicit:
        return Path(explicit).expanduser()
    return _DEFAULT_SETTINGS_PATH


def _load_settings_file(path: Path) -> dict[str, object]:
    if not path.is_file():
        return {}
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError):
        return {}
    return data if isinstance(data, dict) else {}


def _layout_from_mapping(values: dict[str, object]) -> PygitwebLayout:
    projectroot = _path_value(values.get("PROJECTROOT"), Path.home())
    projects_list_raw = values.get("PROJECTS_LIST")
    projects_list = projectroot if projects_list_raw in (None, "") else projectroot
    return PygitwebLayout(
        projectroot=projectroot,
        projects_list=projects_list if projects_list_raw is None else _path_value(projects_list_raw, projectroot),
        project_maxdepth=_int_value(values.get("PROJECT_MAXDEPTH"), 1),
        list_all=_bool_value(values.get("LIST_ALL"), True),
        export_ok=_str_value(values.get("EXPORT_OK"), ""),
    )


def _apply_env_overrides(layout: PygitwebLayout) -> PygitwebLayout:
    projectroot = _env_path(f"{_ENV_PREFIX}PROJECTROOT", layout.projectroot)
    projects_list = layout.projects_list
    if os.environ.get(f"{_ENV_PREFIX}PROJECTS_LIST", "").strip():
        projects_list = _env_path(f"{_ENV_PREFIX}PROJECTS_LIST", layout.projects_list)
    elif not layout.projects_list:
        projects_list = projectroot
    return PygitwebLayout(
        projectroot=projectroot,
        projects_list=projects_list,
        project_maxdepth=_env_int(f"{_ENV_PREFIX}PROJECT_MAXDEPTH", layout.project_maxdepth),
        list_all=_env_bool(f"{_ENV_PREFIX}LIST_ALL", layout.list_all),
        export_ok=_env_str(f"{_ENV_PREFIX}EXPORT_OK", layout.export_ok),
    )


def _path_value(value: object, default: Path) -> Path:
    if value is None or value == "":
        return default
    return Path(str(value)).expanduser()


def _str_value(value: object, default: str) -> str:
    if value is None:
        return default
    return str(value)


def _int_value(value: object, default: int) -> int:
    if value is None:
        return default
    if isinstance(value, bool):
        return int(value)
    if isinstance(value, int):
        return value
    if isinstance(value, str):
        try:
            return int(value.strip())
        except ValueError:
            return default
    if isinstance(value, float):
        return int(value)
    return default


def _bool_value(value: object, default: bool) -> bool:
    if value is None:
        return default
    if isinstance(value, bool):
        return value
    if isinstance(value, int):
        return value != 0
    if isinstance(value, str):
        normalized = value.strip().lower()
        if normalized in ("false", "0", "no", "off"):
            return False
        if normalized in ("true", "1", "yes", "on"):
            return True
    return default


def _env_str(name: str, default: str) -> str:
    raw = os.environ.get(name)
    if raw is None or raw.strip() == "":
        return default
    return raw


def _env_int(name: str, default: int) -> int:
    raw = os.environ.get(name)
    if raw is None or raw.strip() == "":
        return default
    try:
        return int(raw.strip())
    except ValueError:
        return default


def _env_bool(name: str, default: bool) -> bool:
    raw = os.environ.get(name)
    if raw is None or raw.strip() == "":
        return default
    return _bool_value(raw, default)


def _env_path(name: str, default: Path) -> Path:
    raw = os.environ.get(name)
    if raw is None or raw.strip() == "":
        return default
    return Path(raw.strip()).expanduser()