"""
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 collections.abc import Iterator
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: object, name: str, value: object) -> None:
        original(self, name, value)
        if not _persist_enabled or _batch_update:
            return
        if name not in Settings.model_fields or not isinstance(self, Settings):
            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) -> Iterator[None]:
    """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)