# ui/theme_view.py

from dataclasses import dataclass

import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
import os

from accessibility import announce, is_accessibility_enabled, speak
from gi.repository import Gdk, GdkPixbuf, GLib, GObject, Gtk, Pango
from logging_config import get_logger
from services import SystemService
from translations import _
from ui.base_view import BaseItemView

logger = get_logger()


@dataclass(frozen=True)
class ThemeRuntimeState:
    jamesdsp_available: bool
    contrast_available: bool
    total_ram_gb: float
    is_virtual_machine: bool

    @classmethod
    def detect(
        cls, system_service: SystemService, simplified_mode: bool
    ) -> "ThemeRuntimeState":
        return cls(
            jamesdsp_available=system_service.check_jamesdsp_availability(),
            contrast_available=(
                False
                if simplified_mode
                else system_service.check_enhanced_contrast_availability()
            ),
            total_ram_gb=system_service.get_total_memory_gb(),
            is_virtual_machine=(
                False if simplified_mode else system_service.is_virtual_machine()
            ),
        )


class ThemeListItem(GObject.Object):
    """GObject wrapper for theme data."""

    __gtype_name__ = "ThemeListItem"

    def __init__(self, name, system_service: SystemService):
        super().__init__()
        self.name = name
        self.image_path = system_service.get_theme_image_path(name)


