"""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()