#!/usr/bin/env python3
"""
Main application for SolydXK System

References:
Gtk4: https://docs.gtk.org/gtk4/
WebKit 6.0: https://webkitgtk.org/reference/webkitgtk/stable
WebKit2 4.1: https://webkitgtk.org/reference/webkit2gtk/stable

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

import os
import json
import sys
import glob
import argparse
import gettext
import logging
from logging.handlers import TimedRotatingFileHandler
import apt

from tab_favorites import TabFavorites
from tab_drivers import TabDrivers
from tab_repositories import TabRepositories
from tab_packages import TabPackages
from tab_locales import TabLocales
from tab_themes import TabThemes
from tab_encryption import TabEncryption
from utils import show_dialog, DialogIcon
from image import ImageHandler

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

_ = gettext.translation('solydxk-system', fallback=True).gettext

APP_NAME = "SolydXK System"

# Configure logging
SAVE_DIR = os.path.expanduser("~/.config/solydxk")
os.makedirs(SAVE_DIR, exist_ok=True)
LOG_PATH = os.path.join(SAVE_DIR, "solydxk-system.log")
SHOW_WELCOME = not os.path.exists(LOG_PATH)

# Create a timed rotating file handler that keeps logs for 10 days
handler = TimedRotatingFileHandler(
    LOG_PATH,
    when='midnight',  # Rotate at midnight
    interval=1,       # Rotate every 1 day
    backupCount=10,   # Keep 10 days of logs
)

# Set the logging level and format
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s',
                              datefmt='%Y-%m-%d %H:%M:%S')
handler.setFormatter(formatter)

# Rotate logs immediately on startup
handler.doRollover()
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

# Get command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--tab_name", help="Open a specific tab")
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug mode")
parser_args = parser.parse_args()

# Global exception handler
def handle_exception(exc_type, exc_value, exc_traceback):
    """
    Global exception handler to log unexpected errors.
    """
    logging.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
    show_dialog(
        APP_NAME,
        _("An unexpected error occurred.\nPlease check the log file for details."),
        icon=DialogIcon.ERROR
    )

# Set the global exception handler
sys.excepthook = handle_exception


class SolydXKSystem(Gtk.Application):
    """
    Main application class.
    """
    def __init__(self):
        super().__init__(application_id='org.solydxk.solydxk_system')
        self.save_dir: str = SAVE_DIR

        # Check if old flag exists
        old_flag = os.path.expanduser("~/.sws.flag")
        if os.path.exists(old_flag):
            os.remove(old_flag)

        # Directories
        self.script_dir: str = os.path.abspath(os.path.dirname(__file__))
        self.images_dir: str = os.path.join(self.script_dir, "images")
        self.html_dir: str = os.path.join(self.script_dir, "html")
        self.external_dir: str = os.path.join(self.script_dir, "external")
        self.data_dir: str = os.path.join(self.script_dir, "data")
        self.localize_dir: str = os.path.join(self.script_dir, "localize")
        self.log_path: str = LOG_PATH

        # Load configuration from json
        with open(file=os.path.join(self.data_dir, "config.json"),
                  mode="r",
                  encoding="utf-8") as f:
            self.config: dict = json.load(f)

        # Log start date and time
        self.logging: logging = logging
        self.logging.info(f"{APP_NAME} started")

        # Get current icon theme
        self.image_handler: ImageHandler = ImageHandler(logging_handler=self.logging)

        # Load the UI file
        self.builder: Gtk.Builder = Gtk.Builder.new_from_file(
            f'{self.script_dir}/solydxk-system.ui'
        )
        self.window: Gtk.Window = self.builder.get_object('main_window')
        self.progressbar: Gtk.ProgressBar = self.builder.get_object('progressbar')
        self.logo: Gtk.Picture = self.builder.get_object('logo')

        # Add a label OVER the progressbar
        self.progress_overlay: Gtk.Overlay = self.builder.get_object("progress_overlay")
        self.progressbar_label: Gtk.Label = Gtk.Label()
        self.progressbar_label.set_halign(Gtk.Align.START)
        self.progressbar_label.set_valign(Gtk.Align.CENTER)
        self.progressbar_label.set_css_classes(["progressbar_label"])
        self.progressbar_label.set_ellipsize(Pango.EllipsizeMode.END)
        self.progress_overlay.add_overlay(self.progressbar_label)

        # Get current desktop (kde or xfce)
        self.desktop: str = os.getenv('XDG_CURRENT_DESKTOP').lower()

        # Load welcome pictures
        picture_path = os.path.join(self.images_dir, f"softwaremanager_{self.desktop}.png")
        if not os.path.exists(picture_path):
            picture_path = os.path.join(self.images_dir, "softwaremanager_kde.png")
        self.image_handler.set_picture(
            self.builder.get_object('softwaremanager_picture'), picture_path
        )
        picture_path = os.path.join(self.images_dir, "forum.png")
        self.image_handler.set_picture(self.builder.get_object('forum_picture'), picture_path)

        # Set welcome URLs
        support_url = self.config.get("support")
        rss_url = self.config.get("rss")
        welcome_forum_label: Gtk.Label = self.builder.get_object('welcome_forum_label')
        welcome_rss_label: Gtk.Label = self.builder.get_object('welcome_rss_label')
        welcome_forum_label.set_markup(f'<a href="{support_url}">{_("Forum")}</a>')
        welcome_rss_label.set_markup(f'<a href="{rss_url}">{_("RSS")}</a>')

        # Set window title, window icon by ID and left menu logo
        self.window.set_title(APP_NAME)
        self.window.set_icon_name(self.get_application_id())
        self.image_handler.set_picture(self.logo, self.get_application_id(), 128)

        # Load CSS
        css_path = os.path.join(self.script_dir, "style", f"{self.desktop}.css")
        # First, check if there is a css file in the user directory
        css_paths = glob.glob(f"{self.save_dir}/*.css")
        if css_paths:
            css_path = css_paths[0]
        if not os.path.exists(css_path):
            # Default to kde.css
            css_path = os.path.join(self.script_dir, "style", "kde.css")
        css_provider: Gtk.CssProvider = Gtk.CssProvider()
        self.logging.info(f"Loading CSS from {css_path}")
        css_provider.load_from_path(css_path)
        Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(),
                                                  css_provider,
                                                  Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

        # Define tabs with corresponding page numbers
        #TabWelcome(self).setup_welcome_tab
        self.tabs: dict = {
            "welcome": [0, None],
            "favorites": [1, TabFavorites(self).setup_favorites_tab],
            "drivers": [2, TabDrivers(self, parser_args.debug).setup_drivers_tab],
            "repositories": [3, TabRepositories(self).setup_repositories_tab],
            "packages": [4, TabPackages(self).setup_packages_tab],
            "locales": [5, TabLocales(self).setup_locales_tab],
            "themes": [6, TabThemes(self).setup_themes_tab],
            "encryption": [7, TabEncryption(self).setup_encryption_tab]
        }

        # TabDrivers ui objects
        self.listbox_drivers: Gtk.ListBox = self.builder.get_object('listbox_drivers')
        self.spinner: Gtk.Spinner = self.builder.get_object('spinner')
        self.driver_install_button: Gtk.Button = self.builder.get_object('btn_drv_install')

        # URL for the Linux Kernel Device Database (LKDDb)
        self.lkddb_url: str = "https://cateee.net/sources/lkddb/lkddb.list"
        # Vendor IDs
        self.amd_vendor_id: str = "1002"
        self.broadcom_vendor_id: str = "14e4"
        self.nvidia_vendor_id: str = "10de"

        # TabEncryption ui objects
        # Note: Gtk.TreeView is deprecated in GTK4.10+ and will be removed in GTK5.
        self.treeview_partitions: Gtk.TreeView  = self.builder.get_object('tv_partitions')
        self.password_1_entry: Gtk.Entry = self.builder.get_object('entry_password_1')
        self.password_2_entry: Gtk.Entry = self.builder.get_object('entry_password_2')
        self.img_password_check: Gtk.Image = self.builder.get_object('img_password_check')
        self.refresh_button: Gtk.Button = self.builder.get_object('btn_refresh')
        self.encrypt_button: Gtk.Button = self.builder.get_object('btn_encrypt')

        # TabFavorites ui objects
        self.listbox_favorites: Gtk.ListBox = self.builder.get_object('listbox_favorites')

        # TabLocalisation ui objects
        self.treeview_locales: Gtk.TreeView = self.builder.get_object('tv_locales')
        self.dd_continent: Gtk.ComboBox = self.builder.get_object('dd_continent')
        self.dd_city: Gtk.ComboBox = self.builder.get_object('dd_city')
        self.save_locale_button: Gtk.Button = self.builder.get_object('btn_save_locale')

        # TabPackages ui objects
        self.listbox_hold_back: Gtk.ListBox = self.builder.get_object('listbox_hold_back')
        self.listbox_cleanup: Gtk.ListBox = self.builder.get_object('listbox_cleanup')
        self.chk_select_all: Gtk.CheckButton = self.builder.get_object('chk_select_all')
        self.package_search: Gtk.Entry = self.builder.get_object('package_search')
        self.mark_button: Gtk.Button = self.builder.get_object('btn_mark')
        self.unmark_button: Gtk.Button = self.builder.get_object('btn_unmark')
        self.cleanup_button: Gtk.Button = self.builder.get_object('btn_cleanup')

        # TabRepositories ui objects
        self.listbox_repo_solydxk: Gtk.ListBox = self.builder.get_object('listbox_repo_solydxk')
        self.listbox_repo_debian: Gtk.ListBox = self.builder.get_object('listbox_repo_debian')
        self.listbox_repo_other: Gtk.ListBox = self.builder.get_object('listbox_repo_other')
        self.chk_backports: Gtk.CheckButton = self.builder.get_object('chk_backports')
        self.save_sources_button: Gtk.Button = self.builder.get_object('btn_save_sources')

        # TabThemes ui objects
        self.listbox_plymouth: Gtk.ListBox = self.builder.get_object('listbox_plymouth')
        self.listbox_grub: Gtk.ListBox = self.builder.get_object('listbox_grub')
        self.save_plymouth_button: Gtk.Button = self.builder.get_object('btn_save_plymouth')
        self.save_grub_button: Gtk.Button = self.builder.get_object('btn_save_grub')
        self.dd_resolution: Gtk.ComboBox = self.builder.get_object('dd_resolution')


    def do_activate(self, *args, **kwargs) -> None:
        """
        Called when the application is activated.
        Overrides Gtk.Application.do_activate.
        """
        # Get the main window and show it
        self.window.set_application(self)
        self.window.present()

        # Get the notebook and buttons
        select_button = None
        select_tab = "welcome" if not parser_args.tab_name else parser_args.tab_name
        notebook = self.builder.get_object('notebook')
        for key, _value in self.tabs.items():
            button = self.builder.get_object(f"btn_{key}")
            if button is None:
                continue
            if key == select_tab:
                select_button = button
            button.connect('clicked', self.on_button_clicked, notebook, key)

        # Other buttons
        button_log = self.builder.get_object('btn_log')
        button_log.connect('clicked', self.on_button_log_clicked)
        button_help = self.builder.get_object('btn_help')
        button_help.connect('clicked', self.on_button_help_clicked)
        button_about = self.builder.get_object('btn_about')
        button_about.connect('clicked', self.on_button_about_clicked)

        # Switch to the selected tab
        self.on_button_clicked(select_button, notebook, select_tab)
        if SHOW_WELCOME:
            self.window.maximize()

    def on_button_clicked(self,
                          button: Gtk.Button,
                          notebook: Gtk.Notebook,
                          tab_name: str
                          ) -> None:
        """
        Switch to the specified notebook page when a button is clicked.
        """
        if tab_name not in self.tabs:
            raise ValueError(f"Unknown tab: {tab_name}")

        page_num = self.tabs.get(tab_name)[0]
        notebook.set_current_page(page_num)
        if self.tabs.get(tab_name)[1] is not None:
            page_func = self.tabs.get(tab_name)[1]
            page_func()
        button.grab_focus()

    def on_button_log_clicked(self, _button: Gtk.Button) -> None:
        """
        Open the log file in a text editor.
        """
        if not os.path.exists(self.log_path):
            show_dialog(APP_NAME,
                        _("Log file not found"),
                        self.window,
                        DialogIcon.WARNING)
            return
        os.system(f"xdg-open {self.log_path}")

    def on_button_help_clicked(self, _button: Gtk.Button) -> None:
        """
        Open the help file in the default browser.
        """
        help_path = os.path.join(self.script_dir, "help.html")
        if not os.path.exists(help_path):
            return
        os.system(f"xdg-open {help_path}")

    def on_button_about_clicked(self, _button: Gtk.Button) -> None:
        """
        Show the about window.
        """
        try:
            version = apt.Cache()['solydxk-system'].versions[0].source_version
        except KeyError:
            version = ''

        about = Gtk.AboutDialog(
            transient_for=self.window,  # self is your main window
            modal=True,
            program_name=APP_NAME,
            version=version,
            copyright=f"© 2026 {self.config.get('authors')[0]}",
            license_type=Gtk.License.GPL_3_0,
            website=self.config.get("home"),
            website_label="Website",
            authors=self.config.get("authors"),
            logo_icon_name=self.get_application_id()
        )

        about.present()

if __name__ == '__main__':
    # Start the app
    app = SolydXKSystem()
    app.run(None)