class ThemeView(BaseItemView):
    __gtype_name__ = "ThemeView"
    sig_theme_selected = GObject.Signal("theme-selected", arg_types=[str])

    def __init__(
        self,
        system_service: SystemService,
        runtime_state: ThemeRuntimeState,
        simplified_mode: bool = False,
        **kwargs,
    ):
        self.jamesdsp_switch: Gtk.Switch | None = None
        self.contrast_switch: Gtk.Switch | None = None
        self._system_service = system_service  # Store reference for ICC callbacks
        self.simplified_mode = simplified_mode

        self.jamesdsp_available = runtime_state.jamesdsp_available

        # Contrast toggle disabled in simplified mode (GNOME/XFCE/Cinnamon)
        if simplified_mode:
            desktop_env = system_service.get_desktop_environment()
            logger.info(f"ThemeView simplified mode - Desktop: {desktop_env}")
            self.contrast_available = False
            logger.info(f"Contrast disabled in simplified mode for {desktop_env}")
        else:
            self.contrast_available = runtime_state.contrast_available
            logger.info(f"Contrast available in full mode: {self.contrast_available}")

        self.default_jamesdsp_state = runtime_state.total_ram_gb > 7.0
        self.default_contrast_state = not runtime_state.is_virtual_machine

        # Call the parent's __init__ which will build the UI
        super().__init__(system_service=system_service, **kwargs)

    def _build_ui(self):
        # Get the base UI from the parent class (a ScrolledWindow with a content box)
        root_widget = super()._build_ui()

        # The parent _build_ui created self.main_box and self.flow_box.
        # We can now modify them or add other widgets to the main_box.
        if self.simplified_mode:
            # Simplified mode: larger cards, centered
            self.flow_box.set_max_children_per_line(2)
            self.flow_box.set_min_children_per_line(2)
            self.flow_box.set_column_spacing(24)
            self.flow_box.set_row_spacing(24)
            self.flow_box.set_vexpand(False)
            self.flow_box.set_halign(Gtk.Align.CENTER)
            self.flow_box.set_valign(Gtk.Align.CENTER)
        else:
            # Full mode: many small cards
            self.flow_box.set_max_children_per_line(8)
            self.flow_box.set_min_children_per_line(4)
            self.flow_box.set_column_spacing(0)
            self.flow_box.set_row_spacing(12)
            self.flow_box.set_vexpand(True)
            self.flow_box.set_valign(Gtk.Align.END)

        # --- Settings Section (Bottom) ---
        if self.jamesdsp_available or self.contrast_available:
            self.settings_title_label = Gtk.Label(
                label=_("Settings"), halign=Gtk.Align.CENTER
            )
            self.settings_title_label.add_css_class("title-2")
            self.settings_title_label.set_vexpand(True)
            self.settings_title_label.set_valign(Gtk.Align.END)
            self.main_box.append(self.settings_title_label)

            settings_box = Gtk.Box(
                orientation=Gtk.Orientation.HORIZONTAL,
                spacing=12,
                vexpand=True,
                halign=Gtk.Align.CENTER,
                valign=Gtk.Align.START,
            )
            self.main_box.append(settings_box)

            # Card for JamesDSP
            if self.jamesdsp_available:
                self._create_jamesdsp_card(settings_box)

            # Card for Enhanced Contrast
            if self.contrast_available:
                self._create_contrast_card(settings_box)

        return root_widget

    def _retranslate_ui(self):
        """Updates all translatable text in the theme view."""
        super()._retranslate_ui()  # Retranslate the main title from BaseItemView

        if self.jamesdsp_available or self.contrast_available:
            if hasattr(self, "settings_title_label"):
                self.settings_title_label.set_label(_("Settings"))

        if self.jamesdsp_available:
            if hasattr(self, "jamesdsp_title_label"):
                self.jamesdsp_title_label.set_label(_("JamesDSP Audio"))
            if hasattr(self, "jamesdsp_subtitle_label"):
                self.jamesdsp_subtitle_label.set_label(_("Enable audio improvements"))

        if self.contrast_available:
            if hasattr(self, "contrast_title_label"):
                self.contrast_title_label.set_label(_("Image quality"))
            if hasattr(self, "contrast_subtitle_label"):
                self.contrast_subtitle_label.set_label(_("Enable enhanced contrast"))

    def _create_jamesdsp_card(self, parent_box):
        audio_card = Gtk.Box(css_classes=["settings-card"])
        audio_card.set_focusable(True)
        audio_card.update_property(
            [Gtk.AccessibleProperty.LABEL, Gtk.AccessibleProperty.DESCRIPTION],
            [
                _("JamesDSP Audio"),
                _("Enable audio improvements")
                + " — "
                + _("press Enter or Space to toggle"),
            ],
        )
        try:
            cursor = Gdk.Cursor.new_from_name("pointer", None)
            audio_card.set_cursor(cursor)
        except Exception:
            pass
        # Keyboard activation: Enter / Space toggles switch
        card_key_ctl = Gtk.EventControllerKey.new()
        card_key_ctl.connect("key-pressed", self._on_settings_card_key, None)
        audio_card.add_controller(card_key_ctl)
        parent_box.append(audio_card)

        content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
        audio_card.append(content)

        icon = Gtk.Image.new_from_icon_name("multimedia-volume-control-symbolic")
        icon.set_pixel_size(32)
        icon.set_valign(Gtk.Align.CENTER)
        content.append(icon)

        text_vbox = Gtk.Box(
            orientation=Gtk.Orientation.VERTICAL, hexpand=True, spacing=2
        )
        content.append(text_vbox)

        self.jamesdsp_title_label = Gtk.Label(
            label=_("JamesDSP Audio"), xalign=0, css_classes=["title-4"]
        )
        self.jamesdsp_subtitle_label = Gtk.Label(
            label=_("Enable audio improvements"), xalign=0
        )
        text_vbox.append(self.jamesdsp_title_label)
        text_vbox.append(self.jamesdsp_subtitle_label)

        self.jamesdsp_switch = Gtk.Switch(
            valign=Gtk.Align.CENTER, active=self.default_jamesdsp_state
        )
        self.jamesdsp_switch.update_property(
            [Gtk.AccessibleProperty.LABEL],
            [_("JamesDSP Audio")],
        )
        content.append(self.jamesdsp_switch)

        controller = Gtk.GestureClick.new()
        controller.connect(
            "released", self._on_settings_card_clicked, self.jamesdsp_switch
        )
        audio_card.add_controller(controller)

    def _create_contrast_card(self, parent_box):
        contrast_card = Gtk.Box(css_classes=["settings-card"])
        contrast_card.set_focusable(True)
        contrast_card.update_property(
            [Gtk.AccessibleProperty.LABEL, Gtk.AccessibleProperty.DESCRIPTION],
            [
                _("Image quality"),
                _("Enable enhanced contrast")
                + " — "
                + _("press Enter or Space to toggle"),
            ],
        )
        try:
            cursor = Gdk.Cursor.new_from_name("pointer", None)
            contrast_card.set_cursor(cursor)
        except Exception:
            pass
        # Keyboard activation: Enter / Space toggles switch
        card_key_ctl = Gtk.EventControllerKey.new()
        card_key_ctl.connect("key-pressed", self._on_settings_card_key, None)
        contrast_card.add_controller(card_key_ctl)
        parent_box.append(contrast_card)

        content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
        contrast_card.append(content)

        icon = Gtk.Image.new_from_icon_name("image-x-generic-symbolic")
        icon.set_pixel_size(32)
        icon.set_valign(Gtk.Align.CENTER)
        content.append(icon)

        text_vbox = Gtk.Box(
            orientation=Gtk.Orientation.VERTICAL, hexpand=True, spacing=2
        )
        content.append(text_vbox)

        self.contrast_title_label = Gtk.Label(
            label=_("Image quality"), xalign=0, css_classes=["title-4"]
        )
        self.contrast_subtitle_label = Gtk.Label(
            label=_("Enable enhanced contrast"), xalign=0
        )
        text_vbox.append(self.contrast_title_label)
        text_vbox.append(self.contrast_subtitle_label)

        self.contrast_switch = Gtk.Switch(
            valign=Gtk.Align.CENTER, active=self.default_contrast_state
        )
        self.contrast_switch.update_property(
            [Gtk.AccessibleProperty.LABEL],
            [_("Image quality")],
        )
        # Connect callback to apply ICC profile immediately when switch state changes
        self.contrast_switch.connect("notify::active", self._on_contrast_switch_toggled)
        content.append(self.contrast_switch)

        controller = Gtk.GestureClick.new()
        controller.connect(
            "released", self._on_settings_card_clicked, self.contrast_switch
        )
        contrast_card.add_controller(controller)

    def _on_settings_card_clicked(self, gesture, n_press, x, _y, switch):
        if n_press == 1:
            switch.set_active(not switch.get_active())
            if is_accessibility_enabled():
                label = self._label_for_switch(switch)
                state_msg = _("enabled") if switch.get_active() else _("disabled")
                speak(f"{label}, {state_msg}")

    def _on_settings_card_key(self, controller, keyval, keycode, state, _data):
        """Toggle the switch inside a settings card via Enter or Space."""
        if keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter, Gdk.KEY_space):
            card = controller.get_widget()
            switch = self._find_switch_in_widget(card)
            if switch:
                switch.set_active(not switch.get_active())
                state_msg = _("enabled") if switch.get_active() else _("disabled")
                announce(self, state_msg)
                if is_accessibility_enabled():
                    label = self._label_for_switch(switch)
                    speak(f"{label}, {state_msg}")
                return True
        return False

    def _find_switch_in_widget(self, widget):
        """Find a Gtk.Switch inside a widget tree."""
        child = widget.get_first_child()
        while child:
            if isinstance(child, Gtk.Switch):
                return child
            inner = (
                child.get_first_child() if hasattr(child, "get_first_child") else None
            )
            while inner:
                if isinstance(inner, Gtk.Switch):
                    return inner
                inner = inner.get_next_sibling()
            child = child.get_next_sibling()
        return None

    def _label_for_switch(self, switch):
        """Return the label text for a given switch."""
        if switch is self.jamesdsp_switch:
            return _("JamesDSP Audio")
        if switch is self.contrast_switch:
            return _("Image quality")
        return ""

    # --- Implementation of BaseItemView abstract methods ---

    def _select_first_and_announce(self):
        """Override to include JamesDSP state in announcement."""
        first_child = self.flow_box.get_first_child()
        if first_child:
            self.flow_box.select_child(first_child)
            self.grab_focus()
        self._suppress_speak = False
        if is_accessibility_enabled():
            text = self.get_title() or ""
            # Include JamesDSP state
            if self.jamesdsp_available and self.jamesdsp_switch:
                state = (
                    _("enabled") if self.jamesdsp_switch.get_active() else _("disabled")
                )
                text += f". {_('JamesDSP Audio')}, {state}"
            if self.contrast_available and self.contrast_switch:
                state = (
                    _("enabled") if self.contrast_switch.get_active() else _("disabled")
                )
                text += f". {_('Image quality')}, {state}"
            if text:
                speak(text)
        return GLib.SOURCE_REMOVE

    def get_title(self) -> str:
        if self.simplified_mode:
            return _("Choose a Theme Style")
        return _("Choose a System Theme")

    def get_items(self) -> list:
        if self.simplified_mode:
            return ["light", "dark"]
        return self.system_service.get_available_themes()

    def create_item_gobject(self, name: str) -> GObject.Object:
        return ThemeListItem(name, self.system_service)

    def emit_signal(self, name: str):
        self.sig_theme_selected.emit(name)  # type: ignore[arg-type]

    def create_item_widget(self, item: GObject.Object):
        if not isinstance(item, ThemeListItem):
            raise TypeError("ThemeView requires a ThemeListItem")
        if self.simplified_mode:
            return self._create_simple_theme_widget(item)
        return self._create_full_theme_widget(item)

    @staticmethod
    def _create_simple_theme_widget(item: ThemeListItem) -> Gtk.Box:
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        box.set_can_focus(True)
        box.add_css_class("theme-card")
        box.set_size_request(300, 250)
        icon_name = "weather-clear-night" if item.name == "dark" else "weather-clear"
        label_text = _("Dark Theme") if item.name == "dark" else _("Light Theme")
        box.update_property([Gtk.AccessibleProperty.LABEL], [label_text])
        icon_path = f"/usr/share/biglinux/livecd/assets/icons/{icon_name}.svg"
        try:
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, 120, 120)
            icon = (
                Gtk.Image.new_from_paintable(Gdk.Texture.new_for_pixbuf(pixbuf))
                if pixbuf
                else Gtk.Image.new_from_icon_name(icon_name)
            )
        except GLib.Error:
            icon = Gtk.Image.new_from_icon_name(icon_name)
        icon.set_pixel_size(120)
        icon.set_size_request(120, 120)
        icon.set_halign(Gtk.Align.CENTER)
        icon.set_valign(Gtk.Align.CENTER)
        icon.set_vexpand(True)
        box.append(icon)
        label = Gtk.Label(label=label_text, halign=Gtk.Align.CENTER)
        label.add_css_class("title-4")
        box.append(label)
        return box

    @staticmethod
    def _create_full_theme_widget(item: ThemeListItem) -> Gtk.Box:
        # Full mode: standard small theme cards
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        box.set_can_focus(True)

        outer_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        outer_box.set_hexpand(True)
        outer_box.set_vexpand(True)

        try:
            cursor = Gdk.Cursor.new_from_name("pointer", None)
            outer_box.set_cursor(cursor)
        except Exception:
            pass

        if os.path.exists(item.image_path):
            try:
                pixbuf = GdkPixbuf.Pixbuf.new_from_file(item.image_path)
                picture = Gtk.Picture.new_for_pixbuf(pixbuf)
                picture.set_keep_aspect_ratio(True)
                picture.set_hexpand(True)
                picture.set_vexpand(True)
                picture.set_can_shrink(True)
                picture.set_halign(Gtk.Align.CENTER)
                picture.set_valign(Gtk.Align.CENTER)
                picture.set_content_fit(Gtk.ContentFit.CONTAIN)
            except GLib.Error as e:
                logger.error(f"Error loading image {item.image_path}: {e}")
                picture = Gtk.Picture()
        else:
            picture = Gtk.Picture()

        outer_box.append(picture)
        box.append(outer_box)

        label = Gtk.Label()
        label.set_label(item.name.replace("-", " ").title())
        label.set_halign(Gtk.Align.CENTER)
        label.set_margin_top(6)
        label.set_ellipsize(Pango.EllipsizeMode.END)
        label.set_max_width_chars(25)
        box.append(label)

        return box

    def is_jamesdsp_enabled(self) -> bool:
        if self.jamesdsp_switch:
            return self.jamesdsp_switch.get_active()
        return False

    def is_contrast_enabled(self) -> bool:
        if self.contrast_switch:
            return self.contrast_switch.get_active()
        return False

    def _on_contrast_switch_toggled(self, switch, _param):
        """Called immediately when the ICC profile switch state changes."""
        enabled = switch.get_active()
        logger.info(
            f"ICC profile switch toggled: {'enabled' if enabled else 'disabled'}"
        )
        self._system_service.apply_icc_profile_settings(enabled)
