#!/usr/bin/env python3
"""
Drivers tab for SolydXK System

Author: Arjen Balfoort
Date: 2026-02-22
"""

import os
import queue
import gettext
import threading

from image import ImageHandler
from drivers import ProprietaryHardwareChecker
from packages import (
    AptAction,
    PackageFilter,
    filter_packages
)
from utils import (
    show_dialog,
    DialogIcon,
    is_dpkg_locked,
    SudoCommandThread,
    LogLevel,
    DebSoftwareApp,
    LogLevel_To_DialogIcon
)

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib

# i18n: http://docs.python.org/3/library/gettext.html
_ = gettext.translation('solydxk-system', fallback=True).gettext


class TabDrivers:
    """
    Drivers tab for SolydXK System
    """
    def __init__(self, app: DebSoftwareApp, use_test_data: bool = False) -> None:
        self.app: DebSoftwareApp = app
        self.use_test_data: bool = use_test_data
        self.drivers: dict = None
        self.image_handler: ImageHandler = ImageHandler(self.app.logging)

    def get_data(self):
        """Get data from ProprietaryHardwareChecker."""
        self.drivers = ProprietaryHardwareChecker(self.app,
                                                  self.use_test_data).result

    def setup_drivers_tab(self) -> None:
        """
        Populate the Drivers tab with a list of proprietary hardware.
        """
        # Reset the progress bar
        self.app.progressbar.set_fraction(0)

        # Check if the listbox is empty
        # Tab will load faster on subsequent runs
        if self.app.listbox_drivers.get_row_at_index(0) is None:
            # Start the spinner
            self.app.spinner.set_visible(True)
            self.app.spinner.start()

            # Get data
            data = threading.Thread(target=self.get_data)
            data.start()

            def check_thread_done():
                if data.is_alive():
                    return True  # Keep checking

                # Thread is done, stop spinner and update UI
                self.app.spinner.set_visible(False)
                self.app.spinner.stop()

                # Create a row for each hardware entry
                for device_id, device_info in self.drivers.items():
                    self.app.logging.info("Found hardware %s, packages: %s",
                                              device_info['name'],
                                              device_info['packages'])
                    row = Gtk.ListBoxRow()
                    row.set_size_request(-1, 60)  # Set a minimum height for the row
                    box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)

                    # Add checkbox
                    checkbox = Gtk.CheckButton()
                    checkbox.device_id = device_id
                    checkbox.packages = device_info['packages']
                    checkbox.set_valign(Gtk.Align.CENTER)  # Center checkbox vertically

                    # Disable the checkbox and grey out the row if no packages are available
                    if not device_info['packages']:
                        checkbox.set_sensitive(False)
                        row.set_sensitive(False)

                    box.append(checkbox)

                    # Add hardware logo
                    logo = Gtk.Picture()
                    logo_name = self._get_logo_name(device_info['name'])
                    logo_path = os.path.join(self.app.images_dir, f"{logo_name}.svg")
                    if os.path.exists(logo_path):
                        self.image_handler.set_picture(logo, logo_path, 48)
                    else:
                        self.image_handler.set_picture(logo, "dialog-warning", 48)
                    box.append(logo)

                    # Add hardware name
                    name_label = Gtk.Label(label=device_info['name'])
                    name_label.set_css_classes(['driver_name'])
                    name_label.set_halign(Gtk.Align.START)
                    name_label.set_hexpand(True)
                    box.append(name_label)

                    row.set_child(box)
                    self.app.listbox_drivers.append(row)

                return False  # Stop checking

            GLib.idle_add(check_thread_done)

        # Add Install button
        self.app.driver_install_button.connect("clicked", self.on_drv_install_clicked)

    @staticmethod
    def _get_logo_name(name: str) -> str:
        """Determine the logo filename based on the hardware name."""
        name_lower = name.lower()
        if "amd" in name_lower:
            return "amd"
        if "nvidia" in name_lower:
            return "nvidia"
        if "broadcom" in name_lower:
            return "broadcom"
        return "unknown"

    def on_drv_install_clicked(self, _button: Gtk.Button) -> None:
        """Handle the Install button click for the Drivers tab."""
        message = ''
        selected_packages = []

        if is_dpkg_locked():
            self.app.logging.error("dpkg is locked by another process.")
            return

        # Iterate through all rows in the listbox
        for row in self.app.listbox_drivers:
            child = row.get_child()
            if isinstance(child, Gtk.Box) and isinstance(child.get_first_child(), Gtk.CheckButton):
                checkbox = child.get_first_child()
                if checkbox.get_active() and checkbox.packages:
                    packages = checkbox.packages
                    selected_packages.extend(packages)

        if not selected_packages:
            show_dialog(_("Drivers"),
                        _("No drivers selected for installation."),
                        self.app.window,
                        DialogIcon.WARNING)
            return

        self.app.logging.debug("Selected driver packages: %s", selected_packages)

        # Filter the packages to only include available packages
        packages = filter_packages(selected_packages,
                                   PackageFilter.AVAILABLE,
                                   self.app.logging)

        # pkexec will prompt for root password via polkit
        command = ["pkexec",
                   "--disable-internal-agent",
                   "python3",
                   os.path.join(self.app.script_dir, "packages_sudo.py"),
                   AptAction.INSTALL.value] + packages

        # Install selected packages and dependencies
        result_queue = queue.Queue()
        thread = SudoCommandThread(command,
                                   _("Drivers"),
                                   result_queue,
                                   self.app.logging)
        thread.start()

        # Use a timeout to update the progress bar
        def update_progress():
            nonlocal message
            if not result_queue.empty():
                dict_info = result_queue.get()
                level = dict_info['level']
                message = dict_info.get('message')

                if level == LogLevel.ERROR:
                    # An error occurred
                    show_dialog(_("Drivers"),
                                message,
                                self.app.window,
                                DialogIcon.ERROR)
                    self.app.progressbar.set_fraction(0)
                    self.app.progressbar_label.set_text("")
                    return False

                self.app.progressbar.set_fraction(dict_info['progress'] / 100)
                self.app.progressbar_label.set_text(message)

            if not thread.is_alive() and result_queue.empty():
                # Show dialog
                if level == LogLevel.SUCCESS:
                    message = _("Drivers have been installed successfully.")
                show_dialog(_("Drivers"),
                            message,
                            self.app.window,
                            LogLevel_To_DialogIcon.get(level, DialogIcon.INFO))
                self.app.progressbar_label.set_text("")

                # All done
                return False

            # Keep going
            return True

        # Add the timeout callback (runs every 100ms)
        GLib.timeout_add(100, update_progress)
