import sys
import subprocess
import shlex
import signal
import psutil
import os
from PySide6.QtWidgets import QSystemTrayIcon, QMenu, QApplication, QMessageBox
from PySide6.QtGui import QIcon, QAction
from PySide6.QtCore import QTimer
from portprotonqt.logger import get_logger
from portprotonqt.theme_manager import ThemeManager
from portprotonqt.localization import _
from portprotonqt.config_utils import read_favorites, read_theme_from_config, save_theme_to_config
from portprotonqt.dialogs import GameLaunchDialog

logger = get_logger(__name__)

class TrayManager:
    """Модуль управления системным треем для PortProtonQt.

    Обеспечивает:
    - Показ/скрытие главного окна по двойному клику на иконку трея.
    - Контекстное меню с опциями: Show/Hide, Favorites, Recent Games, Themes, Exit.
    - Динамическое заполнение меню Favorites, Recent Games и Themes.
    - Сворачивание в трей при закрытии окна, полное закрытие через Exit.
    """

    def __init__(self, main_window, app_name: str | None = None, theme=None):
        self.app_name = app_name if app_name is not None else "PortProtonQt"
        self.theme_manager = ThemeManager()
        selected_theme = read_theme_from_config()
        self.current_theme_name = selected_theme
        self.theme = self.theme_manager.apply_theme(selected_theme)
        self.main_window = main_window
        self.tray_icon = QSystemTrayIcon(self.main_window)

        icon = self.theme_manager.get_icon("portproton", self.current_theme_name)
        if isinstance(icon, str):
            icon = QIcon(icon)
        elif icon is None:
            icon = QIcon()
        self.tray_icon.setIcon(icon)

        self.tray_icon.activated.connect(self.handle_tray_click)
        self.tray_icon.setToolTip(self.app_name)

        self.tray_menu = QMenu()
        self.toggle_action = QAction(_("Show"), self.main_window)
        self.toggle_action.triggered.connect(self.toggle_window_action)

        self.favorites_menu = QMenu(_("Favorites"))
        self.favorites_menu.aboutToShow.connect(self.populate_favorites_menu)

        self.recent_menu = QMenu(_("Recent Games"))
        self.recent_menu.aboutToShow.connect(self.populate_recent_menu)

        self.themes_menu = QMenu(_("Themes"))
        self.themes_menu.aboutToShow.connect(self.populate_themes_menu)

        self.tray_menu.addAction(self.toggle_action)
        self.tray_menu.addSeparator()
        self.tray_menu.addMenu(self.favorites_menu)
        self.tray_menu.addMenu(self.recent_menu)
        self.tray_menu.addMenu(self.themes_menu)
        self.tray_menu.addSeparator()
        exit_action = QAction(_("Exit"), self.main_window)
        exit_action.triggered.connect(self.force_exit)
        self.tray_menu.addAction(exit_action)

        self.tray_menu.aboutToShow.connect(self.update_toggle_action)

        self.tray_icon.setContextMenu(self.tray_menu)
        self.tray_icon.show()

        self.main_window.is_exiting = False

        self.click_count = 0
        self.click_timer = QTimer()
        self.click_timer.setSingleShot(True)
        self.click_timer.timeout.connect(self.reset_click_count)

        self.launch_dialog = None

    def update_toggle_action(self):
        if self.main_window.isVisible():
            self.toggle_action.setText(_("Hide"))
        else:
            self.toggle_action.setText(_("Show"))

    def handle_tray_click(self, reason):
        if reason == QSystemTrayIcon.ActivationReason.Trigger:
            self.click_count += 1
            if self.click_count == 1:
                self.click_timer.start(300)
            elif self.click_count == 2:
                self.click_timer.stop()
                self.toggle_window_action()
                self.click_count = 0

    def reset_click_count(self):
        self.click_count = 0

    def toggle_window_action(self):
        if self.main_window.isVisible():
            self.main_window.hide()
        else:
            self.main_window.show()
            self.main_window.raise_()
            self.main_window.activateWindow()

    def populate_favorites_menu(self):
        self.favorites_menu.clear()
        favorites = read_favorites()
        if not favorites:
            no_fav_action = QAction(_("No favorites"), self.main_window)
            no_fav_action.setEnabled(False)
            self.favorites_menu.addAction(no_fav_action)
            return

        game_map = {game[0]: (game[4], game[12]) for game in self.main_window.games}

        for fav in sorted(favorites):
            game_data = game_map.get(fav)
            if game_data:
                exec_line, source = game_data
                action_text = f"{fav} ({source})"
                action = QAction(action_text, self.main_window)
                action.triggered.connect(lambda checked=False, el=exec_line, name=fav: self.launch_game_with_dialog(el, name))
                self.favorites_menu.addAction(action)
            else:
                logger.warning(f"Exec line not found for favorite: {fav}")

    def populate_recent_menu(self):
        self.recent_menu.clear()
        if not self.main_window.games:
            no_recent_action = QAction(_("No recent games"), self.main_window)
            no_recent_action.setEnabled(False)
            self.recent_menu.addAction(no_recent_action)
            return

        recent_games = sorted(self.main_window.games, key=lambda g: g[10], reverse=True)[:5]

        for game in recent_games:
            game_name = game[0]
            exec_line = game[4]
            source = game[12]
            action_text = f"{game_name} ({source})"
            action = QAction(action_text, self.main_window)
            action.triggered.connect(lambda checked=False, el=exec_line, name=game_name: self.launch_game_with_dialog(el, name))
            self.recent_menu.addAction(action)

    def launch_game_with_dialog(self, exec_line, game_name):
        """Launch a game with a modal dialog indicating progress."""
        try:
            # Determine target executable
            target_exe = None
            if exec_line.startswith("steam://"):
                # Steam games are handled differently, no target_exe needed
                self.launch_dialog = GameLaunchDialog(self.main_window, game_name=game_name, theme=self.theme)
            else:
                # Extract target executable from exec_line
                entry_exec_split = shlex.split(exec_line)
                if entry_exec_split[0] == "env" and len(entry_exec_split) > 2:
                    file_to_check = entry_exec_split[2]
                elif entry_exec_split[0] == "flatpak" and len(entry_exec_split) > 3:
                    file_to_check = entry_exec_split[3]
                else:
                    file_to_check = entry_exec_split[0]

                if not os.path.exists(file_to_check):
                    logger.error(f"File not found: {file_to_check}")
                    QMessageBox.warning(self.main_window, _("Error"), _("File not found: {0}").format(file_to_check))
                    return

                target_exe = os.path.basename(file_to_check)
                self.launch_dialog = GameLaunchDialog(self.main_window, game_name=game_name, theme=self.theme, target_exe=target_exe)

            self.launch_dialog.rejected.connect(lambda: self.cancel_game_launch(exec_line))
            self.launch_dialog.show()

            self.main_window.toggleGame(exec_line)
        except Exception as e:
            logger.error(f"Failed to launch game {game_name}: {e}")
            if self.launch_dialog:
                self.launch_dialog.reject()
                self.launch_dialog = None
            QMessageBox.warning(self.main_window, _("Error"), _("Failed to launch game: {0}").format(str(e)))

    def cancel_game_launch(self, exec_line):
        """Cancel the game launch and terminate the process, using MainWindow's stop logic."""
        if self.main_window.game_processes and self.main_window.target_exe:
            for proc in self.main_window.game_processes:
                try:
                    parent = psutil.Process(proc.pid)
                    children = parent.children(recursive=True)
                    for child in children:
                        try:
                            child.terminate()
                        except psutil.NoSuchProcess:
                            pass
                    psutil.wait_procs(children, timeout=5)
                    for child in children:
                        if child.is_running():
                            child.kill()
                    os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
                except psutil.NoSuchProcess:
                    pass
            self.main_window.game_processes = []
            self.main_window.resetPlayButton()
            if self.launch_dialog:
                self.launch_dialog.reject()
                self.launch_dialog = None
            logger.info(f"Game launch cancelled for exec line: {exec_line}")

    def populate_themes_menu(self):
        self.themes_menu.clear()
        available_themes = self.theme_manager.get_available_themes()

        for theme_name in sorted(available_themes):
            action = QAction(theme_name, self.main_window)
            action.setCheckable(True)
            action.setChecked(theme_name == self.current_theme_name)
            action.triggered.connect(lambda checked=False, tn=theme_name: self.switch_theme(tn))
            self.themes_menu.addAction(action)

    def switch_theme(self, theme_name: str):
        try:
            save_theme_to_config(theme_name)
            logger.info(f"Saved theme {theme_name}, restarting application to apply changes")

            executable = sys.executable
            args = sys.argv

            self.main_window.is_exiting = True
            QApplication.quit()

            subprocess.Popen([executable] + args)

        except Exception as e:
            logger.error(f"Failed to switch theme to {theme_name}: {e}")
            save_theme_to_config("standart")
            executable = sys.executable
            args = sys.argv
            self.main_window.is_exiting = True
            QApplication.quit()
            subprocess.Popen([executable] + args)

    def force_exit(self):
        self.main_window.is_exiting = True
        self.main_window.close()
        sys.exit(0)
