"""
PyGitWeb settings file: ~/.pygitweb/settings.json (or PYGITWEB_SETTINGS_CONFIG).

On first run, loads from environment and writes the file. When the file exists,
loads from JSON then applies explicit environment/.env overrides (with a warning).
Changes via the settings UI or setattr persist back to the file.
"""

from __future__ import annotations

import json
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
	from pygitweb.config import Settings

DEFAULT_SETTINGS_CONFIG_PATH = Path.home() / ".pygitweb" / "settings.json"

_persist_enabled = False
_batch_update = False
_active_path: Path | None = None
_setattr_patched = False


def settings_config_path() -> Path:
	explicit = __import__("os").environ.get("PYGITWEB_SETTINGS_CONFIG", "").strip()
	if explicit:
		return Path(explicit).expanduser()
	return DEFAULT_SETTINGS_CONFIG_PATH


def write_settings_file(settings: Settings, path: Path | None = None) -> None:
	target = path or _active_path or settings_config_path()
	target.parent.mkdir(parents=True, exist_ok=True)
	target.write_text(
		json.dumps(settings.model_dump(mode="json"), indent=2) + "\n",
		encoding="utf-8",
	)


def _warn_env_overrides(path: Path, fields: list[str]) -> None:
	names = ", ".join(fields)
	print(
		f"WARNING: PyGitWeb: environment overrides settings file {path}\n         Overridden field(s): {names}\n",
		file=sys.stderr,
	)


def _apply_env_overrides(base: Settings) -> list[str]:
	from pygitweb.config import Settings

	env_settings = Settings()
	overrides: list[str] = []
	for name in env_settings.model_fields_set:
		env_val = getattr(env_settings, name)
		if getattr(base, name) == env_val:
			continue
		setattr(base, name, env_val)
		overrides.append(name)
	return overrides


def _patch_settings_setattr() -> None:
	global _setattr_patched
	if _setattr_patched:
		return

	from pygitweb.config import Settings

	original = Settings.__setattr__

	def _setattr(self: Settings, name: str, value: object) -> None:
		original(self, name, value)
		if not _persist_enabled or _batch_update:
			return
		if name not in Settings.model_fields:
			return
		write_settings_file(self)

	Settings.__setattr__ = _setattr  # type: ignore[method-assign]
	_setattr_patched = True


def init_settings() -> Settings:
	from pygitweb.config import Settings, _finalize_auth_settings

	global _persist_enabled, _active_path

	_persist_enabled = False
	path = settings_config_path()
	_active_path = path

	if path.is_file():
		settings = Settings.model_validate_json(path.read_text(encoding="utf-8"))
		overrides = _apply_env_overrides(settings)
		if overrides:
			_warn_env_overrides(path, overrides)
	else:
		settings = Settings()

	_finalize_auth_settings(settings)
	write_settings_file(settings, path)
	_patch_settings_setattr()
	_persist_enabled = True
	return settings


@contextmanager
def settings_batch_update(settings: Settings):
	"""Apply multiple setting changes and persist once."""
	global _batch_update

	_batch_update = True
	try:
		yield
	finally:
		_batch_update = False
		if _persist_enabled:
			write_settings_file(settings)