"""
nterm Settings — persistent user preferences.

Stores zoom level, contrast mode, and UI font scale in ~/.nterm/settings.json.
Thread-safe reads and writes. Values survive app restarts.

Usage:
    from nterm.settings import NtermSettings

    settings = NtermSettings()
    settings.zoom_factor = 1.25    # auto-saves
    print(settings.zoom_factor)    # 1.25
"""

import json
import logging
from pathlib import Path
from typing import Any

logger = logging.getLogger("nterm.settings")

NTERM_DIR = Path.home() / ".nterm"
SETTINGS_FILE = NTERM_DIR / "settings.json"

# ── Defaults ────────────────────────────────────────────────
DEFAULTS = {
    "zoom_factor": 1.0,        # QWebEngineView zoom (0.5–3.0)
    "ui_font_scale": 1.0,      # CSS font scale multiplier (0.8–1.5)
    "contrast_mode": "normal",  # "normal", "enhanced", "high"
    "terminal_font_size": 14,   # xterm.js font size in px
    "sidebar_width": 280,       # sidebar width in px
}

# ── Zoom presets ────────────────────────────────────────────
ZOOM_MIN = 0.5
ZOOM_MAX = 3.0
ZOOM_STEP = 0.1

FONT_SCALE_MIN = 0.8
FONT_SCALE_MAX = 1.5
FONT_SCALE_STEP = 0.05


class NtermSettings:
    """
    Persistent settings backed by a JSON file.

    Properties auto-save on write. File is created on first write
    if it doesn't exist.
    """

    def __init__(self, path: Path = SETTINGS_FILE):
        self._path = path
        self._data: dict = dict(DEFAULTS)
        self._load()

    def _load(self):
        """Load settings from disk, merging with defaults."""
        if self._path.exists():
            try:
                with open(self._path, "r") as f:
                    stored = json.load(f)
                # Merge: stored values override defaults
                for key, default in DEFAULTS.items():
                    self._data[key] = stored.get(key, default)
                logger.debug(f"Settings loaded from {self._path}")
            except (json.JSONDecodeError, OSError) as e:
                logger.warning(f"Failed to load settings: {e}, using defaults")
        else:
            logger.debug("No settings file, using defaults")

    def _save(self):
        """Write current settings to disk."""
        try:
            self._path.parent.mkdir(parents=True, exist_ok=True)
            with open(self._path, "w") as f:
                json.dump(self._data, f, indent=2)
            logger.debug(f"Settings saved to {self._path}")
        except OSError as e:
            logger.error(f"Failed to save settings: {e}")

    def _get(self, key: str) -> Any:
        return self._data.get(key, DEFAULTS.get(key))

    def _set(self, key: str, value: Any):
        self._data[key] = value
        self._save()

    # ── Properties ──────────────────────────────────────────

    @property
    def zoom_factor(self) -> float:
        return max(ZOOM_MIN, min(ZOOM_MAX, self._get("zoom_factor")))

    @zoom_factor.setter
    def zoom_factor(self, value: float):
        self._set("zoom_factor", round(max(ZOOM_MIN, min(ZOOM_MAX, value)), 2))

    @property
    def ui_font_scale(self) -> float:
        return max(FONT_SCALE_MIN, min(FONT_SCALE_MAX, self._get("ui_font_scale")))

    @ui_font_scale.setter
    def ui_font_scale(self, value: float):
        self._set("ui_font_scale", round(max(FONT_SCALE_MIN, min(FONT_SCALE_MAX, value)), 2))

    @property
    def contrast_mode(self) -> str:
        mode = self._get("contrast_mode")
        return mode if mode in ("normal", "enhanced", "high") else "normal"

    @contrast_mode.setter
    def contrast_mode(self, value: str):
        if value in ("normal", "enhanced", "high"):
            self._set("contrast_mode", value)

    @property
    def terminal_font_size(self) -> int:
        return max(8, min(32, self._get("terminal_font_size")))

    @terminal_font_size.setter
    def terminal_font_size(self, value: int):
        self._set("terminal_font_size", max(8, min(32, value)))

    @property
    def sidebar_width(self) -> int:
        return max(200, min(500, self._get("sidebar_width")))

    @sidebar_width.setter
    def sidebar_width(self, value: int):
        self._set("sidebar_width", max(200, min(500, value)))

    # ── Convenience ─────────────────────────────────────────

    def zoom_in(self) -> float:
        """Increase zoom by one step. Returns new zoom."""
        self.zoom_factor = self.zoom_factor + ZOOM_STEP
        return self.zoom_factor

    def zoom_out(self) -> float:
        """Decrease zoom by one step. Returns new zoom."""
        self.zoom_factor = self.zoom_factor - ZOOM_STEP
        return self.zoom_factor

    def zoom_reset(self) -> float:
        """Reset zoom to 1.0. Returns 1.0."""
        self.zoom_factor = 1.0
        return 1.0

    def font_scale_up(self) -> float:
        """Increase UI font scale. Returns new scale."""
        self.ui_font_scale = self.ui_font_scale + FONT_SCALE_STEP
        return self.ui_font_scale

    def font_scale_down(self) -> float:
        """Decrease UI font scale. Returns new scale."""
        self.ui_font_scale = self.ui_font_scale - FONT_SCALE_STEP
        return self.ui_font_scale

    def font_scale_reset(self) -> float:
        """Reset font scale to 1.0."""
        self.ui_font_scale = 1.0
        return 1.0

    def cycle_contrast(self) -> str:
        """Cycle through contrast modes. Returns new mode."""
        modes = ["normal", "enhanced", "high"]
        idx = modes.index(self.contrast_mode)
        self.contrast_mode = modes[(idx + 1) % len(modes)]
        return self.contrast_mode

    def to_css_vars(self) -> str:
        """
        Generate CSS custom property overrides for current settings.
        Injected into the page via JavaScript.
        """
        scale = self.ui_font_scale
        mode = self.contrast_mode

        # Contrast color overrides
        contrast_vars = ""
        if mode == "enhanced":
            contrast_vars = """
            --text: #e2e8f0;
            --text-muted: #8da2b8;
            --text-dim: #5a7a96;
            --border: #263040;
            --border-light: #344455;
            """
        elif mode == "high":
            contrast_vars = """
            --text: #f1f5f9;
            --text-muted: #a8bdd0;
            --text-dim: #7094b4;
            --border: #2d4050;
            --border-light: #3d5565;
            """

        return f"""
        :root {{
            --ui-scale: {scale};
            {contrast_vars}
        }}
        """

    def to_json(self) -> dict:
        """Return all settings as a dict (for API endpoints)."""
        return dict(self._data)