"""Fila de la llista: la fila sencera actua com un botó, amb estil de targeta.

Un clic fa l'acció principal segons l'estat:
  - no descarregat  → descarrega
  - update pendent  → actualitza
  - descarregat i al dia → juga
A la dreta un indicador de text (passiu) mostra què farà el clic. Sota el títol i la
descripció es pinten 'pills' amb la metadata: estat, versió, data, jugadors, autor i
els topics de Gitea. L'esborrat es gestiona per fora; aquest widget només l'exposa.
"""

from __future__ import annotations

from pathlib import Path

from PySide6.QtCore import QRectF, Qt, Signal
from PySide6.QtGui import QPainter, QPainterPath, QPalette, QPixmap
from PySide6.QtWidgets import (
    QApplication,
    QFrame,
    QHBoxLayout,
    QLabel,
    QSizePolicy,
    QVBoxLayout,
    QWidget,
)

from ..config import Game
from ..metadata import GameMeta, icon_path, load_meta
from ..paths import repo_dir
from .flow_layout import FlowLayout

ICON_SIZE = 64
ICON_RADIUS = 14

# Colors d'estat (text + fons translúcid del pill).
_STATUS_COLORS = {
    "none": ("#cc8855", "rgba(204, 136, 85, 0.18)"),    # no descarregat
    "update": ("#e0a030", "rgba(224, 160, 48, 0.20)"),  # actualització disponible
    "ok": ("#6fae6f", "rgba(111, 174, 111, 0.18)"),     # descarregat i al dia
}
_NEUTRAL_BG = "rgba(127, 127, 127, 0.18)"  # pills informatius (segueix clar/fosc)


