import json
import os
import binascii


def _settings_path():
    """Path to settings.json in project root (writable without root)."""
    try:
        base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        return os.path.join(base, "settings.json")
    except Exception:
        return "settings.json"


class Settings(dict):
    SETTINGS_FILE = None  # Set in __init__ from _settings_path()

    def __init__(self):
        super().__init__()
        if Settings.SETTINGS_FILE is None:
            Settings.SETTINGS_FILE = _settings_path()
        self.load()  # Load settings from file during initialization

    def generate_secret_key(self):
        """Generate a random secret key for session signing."""
        try:
            # Try to use os.urandom for secure random bytes
            random_bytes = os.urandom(32)
            return binascii.hexlify(random_bytes).decode('utf-8')
        except (AttributeError, NotImplementedError):
            # Fallback for MicroPython or systems without os.urandom
            try:
                import secrets
                return secrets.token_hex(32)
            except ImportError:
                # Last resort: use a combination of time and random
                import time
                import random
                random.seed(time.time())
                return binascii.hexlify(bytes([random.randint(0, 255) for _ in range(32)])).decode('utf-8')

    def set_defaults(self):
        """Set default settings if they don't exist."""
        if 'session_secret_key' not in self:
            self['session_secret_key'] = self.generate_secret_key()
            # Save immediately when generating a new key
            self.save()
        # ESP-NOW STA channel (2.4 GHz) for LED drivers / bridge alignment; 1–11
        if 'wifi_channel' not in self:
            self['wifi_channel'] = 6
        # Wi-Fi LED drivers: controller opens WebSocket to device (firmware serves /ws)
        if 'wifi_driver_ws_port' not in self:
            self['wifi_driver_ws_port'] = 80
        if 'wifi_driver_ws_path' not in self:
            self['wifi_driver_ws_path'] = '/ws'
        # Seconds between UDP discovery nudges when a Wi-Fi driver WebSocket is
        # down (0 disables). Helps drivers that reconnect after seeing traffic on 8766.
        if 'wifi_driver_hello_interval_s' not in self:
            self['wifi_driver_hello_interval_s'] = 10.0
        # Outbound WebSocket dial: total seconds to keep trying before first success
        # (many devices booting at once need more than a short window).
        if 'wifi_driver_connect_retry_window_s' not in self:
            self['wifi_driver_connect_retry_window_s'] = 120.0
        # Spread outbound dials 0..N s by device IP so six+ drivers do not all hit the AP at once.
        if 'wifi_driver_connect_stagger_max_s' not in self:
            self['wifi_driver_connect_stagger_max_s'] = 2.5
        # TCP/WebSocket open timeout per attempt (seconds).
        if 'wifi_driver_ws_open_timeout' not in self:
            self['wifi_driver_ws_open_timeout'] = 45.0
        # Pause between outbound WebSocket dial attempts (seconds).
        if 'wifi_driver_connect_retry_interval_s' not in self:
            self['wifi_driver_connect_retry_interval_s'] = 2.0
        # UART to ESP32 ESP-NOW bridge; default off (Wi-Fi drivers need no serial).
        if 'serial_enabled' not in self:
            self['serial_enabled'] = False
        # Zone UI global brightness (0–255); shared across browsers/devices.
        if 'global_brightness' not in self:
            self['global_brightness'] = 255

    def save(self):
        try:
            j = json.dumps(self)
            with open(self.SETTINGS_FILE, 'w') as file:
                file.write(j)
            print("Settings saved successfully.")
        except Exception as e:
            print(f"Error saving settings: {e}")

    def load(self):
        loaded_from_file = False
        try:
            with open(self.SETTINGS_FILE, 'r') as file:
                loaded_settings = json.load(file)
            self.update(loaded_settings)
            loaded_from_file = True
            print("Settings loaded successfully.")
        except Exception as e:
            print(f"Error loading settings")
            self.clear()
        finally:
            # Ensure defaults are set even if file exists but is missing keys
            self.set_defaults()
            # Only save if file didn't exist or was invalid
            if not loaded_from_file:
                self.save()
