"""
Runtime settings store. Persists the settings the user changes via the
SETTINGS modal to a sidecar JSON file so they survive restarts.

The single mutable `SETTINGS` dict is the source of truth at runtime —
import it, don't copy it. Endpoints should call `apply_patch()` (which
also writes the file) rather than mutating directly.
"""
from __future__ import annotations
import json
import os
from pathlib import Path
from threading import Lock

_PATH = Path(__file__).parent.parent / "settings.json"
_LOCK = Lock()

DEFAULTS: dict = {
    # AIS source
    "ais_source":          os.getenv("AIS_SOURCE", "SIMULATOR"),
    "ais_serial_port":     "",
    "ais_baud":            38400,
    "ais_net_addr":        "",
    # GPS (mirrored from .env at startup; the GPS hot-change endpoint also writes here)
    "gps_port":            os.getenv("GPS_PORT", ""),
    "gps_baud":            int(os.getenv("GPS_BAUD", 9600) or 9600),
    # Station / antenna (purely descriptive for now; antenna position is
    # exposed to the frontend so it can centre the map and run proximity
    # checks against the station)
    "station_name":        "",
    "iala_region":         "B",
    "antenna_lat":         None,
    "antenna_lon":         None,
    "antenna_height_m":    None,
    "station_notes":       "",
    # Alert thresholds (live — alert_engine reads these every evaluation)
    "displacement_warn_m":   10.0,
    "displacement_alarm_m":  15.0,
    "proximity_alert_meters": int(os.getenv("PROXIMITY_ALERT_METERS", 500)),
    "projection_minutes":     int(os.getenv("PROJECTION_MINUTES", 10)),
    "projection_radius_meters": 800,
    "auto_record_trigger_m":   200.0,
    # ATON battery thresholds (volts)
    "battery_warn_v":      11.5,
    "battery_alarm_v":     10.8,
    # SMTP — operator's organisation email account that sends report emails.
    # When configured, /alerts/report sends EMAIL via SMTP instead of opening
    # the operator's local mail client. Leave smtp_host blank to disable.
    "smtp_host":           "",
    "smtp_port":           587,
    "smtp_user":           "",
    "smtp_password":       "",
    "smtp_from":           "",
    "smtp_from_name":      "AidsMonitoring",
    "smtp_use_tls":        True,
    # ── Cluster / multi-server role ──────────────────────────────────────────
    # STANDALONE: single server (default)
    # MASTER    : central aggregator — accepts connections from slave servers
    # SLAVE     : field server — forwards all events to the master
    "server_role":         os.getenv("SERVER_ROLE", "STANDALONE"),
    # URL of the master's slave WebSocket endpoint (required when SLAVE)
    # Example: "ws://10.0.0.1:8000/ws/slave"
    "master_url":          os.getenv("MASTER_URL", ""),
    # Human-readable name for this slave (shown in master's status panel)
    "slave_name":          os.getenv("SLAVE_NAME", ""),
}

SETTINGS: dict = dict(DEFAULTS)


def _load_from_disk() -> dict:
    if not _PATH.exists():
        return {}
    try:
        return json.loads(_PATH.read_text())
    except Exception:
        return {}


def _flush_to_disk():
    try:
        _PATH.write_text(json.dumps(SETTINGS, indent=2))
    except Exception:
        pass


def init():
    """Call once at app startup — merges saved overrides into SETTINGS."""
    SETTINGS.update(_load_from_disk())
    return SETTINGS


def get_all() -> dict:
    return dict(SETTINGS)


def apply_patch(patch: dict) -> dict:
    """Merge patch into SETTINGS, ignoring unknown keys, write to disk.
    Returns the new full settings dict."""
    with _LOCK:
        for k, v in patch.items():
            if k in DEFAULTS:
                SETTINGS[k] = v
        _flush_to_disk()
    return get_all()