def _rounded_pixmap(src: QPixmap, size: int, radius: int) -> QPixmap:
    """Escala omplint el quadrat, retalla al centre i arrodoneix les cantonades."""
    scaled = src.scaled(
        size, size, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation
    )
    x = max(0, (scaled.width() - size) // 2)
    y = max(0, (scaled.height() - size) // 2)
    cropped = scaled.copy(x, y, size, size)

    out = QPixmap(size, size)
    out.fill(Qt.transparent)
    painter = QPainter(out)
    painter.setRenderHint(QPainter.Antialiasing)
    path = QPainterPath()
    path.addRoundedRect(QRectF(0, 0, size, size), radius, radius)
    painter.setClipPath(path)
    painter.drawPixmap(0, 0, cropped)
    painter.end()
    return out


def _palette_text_color() -> str:
    """Color de text actual de la paleta, com a rgb() concret (no 'palette(text)',
    que Qt cacheja als stylesheets i no es refresca en canviar de tema)."""
    c = QApplication.palette().color(QPalette.WindowText)
    return f"rgb({c.red()}, {c.green()}, {c.blue()})"


def _make_pill(text: str, fg: str | None = None, bg: str = _NEUTRAL_BG,
               bold: bool = False) -> QLabel:
    """Una 'pill' arrodonida amb fons translúcid; passiva al ratolí.

    Sense `fg` explícit s'usa el color de text de la paleta vigent (pills neutres).
    """
    pill = QLabel(text)
    pill.setAttribute(Qt.WA_TransparentForMouseEvents)
    weight = "600" if bold else "normal"
    pill.setStyleSheet(
        f"QLabel {{ background: {bg}; color: {fg or _palette_text_color()}; "
        f"border-radius: 9px; padding: 2px 8px; font-size: 11px; "
        f"font-weight: {weight}; }}"
    )
    return pill


class GameRow(QFrame):
    """Fila clicable. Emet `activated` en clic i `delete_requested` per a l'esborrat."""

    activated = Signal(object)         # Game — clic sobre la fila (acció principal)
    delete_requested = Signal(object)  # Game — esborrar la descàrrega local

    def __init__(self, game: Game, root: Path, parent=None) -> None:
        super().__init__(parent)
        self.game = game
        self.root = root
        self._update_available = False
        self._busy = False
        self._delete_mode = False
        self.setObjectName("gameRow")
        self.setFrameShape(QFrame.StyledPanel)
        self.setCursor(Qt.PointingHandCursor)
        self.setStyleSheet(
            "QFrame#gameRow:hover { background: rgba(127, 127, 127, 0.15); }"
        )

        layout = QHBoxLayout(self)
        layout.setContentsMargins(10, 8, 10, 8)
        layout.setSpacing(12)

        # --- Icona del joc (arrodonida) ---
        self.icon_label = QLabel()
        self.icon_label.setFixedSize(ICON_SIZE, ICON_SIZE)
        self.icon_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.icon_label, alignment=Qt.AlignTop)

        # --- Text: títol + descripció + pills (centrat verticalment) ---
        text_box = QVBoxLayout()
        text_box.setSpacing(4)

        self.name_label = QLabel(game.name)
        self.name_label.setStyleSheet("font-weight: bold; font-size: 18px;")
        self.desc_label = QLabel("")
        self.desc_label.setWordWrap(True)
        self.desc_label.setForegroundRole(QPalette.PlaceholderText)

        # Contenidor de pills amb FlowLayout (envolten si no caben).
        self.pills_box = QWidget()
        self.pills_layout = FlowLayout(self.pills_box, spacing=6)
        self.pills_box.setAttribute(Qt.WA_TransparentForMouseEvents)
        # Sense fons propi: que no pinti cap banda darrere dels pills.
        self.pills_box.setAttribute(Qt.WA_NoSystemBackground, True)
        self.pills_box.setAutoFillBackground(False)
        sp = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sp.setHeightForWidth(True)
        self.pills_box.setSizePolicy(sp)

        text_box.addStretch(1)
        text_box.addWidget(self.name_label)
        text_box.addWidget(self.desc_label)
        text_box.addWidget(self.pills_box)
        text_box.addStretch(1)
        layout.addLayout(text_box, stretch=1)

        # --- Indicador d'acció (passiu, text): què farà el clic ---
        self.action_label = QLabel()
        self.action_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.action_label.setMinimumWidth(96)
        layout.addWidget(self.action_label)

        for child in (self.name_label, self.desc_label, self.action_label):
            child.setAttribute(Qt.WA_TransparentForMouseEvents)

        self.refresh()

    # ------------------------------------------------------------- clic fila

    def mouseReleaseEvent(self, event) -> None:
        if (
            event.button() == Qt.LeftButton
            and not self._busy
            and self.rect().contains(event.position().toPoint())
        ):
            self.activated.emit(self.game)
        super().mouseReleaseEvent(event)

    # ----------------------------------------------------------------- estat

    def is_installed(self) -> bool:
        return (repo_dir(self.root, self.game.id) / ".git").exists()

    def primary_action_is_download(self) -> bool:
        """True si el clic ha de descarregar/actualitzar; False si ha de jugar."""
        return (not self.is_installed()) or self._update_available

    def set_update_available(self, available: bool) -> None:
        """Marca/desmarca la fila com a 'té actualització pendent'."""
        self._update_available = available
        self.refresh()

    def set_delete_mode(self, on: bool) -> None:
        """Activa/desactiva el mode esborrar (canvia el text d'acció a 'Esborra')."""
        self._delete_mode = on
        self.refresh()

    def refresh(self) -> None:
        """Recarrega icona, descripció, pills i estat des de la cache local."""
        meta = load_meta(self.root, self.game.id)
        self._set_icon()
        self.desc_label.setText(meta.description or "(sense descripció encara)")
        self._rebuild_pills(meta)
        self._set_action(meta)

    def _set_icon(self) -> None:
        path = icon_path(self.root, self.game.id)
        pixmap = QPixmap(str(path)) if path.exists() else QPixmap()
        if pixmap.isNull():
            self.icon_label.setText("🎮")
            self.icon_label.setStyleSheet("font-size: 32px;")
        else:
            self.icon_label.setStyleSheet("")
            self.icon_label.setPixmap(_rounded_pixmap(pixmap, ICON_SIZE, ICON_RADIUS))

    # ----------------------------------------------------------------- pills

    def _clear_pills(self) -> None:
        while self.pills_layout.count():
            item = self.pills_layout.takeAt(0)
            w = item.widget() if item else None
            if w is not None:
                w.deleteLater()

    def _rebuild_pills(self, meta: GameMeta) -> None:
        self._clear_pills()
        installed = self.is_installed()

        # 1) Estat (pill amb color).
        if not installed:
            fg, bg = _STATUS_COLORS["none"]
            self.pills_layout.addWidget(_make_pill("No descarregat", fg, bg, bold=True))
        elif self._update_available:
            fg, bg = _STATUS_COLORS["update"]
            self.pills_layout.addWidget(
                _make_pill("⬆ Actualització", fg, bg, bold=True)
            )
        else:
            fg, bg = _STATUS_COLORS["ok"]
            self.pills_layout.addWidget(_make_pill("✓ Descarregat", fg, bg, bold=True))

        # 2) Versió (només si està descarregat i la coneixem).
        if installed and meta.version:
            self.pills_layout.addWidget(_make_pill(meta.version))

        # 3) Data de llançament (creació del repo a Gitea).
        released = _year_month_day(meta.created_at)
        if released:
            self.pills_layout.addWidget(_make_pill(released))

        # 4) Jugadors i autor (manuals, de games.toml).
        if self.game.players:
            self.pills_layout.addWidget(_make_pill(self.game.players))
        if self.game.author:
            self.pills_layout.addWidget(_make_pill(self.game.author))

        # 5) Topics de Gitea.
        for topic in meta.topics:
            self.pills_layout.addWidget(_make_pill(topic))

    # --------------------------------------------------------------- acció

    def _set_action(self, meta: GameMeta) -> None:
        installed = self.is_installed()
        if self._delete_mode:
            if installed:
                self._set_action_text("Esborra", "#d9534f")
            else:
                self.action_label.clear()  # no es pot esborrar el que no està
        elif not installed:
            self._set_action_text("Descarrega", "#4a90d9")
        elif self._update_available:
            self._set_action_text("Actualitza", "#e0a030")
        else:
            self._set_action_text("Juga", "#6fae6f")

    def _set_action_text(self, text: str, color: str) -> None:
        self.action_label.setText(text)
        self.action_label.setStyleSheet(
            f"color: {color}; font-weight: bold; font-size: 13px;"
        )

    # --------------------------------------------------------------- busy UI

    def set_busy(self, busy: bool, message: str = "") -> None:
        self._busy = busy
        if busy and message:
            self._clear_pills()
            self.pills_layout.addWidget(_make_pill(message, "#d0d060"))


def _year_month_day(iso: str) -> str:
    """De un ISO-8601 (2026-04-05T20:26:09+02:00) treu '2026-04-05'; '' si no és vàlid."""
    if not iso or len(iso) < 10:
        return ""
    date = iso[:10]
    return date if date[4] == "-" and date[7] == "-" else ""
