from PySide6.QtGui import QPainter, QColor, QDesktopServices
from PySide6.QtCore import Signal, Property, Qt, QUrl
from PySide6.QtWidgets import QFrame, QGraphicsDropShadowEffect, QVBoxLayout, QWidget, QStackedLayout, QLabel
from collections.abc import Callable
import portprotonqt.themes.standart.styles as default_styles
from portprotonqt.image_utils import load_pixmap_async, round_corners
from portprotonqt.localization import _
from portprotonqt.config_utils import read_favorites, save_favorites, read_display_filter
from portprotonqt.theme_manager import ThemeManager
from portprotonqt.config_utils import read_theme_from_config
from portprotonqt.custom_widgets import ClickableLabel
from portprotonqt.portproton_api import PortProtonAPI
from portprotonqt.downloader import Downloader
from portprotonqt.animations import GameCardAnimations
import weakref
from typing import cast


class GameCard(QFrame):
    borderWidthChanged = Signal()
    gradientAngleChanged = Signal()
    # Signals for context menu actions
    editShortcutRequested = Signal(str, str, str) # name, exec_line, cover_path
    deleteGameRequested = Signal(str, str)        # name, exec_line
    addToMenuRequested = Signal(str, str)         # name, exec_line
    removeFromMenuRequested = Signal(str)         # name
    addToDesktopRequested = Signal(str, str)      # name, exec_line
    removeFromDesktopRequested = Signal(str)      # name
    addToSteamRequested = Signal(str, str, str)   # name, exec_line, cover_path
    removeFromSteamRequested = Signal(str, str)   # name, exec_line
    openGameFolderRequested = Signal(str, str)    # name, exec_line
    hoverChanged = Signal(str, bool)
    focusChanged = Signal(str, bool)

    def __init__(self, name, description, cover_path, appid, controller_support, exec_line,
                last_launch, formatted_playtime, protondb_tier, anticheat_status, last_launch_ts, playtime_seconds, game_source,
                select_callback, theme=None, card_width=250, parent=None, context_menu_manager=None):
        super().__init__(parent)
        self.name = name
        self.description = description
        self.cover_path = cover_path
        self.appid = appid
        self.controller_support = controller_support
        self.exec_line = exec_line
        self.last_launch = last_launch
        self.formatted_playtime = formatted_playtime
        self.protondb_tier = protondb_tier
        self.anticheat_status = anticheat_status
        self.game_source = game_source
        self.last_launch_ts = last_launch_ts
        self.playtime_seconds = playtime_seconds
        self.card_width = card_width

        self.select_callback = select_callback
        self.context_menu_manager = context_menu_manager
        self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
        self.customContextMenuRequested.connect(self._show_context_menu)
        self.theme_manager = ThemeManager()
        self.theme = theme if theme is not None else default_styles

        self.display_filter = read_display_filter()
        self.current_theme_name = read_theme_from_config()
        self.downloader = Downloader(max_workers=4)
        self.portproton_api = PortProtonAPI(self.downloader)

        self.steam_visible = (str(game_source).lower() == "steam" and self.display_filter in ("all", "favorites"))
        self.egs_visible = (str(game_source).lower() == "epic" and self.display_filter in ("all", "favorites"))
        self.portproton_visible = (str(game_source).lower() == "portproton" and self.display_filter in ("all", "favorites"))

        # Дополнительное пространство для анимации
        extra_margin = 20
        self.setFixedSize(card_width + extra_margin, int(card_width * 1.6) + extra_margin)
        self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
        self.setStyleSheet(self.theme.GAME_CARD_WINDOW_STYLE)

        # Параметры анимации обводки
        self._borderWidth = self.theme.GAME_CARD_ANIMATION["default_border_width"]
        self._gradientAngle = self.theme.GAME_CARD_ANIMATION["gradient_start_angle"]
        self._hovered = False
        self._focused = False

        # Анимации
        self.animations = GameCardAnimations(self, self.theme)
        self.animations.setup_animations()

        # Тень
        shadow = QGraphicsDropShadowEffect(self)
        shadow.setBlurRadius(20)
        shadow.setColor(QColor(0, 0, 0, 150))
        shadow.setOffset(0, 0)
        self.setGraphicsEffect(shadow)

        # Отступы
        layout = QVBoxLayout(self)
        layout.setContentsMargins(extra_margin // 2, extra_margin // 2, extra_margin // 2, extra_margin // 2)
        layout.setSpacing(5)

        # Контейнер обложки
        coverWidget = QWidget()
        coverWidget.setFixedSize(card_width, int(card_width * 1.2))
        coverLayout = QStackedLayout(coverWidget)
        coverLayout.setContentsMargins(0, 0, 0, 0)
        coverLayout.setStackingMode(QStackedLayout.StackingMode.StackAll)

        # Обложка
        self.coverLabel = QLabel()
        self.coverLabel.setFixedSize(card_width, int(card_width * 1.2))
        self.coverLabel.setStyleSheet(self.theme.COVER_LABEL_STYLE)
        coverLayout.addWidget(self.coverLabel)

        # создаём слабую ссылку на label
        label_ref = weakref.ref(self.coverLabel)

        def on_cover_loaded(pixmap):
            label = label_ref()
            if label is None:
                return
            label.setPixmap(round_corners(pixmap, 15))

        # асинхронная загрузка обложки (пустая строка даст placeholder внутри load_pixmap_async)
        load_pixmap_async(cover_path or "", card_width, int(card_width * 1.2), on_cover_loaded)

        # Значок избранного (звёздочка) в левом верхнем углу обложки
        self.favoriteLabel = ClickableLabel(coverWidget)
        self.favoriteLabel.setFixedSize(*self.theme.favoriteLabelSize)
        self.favoriteLabel.move(8, 8)
        self.favoriteLabel.clicked.connect(self.toggle_favorite)
        self.is_favorite = self.name in read_favorites()
        self.update_favorite_icon()
        self.favoriteLabel.raise_()

        # Определяем общие параметры для бейджей
        badge_width = int(card_width * 2/3)
        icon_size = int(card_width * 0.06)  # 6% от ширины карточки
        icon_space = int(card_width * 0.012)  # 1.2% от ширины карточки
        font_scale_factor = 0.06  # Шрифт будет 6% от card_width

        # ProtonDB бейдж
        tier_text = self.getProtonDBText(protondb_tier)
        if tier_text:
            icon_filename = self.getProtonDBIconFilename(protondb_tier)
            icon = self.theme_manager.get_icon(icon_filename, self.current_theme_name)
            self.protondbLabel = ClickableLabel(
                tier_text,
                icon=icon,
                parent=coverWidget,
                icon_size=icon_size,
                icon_space=icon_space,
                font_scale_factor=font_scale_factor
            )
            self.protondbLabel.setStyleSheet(self.theme.get_protondb_badge_style(protondb_tier))
            self.protondbLabel.setFixedWidth(badge_width)
            self.protondbLabel.setCardWidth(card_width)
        else:
            self.protondbLabel = ClickableLabel("", parent=coverWidget, icon_size=icon_size, icon_space=icon_space)
            self.protondbLabel.setFixedWidth(badge_width)
            self.protondbLabel.setVisible(False)

        # Steam бейдж
        steam_icon = self.theme_manager.get_icon("steam")
        self.steamLabel = ClickableLabel(
            "Steam",
            icon=steam_icon,
            parent=coverWidget,
            icon_size=icon_size,
            icon_space=icon_space,
            font_scale_factor=font_scale_factor
        )
        self.steamLabel.setStyleSheet(self.theme.STEAM_BADGE_STYLE)
        self.steamLabel.setFixedWidth(badge_width)
        self.steamLabel.setCardWidth(card_width)
        self.steamLabel.setVisible(self.steam_visible)

        # Epic Games Store бейдж
        egs_icon = self.theme_manager.get_icon("epic_games")
        self.egsLabel = ClickableLabel(
            "Epic Games",
            icon=egs_icon,
            parent=coverWidget,
            icon_size=icon_size,
            icon_space=icon_space,
            font_scale_factor=font_scale_factor,
            change_cursor=False
        )
        self.egsLabel.setStyleSheet(self.theme.STEAM_BADGE_STYLE)
        self.egsLabel.setFixedWidth(badge_width)
        self.egsLabel.setCardWidth(card_width)
        self.egsLabel.setVisible(self.egs_visible)

        # PortProton бейдж
        portproton_icon = self.theme_manager.get_icon("portproton")
        self.portprotonLabel = ClickableLabel(
            "PortProton",
            icon=portproton_icon,
            parent=coverWidget,
            icon_size=icon_size,
            icon_space=icon_space,
            font_scale_factor=font_scale_factor
        )
        self.portprotonLabel.setStyleSheet(self.theme.STEAM_BADGE_STYLE)
        self.portprotonLabel.setFixedWidth(badge_width)
        self.portprotonLabel.setCardWidth(card_width)
        self.portprotonLabel.setVisible(self.portproton_visible)
        self.portprotonLabel.clicked.connect(self.open_portproton_forum_topic)

        # WeAntiCheatYet бейдж
        anticheat_text = self.getAntiCheatText(anticheat_status)
        if anticheat_text:
            icon_filename = self.getAntiCheatIconFilename(anticheat_status)
            icon = self.theme_manager.get_icon(icon_filename, self.current_theme_name)
            self.anticheatLabel = ClickableLabel(
                anticheat_text,
                icon=icon,
                parent=coverWidget,
                icon_size=icon_size,
                icon_space=icon_space,
                font_scale_factor=font_scale_factor
            )
            self.anticheatLabel.setStyleSheet(self.theme.get_anticheat_badge_style(anticheat_status))
            self.anticheatLabel.setFixedWidth(badge_width)
            self.anticheatLabel.setCardWidth(card_width)
        else:
            self.anticheatLabel = ClickableLabel("", parent=coverWidget, icon_size=icon_size, icon_space=icon_space)
            self.anticheatLabel.setFixedWidth(badge_width)
            self.anticheatLabel.setVisible(False)

        # Расположение бейджей
        self._position_badges(card_width)
        self.protondbLabel.clicked.connect(self.open_protondb_report)
        self.steamLabel.clicked.connect(self.open_steam_page)
        self.anticheatLabel.clicked.connect(self.open_weanticheatyet_page)

        layout.addWidget(coverWidget)

        # Название игры
        nameLabel = QLabel(name)
        nameLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
        nameLabel.setStyleSheet(self.theme.GAME_CARD_NAME_LABEL_STYLE)
        layout.addWidget(nameLabel)

    def _position_badges(self, card_width):
        """Позиционирует бейджи на основе ширины карточки."""
        right_margin = 8
        badge_spacing = int(card_width * 0.02)  # 2% от ширины карточки
        top_y = 10
        badge_y_positions = []
        badge_width = int(card_width * 2/3)

        badges = [
            (self.steam_visible, self.steamLabel),
            (self.egs_visible, self.egsLabel),
            (self.portproton_visible, self.portprotonLabel),
            (bool(self.getProtonDBText(self.protondb_tier)), self.protondbLabel),
            (bool(self.getAntiCheatText(self.anticheat_status)), self.anticheatLabel),
        ]

        for is_visible, badge in badges:
            if is_visible:
                badge_x = card_width - badge_width - right_margin
                badge_y = badge_y_positions[-1] + badge_spacing if badge_y_positions else top_y
                badge.move(badge_x, badge_y)
                badge_y_positions.append(badge_y + badge.height())

        # Поднимаем бейджи в правильном порядке (от нижнего к верхнему)
        self.anticheatLabel.raise_()
        self.protondbLabel.raise_()
        self.portprotonLabel.raise_()
        self.egsLabel.raise_()
        self.steamLabel.raise_()

    def update_card_size(self, new_width: int):
        """Обновляет размер карточки, обложки и бейджей."""
        self.card_width = new_width
        extra_margin = 20
        self.setFixedSize(new_width + extra_margin, int(new_width * 1.6) + extra_margin)

        if self.coverLabel is None:
            return

        coverWidget = self.coverLabel.parentWidget()
        if coverWidget is None:
            return

        coverWidget.setFixedSize(new_width, int(new_width * 1.2))
        self.coverLabel.setFixedSize(new_width, int(new_width * 1.2))

        label_ref = weakref.ref(self.coverLabel)
        def on_cover_loaded(pixmap):
            label = label_ref()
            if label:
                scaled_pixmap = pixmap.scaled(new_width, int(new_width * 1.2), Qt.AspectRatioMode.KeepAspectRatioByExpanding, Qt.TransformationMode.SmoothTransformation)
                rounded_pixmap = round_corners(scaled_pixmap, 15)
                label.setPixmap(rounded_pixmap)

        load_pixmap_async(self.cover_path or "", new_width, int(new_width * 1.2), on_cover_loaded)

        # Обновляем размеры и шрифты бейджей
        badge_width = int(new_width * 2/3)
        icon_size = int(new_width * 0.06)
        icon_space = int(new_width * 0.012)
        for label in [self.steamLabel, self.egsLabel, self.portprotonLabel, self.protondbLabel, self.anticheatLabel]:
            if label is not None:
                label.setFixedWidth(badge_width)
                label.setIconSize(icon_size, icon_space)
                label.setCardWidth(new_width)  # Пересчитываем размер шрифта

        # Перепозиционируем бейджи
        self._position_badges(new_width)

        self.update()

    def update_badge_visibility(self, display_filter: str):
        """Обновляет видимость бейджей на основе display_filter."""
        self.display_filter = display_filter
        self.steam_visible = (str(self.game_source).lower() == "steam" and display_filter in ("all", "favorites"))
        self.egs_visible = (str(self.game_source).lower() == "epic" and display_filter in ("all", "favorites"))
        self.portproton_visible = (str(self.game_source).lower() == "portproton" and display_filter in ("all", "favorites"))
        protondb_visible = bool(self.getProtonDBText(self.protondb_tier))
        anticheat_visible = bool(self.getAntiCheatText(self.anticheat_status))

        # Обновляем видимость бейджей
        self.steamLabel.setVisible(self.steam_visible)
        self.egsLabel.setVisible(self.egs_visible)
        self.portprotonLabel.setVisible(self.portproton_visible)
        self.protondbLabel.setVisible(protondb_visible)
        self.anticheatLabel.setVisible(anticheat_visible)

        # Перепозиционируем бейджи
        self._position_badges(self.card_width)

    def _show_context_menu(self, pos):
        """Delegate context menu display to ContextMenuManager."""
        if self.context_menu_manager:
            self.context_menu_manager.show_context_menu(self, pos)

    @staticmethod
    def getAntiCheatText(status: str) -> str:
        if not status:
            return ""
        translations = {
            "supported": _("Supported"),
            "running": _("Running"),
            "planned": _("Planned"),
            "broken":  _("Broken"),
            "denied": _("Denied")
        }
        return translations.get(status.lower(), "")

    @staticmethod
    def getAntiCheatIconFilename(status: str) -> str:
        status = status.lower()
        if status in ("supported"):
            return "ac_supported"
        elif status in ("running"):
            return "ac_running"
        elif status in ("planned"):
            return "ac_planned"
        elif status in ("denied"):
            return "ac_denied"
        elif status in ("broken"):
            return "ac_broken"
        return ""

    @staticmethod
    def getProtonDBText(tier: str) -> str:
        if not tier:
            return ""
        translations = {
            "platinum": _("Platinum"),
            "gold": _("Gold"),
            "silver":  _("Silver"),
            "bronze": _("Bronze"),
            "borked": _("Broken"),
            "pending":  _("Pending")
        }
        return translations.get(tier.lower(), "")

    @staticmethod
    def getProtonDBIconFilename(tier: str) -> str:
        tier = tier.lower()
        if tier in ("platinum", "gold"):
            return "platinum-gold"
        elif tier in ("silver", "bronze"):
            return "silver-bronze"
        elif tier in ("borked", "pending"):
            return "broken"
        return ""

    def open_portproton_forum_topic(self):
        """Open the PortProton forum topic or search page for this game."""
        result = self.portproton_api.get_forum_topic_slug(self.name)
        base_url = "https://linux-gaming.ru/"
        if result.startswith("search?q="):
            url = QUrl(f"{base_url}{result}")
        else:
            url = QUrl(f"{base_url}t/{result}")
        QDesktopServices.openUrl(url)

    def open_protondb_report(self):
        url = QUrl(f"https://www.protondb.com/app/{self.appid}")
        QDesktopServices.openUrl(url)

    def open_steam_page(self):
        url = QUrl(f"https://steamcommunity.com/app/{self.appid}")
        QDesktopServices.openUrl(url)

    def open_weanticheatyet_page(self):
        formatted_name = self.name.lower().replace(" ", "-")
        url = QUrl(f"https://areweanticheatyet.com/game/{formatted_name}")
        QDesktopServices.openUrl(url)

    def update_favorite_icon(self):
        if self.is_favorite:
            self.favoriteLabel.setText("★")
        else:
            self.favoriteLabel.setText("☆")
        self.favoriteLabel.setStyleSheet(self.theme.FAVORITE_LABEL_STYLE)

    def toggle_favorite(self):
        favorites = read_favorites()
        if self.is_favorite:
            if self.name in favorites:
                favorites.remove(self.name)
            self.is_favorite = False
        else:
            if self.name not in favorites:
                favorites.append(self.name)
            self.is_favorite = True
        save_favorites(favorites)
        self.update_favorite_icon()

    def getBorderWidth(self) -> int:
        return self._borderWidth

    def setBorderWidth(self, value: int):
        if self._borderWidth != value:
            self._borderWidth = value
            self.borderWidthChanged.emit()
            self.update()

    def getGradientAngle(self) -> float:
        return self._gradientAngle

    def setGradientAngle(self, value: float):
        if self._gradientAngle != value:
            self._gradientAngle = value
            self.gradientAngleChanged.emit()
            self.update()

    borderWidth = Property(int, getBorderWidth, setBorderWidth, None, "", notify=cast(Callable[[], None], borderWidthChanged))
    gradientAngle = Property(float, getGradientAngle, setGradientAngle, None, "", notify=cast(Callable[[], None], gradientAngleChanged))

    def paintEvent(self, event):
        super().paintEvent(event)
        self.animations.paint_border(QPainter(self))

    def enterEvent(self, event):
        self.animations.handle_enter_event()
        super().enterEvent(event)

    def leaveEvent(self, event):
        self.animations.handle_leave_event()
        super().leaveEvent(event)

    def focusInEvent(self, event):
        self.animations.handle_focus_in_event()
        super().focusInEvent(event)

    def focusOutEvent(self, event):
        self.animations.handle_focus_out_event()
        super().focusOutEvent(event)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.select_callback(
                self.name,
                self.description,
                self.cover_path,
                self.appid,
                self.controller_support,
                self.exec_line,
                self.last_launch,
                self.formatted_playtime,
                self.protondb_tier,
                self.game_source,
                self.anticheat_status
            )
        super().mousePressEvent(event)

    def keyPressEvent(self, event):
        if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
            self.select_callback(
                self.name,
                self.description,
                self.cover_path,
                self.appid,
                self.controller_support,
                self.exec_line,
                self.last_launch,
                self.formatted_playtime,
                self.protondb_tier,
                self.game_source,
                self.anticheat_status
            )
        else:
            super().keyPressEvent(event)
