from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Callable, Dict, Optional

from nicegui import ui


@dataclass
class MigManUIRuntime:
    # Config snapshots (mutated in place on reset)
    global_config: Dict[str, Any]
    local_config: Dict[str, Any]
    merged_config: Dict[str, Any]

    # Form state used by renderers
    form_state: Dict[str, Any]  # {"data": {...}}

    # Reactive-ish dicts used by NiceGUI bindings
    state: Dict[str, Any]       # {"dark": bool}
    has_changes: Dict[str, bool]  # {"value": bool}
    active: Dict[str, str]      # {"value": "Global" | ...}

    # UI references
    nav_buttons: Dict[str, Any] = field(default_factory=dict)

    # Optional: active renderer rerender after reset
    renderers: Dict[str, Callable[[], None]] = field(default_factory=dict)


class MigManUIController:
    def __init__(self, ui_app: Any, runtime: MigManUIRuntime) -> None:
        self.ui_app = ui_app
        self.rt = runtime

    # ----------------- state flags -----------------
    def mark_changed(self) -> None:
        self.rt.has_changes["value"] = True

    def mark_saved(self) -> None:
        self.rt.has_changes["value"] = False

    # ----------------- navigation -----------------
    def set_active(self, name: str) -> None:
        self.rt.active["value"] = name
        for key, btn in self.rt.nav_buttons.items():
            btn.props(remove="color unelevated")
            btn.props("flat")
            if key == name:
                btn.props(remove="flat")
                btn.props("color=primary unelevated")

    # ----------------- dark mode -----------------
    def _apply_dark_mode(self, enabled: bool) -> None:
        if enabled:
            ui.dark_mode().enable()
        else:
            ui.dark_mode().disable()

    def set_dark_mode_from_config(self, enabled: bool) -> None:
        # Sets state + form field WITHOUT marking as changed (used for reset/init)
        self.rt.state["dark"] = enabled
        self.rt.form_state["data"]["ui_dark_model"] = enabled
        self._apply_dark_mode(enabled)

    def toggle_dark_mode(self, enabled: bool) -> None:
        # Used for UI switch interaction (marks as changed)
        self.rt.state["dark"] = enabled
        self.rt.form_state["data"]["ui_dark_model"] = enabled
        self.mark_changed()
        self._apply_dark_mode(enabled)

    # ----------------- save/reset -----------------
    def save(self) -> None:
        if self.ui_app.save_configuration(self.rt.form_state["data"]):
            self.mark_saved()

    def reset(self) -> None:
        if not self.ui_app.reset_configuration():
            return

        new_global, new_local, new_merged = self.ui_app.load_configurations()

        # Mutate dicts in place → renderers that reference outer dict variables keep working
        self.rt.global_config.clear()
        self.rt.global_config.update(new_global)

        self.rt.local_config.clear()
        self.rt.local_config.update(new_local)

        self.rt.merged_config.clear()
        self.rt.merged_config.update(new_merged)

        # Keep form_state["data"] object stable (in case something holds a reference)
        self.rt.form_state["data"].clear()
        self.rt.form_state["data"].update(new_merged)

        # Ensure dark mode key exists and apply without "changed" flag
        enabled = self.rt.form_state["data"].get("ui_dark_model", True)
        self.set_dark_mode_from_config(enabled)

        self.mark_saved()
        self.rerender_active()

    def rerender_active(self) -> None:
        fn = self.rt.renderers.get(self.rt.active["value"])
        if fn:
            fn()