import sys
import os
import sqlite3
import requests
import webbrowser
import feedparser
import re
import subprocess
import json
import threading
from pathlib import Path
from dataclasses import dataclass, field
from typing import List, Optional, Tuple, Dict, Set
from urllib.parse import urlencode, urlparse
from datetime import datetime, timedelta
from functools import lru_cache
from tenacity import retry, stop_after_attempt, wait_exponential
from concurrent.futures import ThreadPoolExecutor, as_completed
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QVBoxLayout, QWidget, QLineEdit, QPushButton,
    QLabel, QScrollArea, QListWidget, QInputDialog, QMessageBox,
    QHBoxLayout, QFrame, QMenu, QToolBar, QStatusBar, QGridLayout, QComboBox,
    QListWidgetItem, QCheckBox, QSizePolicy, QProgressBar, QShortcut,
    QAction, QFileDialog, QStackedWidget, QProgressDialog, QDialog
)
from PyQt5.QtGui import QPixmap, QIcon, QFont, QPalette, QColor, QMovie, QKeySequence
from PyQt5.QtCore import Qt, QObject, QRunnable, QThreadPool, pyqtSignal, QTimer, QSize, QDateTime
import logging
from logging.handlers import RotatingFileHandler
import yt_dlp
import time
import signal
from contextlib import contextmanager
from collections import deque
from bs4 import BeautifulSoup

# --- Limitatore di Frequenza API ---
class APIRateLimiter:
    MAX_CALLS = 5000  # Chiamate massime all'ora
    TIME_WINDOW = 3600  # 1 ora in secondi

    def __init__(self):
        self.call_times = deque()  # Memorizza i timestamp delle chiamate API
        self.lock = threading.Lock()  # Blocco thread-safe

    def increment(self) -> bool:
        """Registra una chiamata API e verifica se il limite è superato."""
        current_time = time.time()
        with self.lock:
            # Rimuove i timestamp più vecchi di 1 ora
            while self.call_times and (current_time - self.call_times[0] > self.TIME_WINDOW):
                self.call_times.popleft()
            # Verifica se il limite è superato
            if len(self.call_times) >= self.MAX_CALLS:
                logger.error(f"Limite di chiamate API superato: {len(self.call_times)}/{self.MAX_CALLS} nell'ultima ora")
                return False
            # Registra la chiamata corrente
            self.call_times.append(current_time)
            logger.info(f"Chiamata API {len(self.call_times)}/{self.MAX_CALLS} nell'ultima ora")
            return True

    def reset(self):
        """Resetta la cronologia delle chiamate (per test)."""
        with self.lock:
            self.call_times.clear()

    def remaining(self) -> int:
        """Restituisce il numero rimanente di chiamate API consentite nella finestra corrente."""
        current_time = time.time()
        with self.lock:
            # Rimuove i timestamp più vecchi di 1 ora
            while self.call_times and (current_time - self.call_times[0] > self.TIME_WINDOW):
                self.call_times.popleft()
            return max(0, self.MAX_CALLS - len(self.call_times))

# Inizializza il limitatore di frequenza
api_rate_limiter = APIRateLimiter()

# --- Utilità di Sicurezza ---
def validate_url(url: str) -> bool:
    """Valida se l'URL è ben formato e utilizza HTTPS."""
    if not url.startswith(("http://", "https://")):
        raise ValueError("URL non valido: deve iniziare con http:// o https://")
    parsed_url = urlparse(url)
    if not all([parsed_url.scheme, parsed_url.netloc]):
        raise ValueError("URL non valido: componenti mancanti o malformati.")
    return True

def safe_join(base: Path, path: str) -> Path:
    """Unisce i percorsi in modo sicuro per prevenire il directory traversal."""
    resolved_path = (base / path).resolve()
    if not resolved_path.is_relative_to(base):
        raise ValueError("Percorso non valido: rilevato directory traversal.")
    return resolved_path

# --- Applicazione HTTPS e Sanitizzazione Input ---
def secure_request(url: str, **kwargs) -> requests.Response:
    """Effettua una richiesta HTTP sicura con applicazione HTTPS e verifica SSL."""
    validate_url(url)
    if not api_rate_limiter.increment():
        raise RuntimeError("Limite di chiamate API superato. Riprova più tardi.")
    return requests.get(url, verify=True, **kwargs)

# --- Configurazione del Logging ---
def configure_logging() -> Tuple[logging.Logger, Path]:
    """Configura il logging sicuro con permessi di directory ristretti."""
    log_dir = Path.home() / ".purpletube_player" if os.name != 'nt' else Path(os.getenv('APPDATA')) / "PurpleTube"
    log_path = log_dir / "app.log"
    try:
        log_dir.mkdir(parents=True, exist_ok=True)
        if os.name != 'nt':
            log_dir.chmod(0o700)  # Restringe l'accesso solo all'utente
    except Exception as e:
        logging.warning(f"[AVVISO] Impossibile creare {log_dir}. Errore: {e}")
        log_dir = Path(__file__).parent / "purpletube_logs"
        log_dir.mkdir(parents=True, exist_ok=True)
        log_path = log_dir / "app.log"
    logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        handlers=[
            RotatingFileHandler(log_path, maxBytes=1024 * 1024, backupCount=3, encoding='utf-8', delay=True),
            logging.StreamHandler()
        ]
    )
    return logging.getLogger(__name__), log_path

logger, log_path = configure_logging()

# --- Controlli Versione ---
def check_yt_dlp_version() -> None:
    """Controlla se yt-dlp è aggiornato."""
    try:
        result = subprocess.run(
            ["yt-dlp", "--version"],
            capture_output=True,
            text=True,
            check=True,
            shell=False
        )
        current_version = result.stdout.strip()
        logger.info(f"Versione yt-dlp: {current_version}")
        try:
            latest_version = requests.get(
                "https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest",
                timeout=5,
                verify=True
            ).json()["tag_name"]
            logger.info(f"Ultima versione disponibile: {latest_version}")
            if current_version != latest_version:
                logger.warning(
                    f"Attenzione: yt-dlp ({current_version}) è obsoleto. "
                    f"L'ultima versione è {latest_version}. "
                    "Aggiorna con: pip install --upgrade yt-dlp"
                )
        except Exception as e:
            logger.warning(f"Impossibile controllare l'ultima versione di yt-dlp: {e}")
    except FileNotFoundError:
        logger.error("yt-dlp non trovato. Installalo con: pip install yt-dlp")
        sys.exit(1)
    except subprocess.CalledProcessError as e:
        logger.error(f"Errore nel controllo della versione di yt-dlp: {e}")
        sys.exit(1)

def check_mpv_installed() -> bool:
    """Controlla se mpv è installato."""
    try:
        subprocess.run(
            ["mpv", "--version"],
            check=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            shell=False
        )
        logger.info("mpv è installato.")
        return True
    except FileNotFoundError:
        logger.warning("mpv non è installato. I video verranno aperti nel browser predefinito.")
        return False

# --- Modelli Dati ---
@dataclass
class Video:
    title: str
    url: str
    thumbnail_url: str
    duration: str
    video_id: str
    platform: str = "PeerTube"
    author: str = "Sconosciuto"
    published: Optional[datetime] = None

    def to_dict(self) -> Dict:
        return {
            'title': self.title,
            'url': self.url,
            'thumbnail_url': self.thumbnail_url,
            'duration': self.duration,
            'video_id': self.video_id,
            'platform': self.platform,
            'author': self.author,
            'published': self.published.isoformat() if self.published else None
        }

@dataclass
class Playlist:
    name: str
    videos: List[Video] = field(default_factory=list)
    id: Optional[int] = None

    def add_video(self, video: Video) -> bool:
        if video.video_id not in [v.video_id for v in self.videos]:
            self.videos.append(video)
            return True
        return False

    def remove_video(self, video_id: str) -> bool:
        for i, video in enumerate(self.videos):
            if video.video_id == video_id:
                self.videos.pop(i)
                return True
        return False

@dataclass
class RSSFeed:
    url: str
    title: str
    id: Optional[int] = None
    last_updated: Optional[datetime] = None
    is_channel: bool = False

@dataclass
class FeedItem:
    title: str
    url: str
    thumbnail_url: str
    duration: str
    video_id: str
    published: datetime
    author: str
    platform: str = "PeerTube"

# --- Gestore Database ---
class DBManager:
    def __init__(self) -> None:
        if os.name == 'nt':
            self.DB_DIR = Path(os.getenv('APPDATA')) / "purpleTube"
        else:
            self.DB_DIR = Path.home() / ".purpletube_player"
        self.DB_PATH = self.DB_DIR / "database.db"
        try:
            self.DB_DIR.mkdir(parents=True, exist_ok=True)
            if os.name != 'nt':
                self.DB_DIR.chmod(0o700)  # Restringe l'accesso solo all'utente
            test_file = self.DB_DIR / ".write_test"
            test_file.touch()
            test_file.unlink()
        except Exception as e:
            logger.error(f"Impossibile configurare {self.DB_DIR}: {e}")
            self.DB_DIR = Path(__file__).parent / "purpletube_data"
            self.DB_DIR.mkdir(parents=True, exist_ok=True)
            if os.name != 'nt':
                self.DB_DIR.chmod(0o700)
        self.DB_PATH = self.DB_DIR / "database.db"
        self._init_db()
        self._create_indices()
        self._verify_db_integrity()
        self._verify_setup()

    def _verify_setup(self) -> None:
        errors = []
        if not self.DB_DIR.exists():
            errors.append(f"La directory {self.DB_DIR} non esiste.")
        else:
            if not os.access(self.DB_DIR, os.W_OK):
                errors.append(f"Permessi insufficienti per scrivere in {self.DB_DIR}")
        if not log_path.exists():
            try:
                with open(log_path, 'a'):
                    pass
            except Exception as e:
                errors.append(f"Impossibile creare {log_path}: {e}")
        if errors:
            logger.error("Problemi di configurazione:")
            for error in errors:
                logger.error(f"  - {error}")
            logger.error("Tentativo con directory di fallback...")
            self.DB_DIR = Path(__file__).parent / "purpletube_data"
            self.DB_PATH = self.DB_DIR / "database.db"
            self.DB_DIR.mkdir(parents=True, exist_ok=True)
            if os.name != 'nt':
                self.DB_DIR.chmod(0o700)
            logger.error(f"Nuovo percorso del database: {self.DB_DIR}")

    def _init_db(self) -> None:
        try:
            with sqlite3.connect(self.DB_PATH) as conn:
                conn.execute("PRAGMA foreign_keys = ON")
                conn.execute("PRAGMA journal_mode = WAL")
                conn.execute("PRAGMA synchronous = NORMAL")
                conn.execute("""
                CREATE TABLE IF NOT EXISTS playlists (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name TEXT UNIQUE NOT NULL,
                    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
                    updated_at TEXT DEFAULT CURRENT_TIMESTAMP
                );
                """)
                conn.execute("""
                CREATE TABLE IF NOT EXISTS playlist_videos (
                    playlist_id INTEGER NOT NULL,
                    video_id TEXT NOT NULL,
                    position INTEGER DEFAULT 0,
                    title TEXT NOT NULL,
                    url TEXT NOT NULL,
                    thumbnail_url TEXT,
                    duration TEXT DEFAULT '0',
                    platform TEXT DEFAULT 'PeerTube',
                    author TEXT DEFAULT 'Unknown',
                    published TEXT,
                    added_at TEXT DEFAULT CURRENT_TIMESTAMP,
                    PRIMARY KEY (playlist_id, video_id),
                    FOREIGN KEY (playlist_id) REFERENCES playlists(id) ON DELETE CASCADE
                );
                """)
                conn.execute("""
                CREATE TABLE IF NOT EXISTS rss_feeds (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    url TEXT UNIQUE NOT NULL,
                    title TEXT NOT NULL,
                    last_updated TEXT,
                    is_channel INTEGER DEFAULT 0,
                    created_at TEXT DEFAULT CURRENT_TIMESTAMP
                );
                """)
                conn.execute("""
                CREATE TABLE IF NOT EXISTS feed_items (
                    feed_id INTEGER NOT NULL,
                    video_id TEXT NOT NULL,
                    title TEXT NOT NULL,
                    url TEXT NOT NULL,
                    thumbnail_url TEXT,
                    duration TEXT DEFAULT '0',
                    published TEXT NOT NULL,
                    author TEXT DEFAULT 'Unknown',
                    platform TEXT DEFAULT 'PeerTube',
                    added_at TEXT DEFAULT CURRENT_TIMESTAMP,
                    PRIMARY KEY (feed_id, video_id),
                    FOREIGN KEY (feed_id) REFERENCES rss_feeds(id) ON DELETE CASCADE
                );
                """)
                conn.execute("""
                CREATE TABLE IF NOT EXISTS user_preferences (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    key TEXT UNIQUE NOT NULL,
                    value TEXT,
                    updated_at TEXT DEFAULT CURRENT_TIMESTAMP
                );
                """)
                conn.execute("INSERT OR IGNORE INTO playlists (name) VALUES ('Guarda più tardi')")
                conn.execute("INSERT OR IGNORE INTO user_preferences (key, value) VALUES ('player', 'Default Browser')")
                conn.execute("INSERT OR IGNORE INTO user_preferences (key, value) VALUES ('mpv_buffer', 'False')")
                conn.commit()
                logger.info(f"Database inizializzato in: {self.DB_PATH}")
        except sqlite3.Error as e:
            logger.error(f"Errore nell'inizializzazione del database: {e}")
            raise

    def _create_indices(self) -> None:
        with sqlite3.connect(self.DB_PATH) as conn:
            conn.execute("CREATE INDEX IF NOT EXISTS idx_playlist_videos_playlist ON playlist_videos(playlist_id)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_playlist_videos_position ON playlist_videos(playlist_id, position)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_playlist_videos_video ON playlist_videos(video_id)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_playlist_videos_added ON playlist_videos(added_at)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_feed_items_feed ON feed_items(feed_id)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_feed_items_published ON feed_items(published)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_feed_items_video ON feed_items(video_id)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_feed_items_added ON feed_items(added_at)")
            conn.commit()

    def _verify_db_integrity(self) -> None:
        with sqlite3.connect(self.DB_PATH) as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT 1 FROM playlists WHERE name = 'Guarda più tardi'")
            if not cursor.fetchone():
                cursor.execute("INSERT INTO playlists (name) VALUES ('Guarda più tardi')")
            cursor.execute("""
            DELETE FROM playlist_videos
            WHERE playlist_id NOT IN (SELECT id FROM playlists)
            """)
            cursor.execute("""
            DELETE FROM feed_items
            WHERE feed_id NOT IN (SELECT id FROM rss_feeds)
            """)
            conn.commit()

    def get_connection(self) -> sqlite3.Connection:
        conn = sqlite3.connect(self.DB_PATH)
        conn.execute("PRAGMA foreign_keys = ON")
        return conn

    def clear_all_user_data(self) -> bool:
        try:
            with sqlite3.connect(self.DB_PATH) as conn:
                cursor = conn.cursor()
                cursor.execute("SELECT id FROM playlists WHERE name = 'Guarda più tardi'")
                wl_id = cursor.fetchone()
                cursor.execute("DELETE FROM playlist_videos")
                cursor.execute("DELETE FROM playlists WHERE name != 'Guarda più tardi'")
                cursor.execute("DELETE FROM feed_items")
                cursor.execute("DELETE FROM rss_feeds")
                cursor.execute("DELETE FROM user_preferences WHERE key != 'player' AND key != 'mpv_buffer'")
                if not wl_id:
                    cursor.execute("INSERT OR IGNORE INTO playlists (name) VALUES ('Guarda più tardi')")
                conn.commit()
            return True
        except Exception as e:
            logger.error(f"Errore nella cancellazione dei dati utente: {e}")
            return False

    def close(self) -> None:
        pass

db_manager = DBManager()

# --- Estrazione Feed RSS ---
def extract_video_channel_id(video_url: str) -> Optional[str]:
    """Estrae l'ID del canale da un URL video di PeerTube."""
    try:
        validate_url(video_url)
        response = secure_request(video_url)
        soup = BeautifulSoup(response.text, 'html.parser')
        scripts = soup.find_all('script')
        for script in scripts:
            script_text = script.string
            if script_text and 'videoChannelId' in script_text:
                match = re.search(r'videoChannelId[\'"\s:]=(\d+)', script_text)
                if match:
                    return match.group(1)
        elements_with_data = soup.find_all(attrs={"data-video-channel-id": True})
        if elements_with_data:
            return elements_with_data[0]['data-video-channel-id']
        video_uuid = video_url.split('/')[-1]
        api_url = f"https://{urlparse(video_url).netloc}/api/v1/videos/{video_uuid}"
        api_response = secure_request(api_url)
        if api_response.status_code == 200:
            api_data = api_response.json()
            if 'channel' in api_data and 'id' in api_data['channel']:
                return str(api_data['channel']['id'])
        logger.warning("Impossibile trovare videoChannelId.")
        return None
    except Exception as e:
        logger.error(f"Errore nell'estrazione dell'ID del canale: {e}")
        return None

def extract_channel_id(video_url: str) -> Optional[str]:
    """Estrae l'ID del canale da un URL video di PeerTube."""
    try:
        channel_id = extract_video_channel_id(video_url)
        if channel_id:
            return channel_id
        response = secure_request(video_url)
        soup = BeautifulSoup(response.text, 'html.parser')
        rss_link = soup.find('a', href=lambda href: href and '/feeds/videos.xml?videoChannelId=' in href)
        if rss_link:
            channel_id = rss_link['href'].split('videoChannelId=')[1]
            return channel_id
    except Exception as e:
        logger.error(f"Errore nell'estrazione dell'ID del canale: {e}")
        return None

def generate_rss_url(video_url: str, channel_id: str) -> str:
    """Genera l'URL del feed RSS per un canale PeerTube."""
    base_domain = f"{urlparse(video_url).scheme}://{urlparse(video_url).netloc}"
    return f"{base_domain}/feeds/videos.xml?videoChannelId={channel_id}"

def extract_channel_feed_url(channel_url: str) -> Optional[str]:
    """Estrae l'URL del feed RSS da un URL canale di PeerTube."""
    try:
        validate_url(channel_url)
        channel_id = extract_channel_id(channel_url)
        if not channel_id:
            return None
        return generate_rss_url(channel_url, channel_id)
    except Exception as e:
        logger.error(f"Errore nell'estrazione del feed RSS: {e}")
        return None

# --- Spinner di Caricamento ---
class LoadingSpinner(QFrame):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFixedSize(60, 60)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setFocusPolicy(Qt.NoFocus)
        self.setStyleSheet("""
            LoadingSpinner {
                background: transparent;
                border: none;
            }
            QLabel {
                background: transparent;
                border: none;
            }
        """)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.spinner_label = QLabel()
        self.spinner_label.setFixedSize(60, 60)
        self.spinner_label.setAlignment(Qt.AlignCenter)
        self.spinner_label.setStyleSheet("""
            QLabel {
                background: transparent;
                border: none;
            }
        """)
        self.movie = QMovie("spinner.gif")
        self.movie.setScaledSize(QSize(60, 60))
        self.spinner_label.setMovie(self.movie)
        layout.addWidget(self.spinner_label)

    def showEvent(self, event):
        super().showEvent(event)
        self.movie.start()

    def hideEvent(self, event):
        super().hideEvent(event)
        self.movie.stop()

# --- Servizi ---
class PlaylistService:
    @staticmethod
    @lru_cache(maxsize=1)
    def get_watch_later_playlist() -> Optional[Playlist]:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id FROM playlists WHERE name = 'Guarda più tardi'")
            row = cursor.fetchone()
            if row:
                pid = row[0]
                cursor.execute("""
                    SELECT video_id, title, url, thumbnail_url, duration, platform, author, published
                    FROM playlist_videos
                    WHERE playlist_id = ?
                    ORDER BY position ASC
                """, (pid,))
                videos = []
                for vid, t, u, thumb, d, plat, auth, pub in cursor.fetchall():
                    try:
                        video = Video(
                            title=t or "Nessun Titolo",
                            url=u or "",
                            thumbnail_url=thumb or "",
                            duration=d or "0",
                            video_id=vid,
                            platform=plat or "PeerTube",
                            author=auth or "Sconosciuto",
                            published=datetime.fromisoformat(pub) if pub else None
                        )
                        videos.append(video)
                    except Exception as e:
                        logger.error(f"Errore nel parsing del video {vid}: {e}")
                return Playlist(name="Guarda più tardi", videos=videos, id=pid)
        return None

    @staticmethod
    def get_playlists() -> List[Playlist]:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id, name FROM playlists")
            playlists = []
            for pid, name in cursor.fetchall():
                cursor.execute("""
                    SELECT video_id, title, url, thumbnail_url, duration, platform, author, published
                    FROM playlist_videos
                    WHERE playlist_id = ?
                    ORDER BY position ASC
                """, (pid,))
                videos = []
                for vid, t, u, thumb, d, plat, auth, pub in cursor.fetchall():
                    try:
                        video = Video(
                            title=t or "Nessun Titolo",
                            url=u or "",
                            thumbnail_url=thumb or "",
                            duration=d or "0",
                            video_id=vid,
                            platform=plat or "PeerTube",
                            author=auth or "Sconosciuto",
                            published=datetime.fromisoformat(pub) if pub else None
                        )
                        videos.append(video)
                    except Exception as e:
                        logger.error(f"Errore nel parsing del video {vid}: {e}")
                playlists.append(Playlist(name=name, videos=videos, id=pid))
            return playlists

    @staticmethod
    def get_playlist_videos(playlist_id: int) -> List[Video]:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT video_id, title, url, thumbnail_url, duration, platform, author, published
                FROM playlist_videos
                WHERE playlist_id = ?
                ORDER BY position ASC
            """, (playlist_id,))
            videos = []
            for vid, t, u, thumb, d, plat, auth, pub in cursor.fetchall():
                try:
                    video = Video(
                        title=t or "Nessun Titolo",
                        url=u or "",
                        thumbnail_url=thumb or "",
                        duration=d or "0",
                        video_id=vid,
                        platform=plat or "PeerTube",
                        author=auth or "Sconosciuto",
                        published=datetime.fromisoformat(pub) if pub else None
                    )
                    videos.append(video)
                except Exception as e:
                    logger.error(f"Errore nel parsing del video {vid}: {e}")
            return videos

    @staticmethod
    def save_playlist(playlist: Playlist) -> int:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("INSERT INTO playlists (name) VALUES (?)", (playlist.name,))
            playlist.id = cursor.lastrowid
            conn.commit()
        return playlist.id

    @staticmethod
    def add_to_playlist(playlist_id: int, video: Video) -> bool:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute(
                "SELECT COUNT(*) FROM playlist_videos WHERE playlist_id=? AND video_id=?",
                (playlist_id, video.video_id)
            )
            if cursor.fetchone()[0] > 0:
                return False
            cursor.execute(
                "SELECT COALESCE(MAX(position),0) FROM playlist_videos WHERE playlist_id=?",
                (playlist_id,)
            )
            max_pos = cursor.fetchone()[0] or 0
            cursor.execute("""
                INSERT INTO playlist_videos
                (playlist_id, video_id, position, title, url, thumbnail_url, duration, platform, author, published)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (
                playlist_id, video.video_id, max_pos+1, video.title, video.url,
                video.thumbnail_url, video.duration, video.platform, video.author,
                video.published.isoformat() if video.published else None
            ))
            cursor.execute("""
                UPDATE playlists
                SET updated_at = CURRENT_TIMESTAMP
                WHERE id = ?
            """, (playlist_id,))
            conn.commit()
        return True

    @staticmethod
    def remove_from_playlist(playlist_id: int, video_id: str) -> bool:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute(
                "SELECT position FROM playlist_videos WHERE playlist_id=? AND video_id=?",
                (playlist_id, video_id)
            )
            pos_row = cursor.fetchone()
            if not pos_row:
                return False
            pos = pos_row[0]
            cursor.execute(
                "DELETE FROM playlist_videos WHERE playlist_id=? AND video_id=?",
                (playlist_id, video_id)
            )
            cursor.execute("""
                UPDATE playlist_videos
                SET position = position - 1
                WHERE playlist_id = ? AND position > ?
            """, (playlist_id, pos))
            cursor.execute("""
                UPDATE playlists
                SET updated_at = CURRENT_TIMESTAMP
                WHERE id = ?
            """, (playlist_id,))
            conn.commit()
        return True

    @staticmethod
    def remove_playlist(playlist_name: str) -> bool:
        if playlist_name == "Guarda più tardi":
            return False
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id FROM playlists WHERE name=?", (playlist_name,))
            row = cursor.fetchone()
            if not row:
                return False
            pid = row[0]
            cursor.execute("DELETE FROM playlists WHERE id=?", (pid,))
            conn.commit()
        return True

    @staticmethod
    def update_playlist_name(playlist_id: int, new_name: str) -> bool:
        if new_name == "Guarda più tardi":
            return False
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT 1 FROM playlists WHERE name = ? AND id != ?", (new_name, playlist_id))
            if cursor.fetchone():
                return False
            cursor.execute("""
                UPDATE playlists
                SET name = ?, updated_at = CURRENT_TIMESTAMP
                WHERE id = ?
            """, (new_name, playlist_id))
            conn.commit()
        return True

    @staticmethod
    def reorder_playlist_videos(playlist_id: int, video_ids: List[str]) -> bool:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT COUNT(*)
                FROM playlist_videos
                WHERE playlist_id = ? AND video_id IN ({})
            """.format(','.join(['?']*len(video_ids))), [playlist_id] + video_ids)
            if cursor.fetchone()[0] != len(video_ids):
                return False
            for position, video_id in enumerate(video_ids):
                cursor.execute("""
                    UPDATE playlist_videos
                    SET position = ?
                    WHERE playlist_id = ? AND video_id = ?
                """, (position, playlist_id, video_id))
            cursor.execute("""
                UPDATE playlists
                SET updated_at = CURRENT_TIMESTAMP
                WHERE id = ?
            """, (playlist_id,))
            conn.commit()
        return True

class SearchService:
    SESSION = requests.Session()
    SESSION.headers.update({"User-Agent": "purpleTubePlayer/1.0"})

    @staticmethod
    @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
    def search_videos(
        query: str,
        page: int = 1,
        count: int = 50,
        sort_by: str = "recent",
        language: str = None,
        category: str = None,
        duration: str = None,
        published_after: str = None
    ) -> Tuple[List[Video], int]:
        try:
            if not api_rate_limiter.increment():
                raise RuntimeError("Limite di chiamate API superato. Riprova più tardi.")
            params = {
                'search': query,
                'start': (page-1)*count,
                'count': count,
                'nsfw': 'false'
            }
            if language:
                params['languageOneOf'] = language
            if duration:
                params['durationRange'] = duration
            if category:
                params['categoryOneOf'] = category
            if published_after:
                params['publishedAfter'] = published_after
            if sort_by == "recent":
                params['sort'] = '-publishedAt'
            elif sort_by == "views":
                params['sort'] = '-views'
            elif sort_by == "duration":
                params['sort'] = 'duration'
            else:
                params['sort'] = '-match'
            url = f"https://sepiasearch.org/api/v1/search/videos?{urlencode(params)}"
            resp = SearchService.SESSION.get(url, timeout=10, verify=True)
            resp.raise_for_status()
            data = resp.json()
            videos = []
            for item in data.get("data", []):
                try:
                    video = Video(
                        title=item.get("name", "Nessun Titolo"),
                        url=item.get("url", ""),
                        thumbnail_url=item.get("thumbnailUrl", ""),
                        duration=str(item.get("duration", 0)),
                        video_id=str(item.get("uuid", "")),
                        author=item.get("account", {}).get("name", "Sconosciuto"),
                        published=datetime.fromisoformat(item.get("publishedAt").replace('Z', '+00:00')) if item.get("publishedAt") else None
                    )
                    videos.append(video)
                except Exception as e:
                    logger.error(f"Errore nel parsing del video {item.get('uuid')}: {e}")
            return videos, data.get("total", 0)
        except Exception as e:
            logger.error(f"Errore SepiaSearch: {e}")
            return [], 0

class RSSService:
    SESSION = requests.Session()
    SESSION.headers.update({"User-Agent": "purpleTubePlayer/1.0"})
    _lock = threading.Lock()  # Blocco thread-safe per operazioni sul database

    @staticmethod
    @lru_cache(maxsize=32)
    def get_feed_items_cached(feed_id: int, limit: int = None) -> List[FeedItem]:
        return RSSService.get_feed_items(feed_id, limit)

    @staticmethod
    @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
    def parse_peertube_feed(feed_url: str) -> Tuple[Optional[RSSFeed], List[FeedItem]]:
        try:
            if not api_rate_limiter.increment():
                raise RuntimeError("Limite di chiamate API superato. Riprova più tardi.")
            validate_url(feed_url)
            feed = feedparser.parse(feed_url)
            if not feed.entries:
                logger.warning(f"Feed vuoto o non valido: {feed_url}")
                return None, []
            is_channel = "account" in feed.feed.get("link", "").lower() if feed.feed else False
            rss_feed = RSSFeed(
                url=feed_url,
                title=feed.feed.get("title", "Feed Senza Titolo"),
                last_updated=datetime.now(),
                is_channel=is_channel
            )
            items = []
            for entry in feed.entries:
                try:
                    duration = "0"
                    for tag in entry.get("media_content", []):
                        if isinstance(tag, dict) and tag.get("medium") == "video":
                            duration = str(tag.get("duration", "0"))
                            break
                    thumbnail_url = ""
                    if hasattr(entry, "media_thumbnail"):
                        thumbnail_url = entry.media_thumbnail[0]["url"] if entry.media_thumbnail else ""
                    elif hasattr(entry, "enclosures") and entry.enclosures:
                        thumbnail_url = entry.enclosures[0].get("url", "")
                    video_url = entry.link
                    video_id = ""
                    if video_url:
                        parsed = urlparse(video_url)
                        if parsed.path:
                            video_id = parsed.path.split("/")[-1]
                    published = datetime.now()
                    if hasattr(entry, "published_parsed"):
                        published = datetime(*entry.published_parsed[:6])
                    items.append(FeedItem(
                        title=entry.get("title", "Nessun Titolo"),
                        url=video_url,
                        thumbnail_url=thumbnail_url,
                        duration=duration,
                        video_id=video_id,
                        published=published,
                        author=entry.get("author", "Sconosciuto"),
                        platform="PeerTube"
                    ))
                except Exception as e:
                    logger.error(f"Errore nel parsing dell'elemento del feed: {e}")
            return rss_feed, items
        except Exception as e:
            logger.error(f"Errore nel parsing del feed RSS: {e}")
            return None, []

    @staticmethod
    def add_feed(feed_url: str) -> bool:
        try:
            feed, items = RSSService.parse_peertube_feed(feed_url)
            if not feed:
                return False
            with RSSService._lock:  # Operazione thread-safe sul database
                with db_manager.get_connection() as conn:
                    cursor = conn.cursor()
                    cursor.execute("""
                        INSERT INTO rss_feeds (url, title, last_updated, is_channel)
                        VALUES (?, ?, ?, ?)
                    """, (feed.url, feed.title, feed.last_updated.isoformat(), int(feed.is_channel)))
                    feed_id = cursor.lastrowid
                    # Inserimento batch degli elementi del feed
                    cursor.executemany("""
                        INSERT OR IGNORE INTO feed_items
                        (feed_id, video_id, title, url, thumbnail_url, duration, published, author, platform)
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                    """, [
                        (
                            feed_id, item.video_id, item.title, item.url, item.thumbnail_url,
                            item.duration, item.published.isoformat(), item.author, item.platform
                        )
                        for item in items
                    ])
                    conn.commit()
            return True
        except Exception as e:
            logger.error(f"Errore nell'aggiunta del feed: {e}")
            return False

    @staticmethod
    def get_feeds() -> List[RSSFeed]:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id, url, title, last_updated, is_channel FROM rss_feeds")
            return [
                RSSFeed(
                    id=row[0],
                    url=row[1],
                    title=row[2],
                    last_updated=datetime.fromisoformat(row[3]) if row[3] else None,
                    is_channel=bool(row[4])
                )
                for row in cursor.fetchall()
            ]

    @staticmethod
    def get_feed_items(feed_id: int, limit: int = None, published_after: datetime = None) -> List[FeedItem]:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            query = """
                SELECT video_id, title, url, thumbnail_url, duration, published, author, platform
                FROM feed_items
                WHERE feed_id = ?
            """
            params = [feed_id]
            if published_after:
                query += " AND published >= ?"
                params.append(published_after.isoformat())
            query += " ORDER BY published DESC"
            if limit is not None:
                query += " LIMIT ?"
                params.append(limit)
            cursor.execute(query, params)
            return [
                FeedItem(
                    video_id=row[0],
                    title=row[1],
                    url=row[2],
                    thumbnail_url=row[3],
                    duration=row[4],
                    published=datetime.fromisoformat(row[5]),
                    author=row[6],
                    platform=row[7]
                )
                for row in cursor.fetchall()
            ]

    @staticmethod
    def remove_feed(feed_id: int) -> bool:
        try:
            with RSSService._lock:  # Operazione thread-safe sul database
                with db_manager.get_connection() as conn:
                    cursor = conn.cursor()
                    cursor.execute("DELETE FROM rss_feeds WHERE id = ?", (feed_id,))
                    conn.commit()
            return True
        except Exception as e:
            logger.error(f"Errore nella rimozione del feed: {e}")
            return False

    @staticmethod
    def refresh_feed(feed_id: int) -> bool:
        try:
            with db_manager.get_connection() as conn:
                cursor = conn.cursor()
                cursor.execute("SELECT url FROM rss_feeds WHERE id = ?", (feed_id,))
                row = cursor.fetchone()
                if not row:
                    return False
                feed_url = row[0]
            feed, items = RSSService.parse_peertube_feed(feed_url)
            if not feed:
                return False
            with RSSService._lock:  # Operazione thread-safe sul database
                with db_manager.get_connection() as conn:
                    cursor = conn.cursor()
                    cursor.execute("""
                        UPDATE rss_feeds
                        SET title = ?, last_updated = ?
                        WHERE id = ?
                    """, (feed.title, datetime.now().isoformat(), feed_id))
                    existing_ids = {row[0] for row in cursor.execute(
                        "SELECT video_id FROM feed_items WHERE feed_id = ?", (feed_id,)
                    ).fetchall()}
                    # Inserimento batch dei nuovi elementi
                    new_items = [
                        (
                            feed_id, item.video_id, item.title, item.url, item.thumbnail_url,
                            item.duration, item.published.isoformat(), item.author, item.platform
                        )
                        for item in items if item.video_id not in existing_ids
                    ]
                    if new_items:
                        cursor.executemany("""
                            INSERT INTO feed_items
                            (feed_id, video_id, title, url, thumbnail_url, duration, published, author, platform)
                            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                        """, new_items)
                    conn.commit()
            return True
        except Exception as e:
            logger.error(f"Errore nell'aggiornamento del feed {feed_id}: {e}")
            return False

    @staticmethod
    def refresh_all_feeds() -> bool:
        try:
            with db_manager.get_connection() as conn:
                cursor = conn.cursor()
                cursor.execute("SELECT id, url FROM rss_feeds")
                feeds = cursor.fetchall()

            def refresh_single_feed(feed_id, feed_url):
                try:
                    feed, items = RSSService.parse_peertube_feed(feed_url)
                    if not feed:
                        return False
                    with RSSService._lock:  # Operazione thread-safe sul database
                        with db_manager.get_connection() as conn:
                            cursor = conn.cursor()
                            cursor.execute("""
                                UPDATE rss_feeds
                                SET title = ?, last_updated = ?
                                WHERE id = ?
                            """, (feed.title, datetime.now().isoformat(), feed_id))
                            existing_ids = {row[0] for row in cursor.execute(
                                "SELECT video_id FROM feed_items WHERE feed_id = ?", (feed_id,)
                            ).fetchall()}
                            new_items = [
                                (
                                    feed_id, item.video_id, item.title, item.url, item.thumbnail_url,
                                    item.duration, item.published.isoformat(), item.author, item.platform
                                )
                                for item in items if item.video_id not in existing_ids
                            ]
                            if new_items:
                                cursor.executemany("""
                                    INSERT INTO feed_items
                                    (feed_id, video_id, title, url, thumbnail_url, duration, published, author, platform)
                                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                                """, new_items)
                            conn.commit()
                    return True
                except Exception as e:
                    logger.error(f"Errore nell'aggiornamento del feed {feed_id}: {e}")
                    return False

            with ThreadPoolExecutor(max_workers=4) as executor:
                futures = [executor.submit(refresh_single_feed, feed_id, feed_url) for feed_id, feed_url in feeds]
                results = [future.result() for future in as_completed(futures)]
            return all(results)
        except Exception as e:
            logger.error(f"Errore nell'aggiornamento dei feed: {e}")
            return False

    @staticmethod
    def get_all_feed_items(limit: int = None) -> List[FeedItem]:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            query = """
                SELECT fi.video_id, fi.title, fi.url, fi.thumbnail_url, fi.duration,
                       fi.published, fi.author, fi.platform, f.title as feed_title
                FROM feed_items fi
                JOIN rss_feeds f ON fi.feed_id = f.id
                ORDER BY fi.published DESC
            """
            if limit is not None:
                query += " LIMIT ?"
                cursor.execute(query, (limit,))
            else:
                cursor.execute(query)
            return [
                FeedItem(
                    video_id=row[0],
                    title=row[1],
                    url=row[2],
                    thumbnail_url=row[3],
                    duration=row[4],
                    published=datetime.fromisoformat(row[5]),
                    author=row[6],
                    platform=row[7]
                )
                for row in cursor.fetchall()
            ]

    @staticmethod
    def get_all_feed_items_with_timeout(limit: int = None, timeout: int = 30) -> List[FeedItem]:
        """
        Ottiene tutti gli elementi del feed con timeout per prevenire blocchi.
        Utilizza un approccio thread-safe senza il modulo signal.
        """
        def worker():
            try:
                return RSSService.get_all_feed_items(limit)
            except Exception as e:
                logger.error(f"Errore nel thread worker: {e}")
                return []

        # Crea un future per eseguire il worker
        with ThreadPoolExecutor(max_workers=1) as executor:
            future = executor.submit(worker)
            try:
                # Attende il risultato con timeout
                return future.result(timeout=timeout)
            except TimeoutError:
                logger.error("Timeout durante il recupero degli elementi del feed")
                # Annulla il future se è ancora in esecuzione
                future.cancel()
                return []
            except Exception as e:
                logger.error(f"Errore nel recupero degli elementi del feed: {e}")
                return []

    @staticmethod
    def clear_all_feeds() -> bool:
        try:
            with RSSService._lock:  # Operazione thread-safe sul database
                with db_manager.get_connection() as conn:
                    cursor = conn.cursor()
                    cursor.execute("DELETE FROM feed_items")
                    cursor.execute("DELETE FROM rss_feeds")
                    conn.commit()
            return True
        except Exception as e:
            logger.error(f"Errore nella cancellazione dei feed: {e}")
            return False

# --- Caricatore Miniature ---
class ThumbnailLoader(QRunnable):
    def __init__(self, url: str, label: QLabel):
        super().__init__()
        self.url = url
        self.label = label
        self._running = True

    def run(self):
        if not self._running:
            return
        try:
            if not api_rate_limiter.increment():
                return
            response = SearchService.SESSION.get(self.url, timeout=5, verify=True)
            response.raise_for_status()
            pixmap = QPixmap()
            pixmap.loadFromData(response.content)
            if self._running and not pixmap.isNull():
                scaled = pixmap.scaled(
                    self.label.size(),
                    Qt.KeepAspectRatio,
                    Qt.SmoothTransformation
                )
                self.label.setPixmap(scaled)
        except requests.RequestException as e:
            logger.warning(f"Errore nel caricamento della miniatura: {e}")

    def stop(self):
        self._running = False

# --- Worker Download ---
class WorkerSignals(QObject):
    progress = pyqtSignal(int)
    finished = pyqtSignal(str, str)
    error = pyqtSignal(str, str)

class StopDownload(Exception):
    pass

class DownloadWorker(QRunnable):
    def __init__(self, video_url: str, output_path: str):
        super().__init__()
        self.signals = WorkerSignals()
        self.video_url = video_url
        self.output_path = safe_join(Path(output_path), "")
        self._running = True

    def run(self):
        if not self._running:
            return
        ydl_opts = {
            'outtmpl': str(self.output_path / '%(title)s.%(ext)s'),
            'progress_hooks': [self._progress_hook],
            'quiet': True,
            'no_warnings': True,
            'force_color': False,
            'format': 'best',
        }
        try:
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                ydl.download([self.video_url])
                info = ydl.extract_info(self.video_url, download=False)
                filename = ydl.prepare_filename(info)
                self.signals.finished.emit(filename, "Download completato")
        except StopDownload:
            self.signals.error.emit(self.video_url, "Download annullato dall'utente")
        except Exception as e:
            self.signals.error.emit(self.video_url, f"Errore: {str(e)}")

    def _progress_hook(self, d):
        if not self._running:
            raise StopDownload()
        if d['status'] == 'downloading':
            percent_str = d.get('_percent_str', '0%')
            percent_str = re.sub(r'\x1b\[[0-9;]*m', '', percent_str)
            progress = int(float(percent_str.strip('%')))
            self.signals.progress.emit(progress)

    def stop(self):
        self._running = False

# --- Worker Contenuto Feed ---
class FeedContentSignals(QObject):
    finished = pyqtSignal(list)
    error = pyqtSignal(str)

class FeedContentWorker(QRunnable):
    def __init__(self):
        super().__init__()
        self.signals = FeedContentSignals()

    def run(self):
        try:
            # Simula un ritardo per mostrare il caricamento (rimuovere in produzione)
            time.sleep(0.5)
            items = RSSService.get_all_feed_items_with_timeout(limit=None)
            self.signals.finished.emit(items)
        except Exception as e:
            logger.error(f"Errore nel worker del contenuto del feed: {e}")
            self.signals.error.emit(str(e))

class FeedRefreshSignals(QObject):
    progress = pyqtSignal(int)
    finished = pyqtSignal()
    error = pyqtSignal(str)

class FeedRefreshWorker(QRunnable):
    def __init__(self):
        super().__init__()
        self.signals = FeedRefreshSignals()

    def run(self):
        try:
            self.signals.progress.emit(0)
            success = RSSService.refresh_all_feeds()
            self.signals.progress.emit(100)
            if success:
                self.signals.finished.emit()
            else:
                self.signals.error.emit("Impossibile aggiornare i feed")
        except Exception as e:
            logger.error(f"Errore nel worker di aggiornamento del feed: {e}")
            self.signals.error.emit(str(e))

# --- Tema ---
class Theme:
    PRIMARY = "#6a5acd"
    PRIMARY_HOVER = "#7a6bdb"
    PRIMARY_PRESSED = "#5a4bc9"
    SECONDARY = "#AAAAAA"
    SECONDARY_HOVER = "#CCCCCC"
    TERTIARY = "#FF9800"
    TERTIARY_HOVER = "#E68A00"
    BACKGROUND = "#0F0F0F"
    BACKGROUND_LIGHT = "#181818"
    SURFACE = "#212121"
    SURFACE_HOVER = "#2A2A2A"
    SURFACE_PRESSED = "#323232"
    TEXT_PRIMARY = "#FFFFFF"
    TEXT_SECONDARY = "#AAAAAA"
    TEXT_DISABLED = "#6B6B6B"
    TEXT_LINK = "#6a5acd"
    BORDER = "#3A3A3A"
    BORDER_ACTIVE = "#6a5acd"
    BORDER_HOVER = "#6B6B6B"
    SUCCESS = "#00FF66"
    DANGER = "#FF3333"
    WARNING = "#FFCC00"
    INFO = "#FF9800"

    @classmethod
    def global_stylesheet(cls) -> str:
        return f"""
        QMainWindow, QDialog {{
            background: {cls.BACKGROUND};
            font-family: 'Inter', 'Segoe UI', 'Roboto', sans-serif;
            font-size: 14px;
            color: {cls.TEXT_PRIMARY};
        }}
        QFrame[role="card"], QGroupBox {{
            background: {cls.SURFACE};
            border-radius: 8px;
            border: 1px solid {cls.BORDER};
            padding: 12px;
            margin: 6px;
            transition: all 0.2s ease;
        }}
        QFrame[role="card"]:hover {{
            border: 1px solid {cls.PRIMARY};
            box-shadow: 0 4px 8px rgba(106, 90, 205, 0.2);
        }}
        QToolBar {{
            background: {cls.BACKGROUND_LIGHT};
            border: none;
            padding: 8px;
            spacing: 6px;
            border-radius: 8px;
            margin: 6px;
        }}
        QToolBar QToolButton {{
            background: transparent;
            border: none;
            padding: 6px;
            border-radius: 6px;
            icon-size: 18px;
        }}
        QToolBar QToolButton:hover {{
            background: rgba(106, 90, 205, 0.1);
        }}
        QToolBar QToolButton:checked {{
            background: rgba(106, 90, 205, 0.2);
        }}
        QStatusBar {{
            background: {cls.SURFACE};
            color: {cls.TEXT_SECONDARY};
            padding: 6px 12px;
            border-top: 1px solid {cls.BORDER};
            font-size: 14px;
        }}
        QLineEdit, QTextEdit, QPlainTextEdit, QComboBox {{
            background: {cls.SURFACE};
            border: 1px solid {cls.BORDER};
            border-radius: 6px;
            color: {cls.TEXT_PRIMARY};
            padding: 8px 12px;
            selection-background-color: {cls.PRIMARY};
        }}
        QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus, QComboBox:focus {{
            border: 1px solid {cls.PRIMARY};
        }}
        QLineEdit[role="search"] {{
            border-radius: 16px;
            padding-left: 32px;
        }}
        QPushButton {{
            background: {cls.PRIMARY};
            color: white;
            border: none;
            border-radius: 6px;
            padding: 8px 16px;
            font-family: 'Inter Medium', 'Segoe UI Semibold', sans-serif;
            font-weight: 500;
        }}
        QPushButton:hover {{
            background: {cls.PRIMARY_HOVER};
        }}
        QPushButton:pressed {{
            background: {cls.PRIMARY_PRESSED};
        }}
        QPushButton:disabled {{
            background: {cls.BORDER};
            color: {cls.TEXT_DISABLED};
        }}
        QPushButton[role="secondary"] {{
            background: {cls.SECONDARY};
        }}
        QPushButton[role="secondary"]:hover {{
            background: {cls.SECONDARY_HOVER};
        }}
        QPushButton[role="tertiary"] {{
            background: {cls.TERTIARY};
        }}
        QPushButton[role="danger"] {{
            background: {cls.DANGER};
        }}
        QPushButton[role="danger"]:hover {{
            background: #d32f2f;
        }}
        QScrollBar:vertical {{
            border: none;
            background: {cls.BACKGROUND_LIGHT};
            width: 8px;
            margin: 0px;
            border-radius: 4px;
        }}
        QScrollBar::handle:vertical {{
            background: {cls.PRIMARY};
            min-height: 16px;
            border-radius: 4px;
        }}
        QScrollBar::handle:vertical:hover {{
            background: {cls.PRIMARY_HOVER};
        }}
        QScrollBar::add-line, QScrollBar::sub-line {{
            background: none;
            border: none;
        }}
        QCheckBox::indicator, QRadioButton::indicator {{
            width: 16px;
            height: 16px;
        }}
        QCheckBox::indicator:checked {{
            background: {cls.PRIMARY};
            border-radius: 4px;
            border: none;
        }}
        QRadioButton::indicator:checked {{
            background: {cls.PRIMARY};
            border-radius: 8px;
            border: none;
        }}
        QProgressBar {{
            border: none;
            border-radius: 4px;
            text-align: center;
            background: {cls.SURFACE};
        }}
        QProgressBar::chunk {{
            background: {cls.PRIMARY};
            border-radius: 4px;
        }}
        QMenu {{
            background: {cls.SURFACE};
            border: 1px solid {cls.BORDER};
            border-radius: 6px;
            padding: 4px;
            color: {cls.TEXT_PRIMARY};
        }}
        QMenu::item:selected {{
            background: rgba(106, 90, 205, 0.2);
            border-radius: 4px;
        }}
        QListWidget {{
            background: {cls.SURFACE};
            border: 1px solid {cls.BORDER};
            border-radius: 6px;
            color: {cls.TEXT_PRIMARY};
            font-family: 'Inter';
            font-size: 14px;
            padding: 6px;
        }}
        QListWidget::item {{
            padding: 8px;
            border-radius: 4px;
        }}
        QListWidget::item:selected {{
            background: {cls.PRIMARY};
            color: white;
        }}
        QListWidget::item:hover {{
            background: {cls.SURFACE_HOVER};
        }}
        QScrollArea {{
            background: transparent;
            border: none;
        }}
        QGridLayout {{
            spacing: 16px;
        }}
        """

    @classmethod
    def search_bar_style(cls) -> str:
        return f"""
        QLineEdit[role="search"] {{
            background: {cls.SURFACE};
            border: 1px solid {cls.BORDER};
            border-radius: 16px;
            color: {cls.TEXT_PRIMARY};
            padding: 0 32px 0 16px;
            min-height: 32px;
        }}
        QLineEdit[role="search"]:focus {{
            border: 1px solid {cls.PRIMARY};
        }}
        """

    @classmethod
    def video_card_style(cls) -> str:
        return f"""
        VideoCard {{
            background: {cls.SURFACE};
            border-radius: 8px;
            border: 1px solid {cls.BORDER};
            color: {cls.TEXT_PRIMARY};
            transition: all 0.2s ease;
        }}
        VideoCard:hover {{
            border: 1px solid {cls.PRIMARY};
            box-shadow: 0 4px 8px rgba(106, 90, 205, 0.3);
            transform: translateY(-2px);
        }}
        #title_label {{
            font-weight: 600;
            font-size: 14px;
            color: {cls.TEXT_PRIMARY};
            margin-top: 8px;
        }}
        #duration_label {{
            font-size: 12px;
            color: white;
            background-color: rgba(0, 0, 0, 0.8);
            border: none;
            border-radius: 4px;
            padding: 2px 6px;
            font-weight: bold;
            min-width: 40px;
            text-align: center;
            position: absolute;
            bottom: 8px;
            right: 8px;
        }}
        #thumb_label {{
            border-radius: 6px;
            border: none;
            background: transparent;
        }}
        #author_label, #date_label {{
            font-size: 12px;
            color: {cls.TEXT_SECONDARY};
        }}
        QCheckBox::indicator {{
            width: 14px;
            height: 14px;
            border: 1px solid {cls.BORDER};
            border-radius: 3px;
        }}
        QCheckBox::indicator:checked {{
            background: {cls.PRIMARY};
            border: 1px solid {cls.PRIMARY};
        }}
        """

    @classmethod
    def sidebar_style(cls) -> str:
        return f"""
        #sidebar {{
            background: {cls.BACKGROUND_LIGHT};
            border-right: 1px solid {cls.BORDER};
            padding: 12px 0;
        }}
        #sidebar QPushButton {{
            background: transparent;
            color: {cls.TEXT_SECONDARY};
            border: none;
            text-align: left;
            padding: 8px 16px;
            font-size: 14px;
            border-radius: 4px;
            margin: 2px 6px;
        }}
        #sidebar QPushButton:hover {{
            background: {cls.SURFACE_HOVER};
            color: {cls.TEXT_PRIMARY};
        }}
        #sidebar QPushButton:checked {{
            background: {cls.PRIMARY};
            color: white;
        }}
        #sidebar QPushButton[active="true"] {{
            background: {cls.PRIMARY};
            color: white;
        }}
        """

# --- Scheda Video ---
class VideoCard(QFrame):
    watch_later_removed = pyqtSignal()
    video_removed = pyqtSignal(str)

    def __init__(self, video: Video, playlist_id: int = None, main_window=None, show_selector: bool = False):
        super().__init__()
        self.video = video
        self.playlist_id = playlist_id
        self.main_window = main_window
        self.show_selector = show_selector
        self.thumbnail_loader = None
        self.thread_pool = QThreadPool.globalInstance()
        self.setFixedSize(360, 280)
        self.setStyleSheet(Theme.video_card_style())
        self._setup_ui()
        self.loading_spinner = None
        self.mpv_process = None
        self.setFocusPolicy(Qt.StrongFocus)

    def _setup_ui(self):
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(8)
        thumb_container = QFrame()
        thumb_container.setFixedSize(360, 200)
        thumb_container.setStyleSheet("""
            QFrame {
                background: #181818;
                border-radius: 8px;
                border: none;
                overflow: hidden;
            }
        """)
        thumb_layout = QVBoxLayout(thumb_container)
        thumb_layout.setContentsMargins(0, 0, 0, 0)
        self.thumb_label = QLabel()
        self.thumb_label.setObjectName("thumb_label")
        self.thumb_label.setFixedSize(360, 200)
        self.thumb_label.setAlignment(Qt.AlignCenter)
        self.thumb_label.setStyleSheet("""
            QLabel {
                background: #000;
                border: none;
                border-radius: 0px;
            }
        """)
        self.thumb_label.setScaledContents(True)
        thumb_layout.addWidget(self.thumb_label)
        self.duration_label = QLabel(self.thumb_label)
        self.duration_label.setObjectName("duration_label")
        self.duration_label.setText(f"⏱ {self._format_duration(self.video.duration)}")
        self.duration_label.setStyleSheet("""
            QLabel#duration_label {
                background-color: rgba(0, 0, 0, 0.8);
                color: white;
                border: none;
                border-radius: 4px;
                padding: 4px 8px;
                font-size: 12px;
                font-weight: bold;
                min-width: 50px;
                text-align: center;
            }
        """)
        self.duration_label.move(self.thumb_label.width() - 80, self.thumb_label.height() - 30)
        layout.addWidget(thumb_container)
        title_text = self.video.title[:50] + "..." if len(self.video.title) > 50 else self.video.title
        self.title_label = QLabel(title_text)
        self.title_label.setObjectName("title_label")
        self.title_label.setWordWrap(True)
        self.title_label.setToolTip(self.video.title)
        layout.addWidget(self.title_label)
        if hasattr(self.video, 'author') and self.video.author:
            self.author_label = QLabel(f"👤 {self.video.author}")
            self.author_label.setObjectName("author_label")
            layout.addWidget(self.author_label)
        if hasattr(self.video, 'published') and self.video.published:
            pub_date = self.video.published.strftime("%d/%m/%Y")
            self.date_label = QLabel(f"📅 {pub_date}")
            self.date_label.setObjectName("date_label")
            layout.addWidget(self.date_label)
        if self.show_selector:
            self.checkbox = QCheckBox()
            layout.addWidget(self.checkbox)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_menu)
        if hasattr(self.video, 'thumbnail_url') and self.video.thumbnail_url:
            self.thumbnail_loader = ThumbnailLoader(self.video.thumbnail_url, self.thumb_label)
            self.thread_pool.start(self.thumbnail_loader)

    def _center_spinner(self):
        if self.loading_spinner:
            spinner_x = (self.thumb_label.width() - self.loading_spinner.width()) // 2
            spinner_y = (self.thumb_label.height() - self.loading_spinner.height()) // 2
            self.loading_spinner.move(spinner_x, spinner_y)

    def _format_duration(self, seconds: str) -> str:
        try:
            sec = int(seconds)
            h, m, s = sec // 3600, (sec % 3600) // 60, sec % 60
            if h:
                return f"{h}h {m}m"
            elif m:
                return f"{m}m {s}s"
            return f"{s}s"
        except ValueError:
            return "0s"

    def _safe_hide_spinner(self):
        if self.loading_spinner:
            self.loading_spinner.hide()
            self.loading_spinner.deleteLater()
            self.loading_spinner = None
        self.setEnabled(True)
        self.duration_label.raise_()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton and (not hasattr(self, 'checkbox') or not self.checkbox.underMouse()):
            if isinstance(self, MPVVideoCard) or self._is_mpv_default():
                self.play_with_mpv()
            else:
                webbrowser.open(self.video.url)

    def _is_mpv_default(self) -> bool:
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT value FROM user_preferences WHERE key = 'player'")
            row = cursor.fetchone()
            return row and row[0] == "mpv"

    def play_with_mpv(self):
        self.loading_spinner = LoadingSpinner(self.thumb_label)
        self.loading_spinner.setFixedSize(60, 60)
        self._center_spinner()
        self.loading_spinner.show()
        self.loading_spinner.raise_()
        self.duration_label.raise_()
        self.setEnabled(False)
        QTimer.singleShot(100, self._start_mpv_process)

    def _start_mpv_process(self):
        try:
            direct_url = self.get_direct_url(self.video.url)
            logger.info(f"Riproduzione con MPV: {direct_url}")
            from threading import Thread
            thread = Thread(target=self._run_mpv_with_monitoring, args=(direct_url,))
            thread.daemon = True
            thread.start()
        except Exception as e:
            logger.error(f"Errore nell'avvio di MPV: {e}")
            self._hide_loading_spinner()
            QMessageBox.warning(self, "Errore", f"Impossibile avviare MPV: {str(e)}")

    def _run_mpv_with_monitoring(self, direct_url: str):
        try:
            with db_manager.get_connection() as conn:
                cursor = conn.cursor()
                cursor.execute("SELECT value FROM user_preferences WHERE key = 'mpv_buffer'")
                row = cursor.fetchone()
                use_buffer = row[0] == "True" if row else False
            cmd = [
                "mpv",
                "--really-quiet",
                "--no-terminal",
            ]
            if use_buffer:
                cmd.append("--demuxer-lavf-buffersize=327668")
            cmd.append(direct_url)
            logger.info(f"Esecuzione comando MPV: {' '.join(cmd)}")
            self.mpv_process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True,
                shell=False,
                bufsize=1,
                start_new_session=True
            )
            start_time = datetime.now()
            video_started = False
            while self.mpv_process.poll() is None:
                elapsed_time = (datetime.now() - start_time).total_seconds()
                if elapsed_time > 30:
                    logger.warning(f"Timeout processo MPV dopo {elapsed_time} secondi")
                    self._terminate_mpv_process()
                    break
                try:
                    stdout_line = self.mpv_process.stdout.readline()
                    stderr_line = self.mpv_process.stderr.readline()
                    if stdout_line.strip():
                        logger.debug(f"MPV stdout: {stdout_line.strip()}")
                    if stderr_line.strip():
                        logger.debug(f"MPV stderr: {stderr_line.strip()}")
                    output_lines = [stdout_line, stderr_line]
                    for line in output_lines:
                        if line and any(keyword in line.lower() for keyword in [
                            'playing:', 'start', 'avsync', 'video:', 'audio:'
                        ]):
                            video_started = True
                            logger.info("Riproduzione video MPV avviata con successo")
                            break
                    if video_started:
                        break
                except Exception as e:
                    logger.debug(f"Errore nella lettura dell'output di MPV: {e}")
                time.sleep(0.1)
            exit_code = self.mpv_process.poll()
            self._hide_loading_spinner()
            if exit_code is not None and exit_code != 0:
                error_output = ""
                try:
                    remaining_stderr = self.mpv_process.stderr.read()
                    if remaining_stderr:
                        error_output = remaining_stderr
                except:
                    pass
                if not error_output:
                    error_output = f"Processo terminato con codice {exit_code}"
                logger.error(f"MPV terminato con errore: {error_output}")
                QTimer.singleShot(0, lambda: self._show_mpv_error(error_output))
            elif not video_started and exit_code is None:
                logger.warning("Timeout processo MPV senza avvio video")
                QTimer.singleShot(0, lambda: self._show_mpv_error("Il video non è riuscito ad avviarsi entro il timeout"))
        except Exception as e:
            logger.error(f"Errore nell'esecuzione di MPV: {e}")
            self._hide_loading_spinner()
            QTimer.singleShot(0, lambda: self._show_mpv_error(str(e)))

    def _terminate_mpv_process(self):
        if self.mpv_process and self.mpv_process.poll() is None:
            try:
                logger.info("Terminazione processo MPV...")
                self.mpv_process.terminate()
                try:
                    self.mpv_process.wait(timeout=5)
                    logger.info("Processo MPV terminato con successo")
                except subprocess.TimeoutExpired:
                    logger.warning("Il processo MPV non si è terminato, forzando l'uccisione...")
                    self.mpv_process.kill()
                    self.mpv_process.wait()
                    logger.info("Processo MPV ucciso")
            except Exception as e:
                logger.error(f"Errore nella terminazione del processo MPV: {e}")

    def _hide_loading_spinner(self):
        QTimer.singleShot(0, self._safe_hide_spinner)

    def _show_mpv_error(self, error_message: str):
        QMessageBox.warning(
            self,
            "Errore MPV",
            f"Impossibile riprodurre il video con MPV:\n\n{error_message}\n\n"
            "Assicurati che:\n"
            "• MPV sia installato correttamente\n"
            "• L'URL del video sia accessibile\n"
            "• Tu abbia i codec necessari\n"
            "• La tua connessione di rete sia stabile"
        )

    def get_direct_url(self, video_url: str) -> str:
        ydl_opts = {
            'quiet': True,
            'no_warnings': True,
            'force_generic_extractor': True,
            'format': 'best[height<=1080]',
            'get_url': True,
        }
        try:
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                result = ydl.extract_info(video_url, download=False)
                if 'url' in result:
                    logger.info(f"URL diretto estratto con successo per: {video_url}")
                    return result['url']
                else:
                    logger.warning(f"Nessun URL diretto trovato per: {video_url}, utilizzo dell'URL originale")
                    return video_url
        except Exception as e:
            logger.error(f"Errore nell'ottenimento dell'URL diretto per {video_url}: {e}")
            return video_url

    def play_with_default_player(self):
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT value FROM user_preferences WHERE key = 'player'")
            row = cursor.fetchone()
            player = row[0] if row else "Default Browser"
        if player == "mpv":
            self.play_with_mpv()
        else:
            webbrowser.open(self.video.url)

    def show_menu(self, pos):
        menu = QMenu(self)
        menu.setStyleSheet(f"""
            QMenu {{
                background: {Theme.SURFACE};
                border: 1px solid {Theme.BORDER};
                border-radius: 6px;
                color: {Theme.TEXT_PRIMARY};
            }}
            QMenu::item:selected {{
                background: rgba(106, 90, 205, 0.2);
                border-radius: 4px;
            }}
        """)
        menu.addAction(QIcon.fromTheme("media-playback-start"), "Apri con browser",
                      lambda: webbrowser.open(self.video.url))
        menu.addAction(QIcon.fromTheme("media-playback-start"), "Apri con MPV", self.play_with_mpv)
        menu.addAction(QIcon.fromTheme("edit-copy"), "Copia link",
                      lambda: QApplication.clipboard().setText(self.video.url))
        menu.addAction(QIcon.fromTheme("document-save"), "Scarica video", self.download_video)
        if hasattr(self.video, 'author') and self.video.author and self.video.platform == "PeerTube":
            add_feed_action = menu.addAction(QIcon.fromTheme("application-rss+xml"), "Aggiungi feed RSS del canale", self.add_channel_feed)
        playlist_menu = menu.addMenu(QIcon.fromTheme("view-media-playlist"), "Aggiungi a playlist")
        for pl in PlaylistService.get_playlists():
            if pl.name != "Guarda più tardi":
                action = playlist_menu.addAction(pl.name)
                action.setIcon(QIcon.fromTheme("folder"))
                action.triggered.connect(lambda _, p=pl.id: self.add_to_playlist(p))
        # Aggiungi opzione "Crea nuova playlist"
        create_playlist_action = playlist_menu.addAction(QIcon.fromTheme("list-add"), "Crea nuova playlist")
        create_playlist_action.triggered.connect(self.create_new_playlist)
        if self.playlist_id is None:
            menu.addAction(QIcon.fromTheme("bookmark-new"), "Salva in Guarda più tardi", self.toggle_watch_later)
        else:
            menu.addAction(QIcon.fromTheme("list-remove"), "Rimuovi dalla playlist", self.remove_from_current_playlist)
        menu.exec_(self.mapToGlobal(pos))

    def create_new_playlist(self):
        """Crea una nuova playlist e aggiunge il video corrente."""
        name, ok = QInputDialog.getText(self, "Nuova Playlist", "Inserisci il nome della playlist:")
        if ok and name:
            try:
                # Crea la nuova playlist
                pl = Playlist(name=name)
                playlist_id = PlaylistService.save_playlist(pl)
                # Aggiunge il video corrente alla nuova playlist
                if PlaylistService.add_to_playlist(playlist_id, self.video):
                    self.main_window.statusBar().showMessage(f"✓ Video aggiunto alla nuova playlist '{name}'")
                else:
                    QMessageBox.warning(self, "Errore", "Impossibile aggiungere il video alla nuova playlist")
            except Exception as e:
                logger.error(f"Errore nella creazione della nuova playlist: {e}")
                QMessageBox.warning(self, "Errore", f"Impossibile creare la playlist: {str(e)}")

    def add_channel_feed(self):
        if not hasattr(self.video, 'url') or not self.video.url:
            QMessageBox.warning(self, "Errore", "URL del video non disponibile.")
            return
        try:
            channel_id = extract_channel_id(self.video.url)
            if not channel_id:
                QMessageBox.warning(self, "Errore", "Impossibile estrarre l'ID del canale dal video.")
                return
            feed_url = generate_rss_url(self.video.url, channel_id)
            existing_feeds = RSSService.get_feeds()
            if any(feed.url == feed_url for feed in existing_feeds):
                QMessageBox.warning(self, "Attenzione", "Questo feed RSS è già stato aggiunto.")
                return
            if RSSService.add_feed(feed_url):
                QMessageBox.information(
                    self,
                    "Successo",
                    f"Feed RSS del canale '{self.video.author}' aggiunto con successo!"
                )
                self.main_window.statusBar().showMessage(
                    f"✓ Feed RSS del canale '{self.video.author}' aggiunto."
                )
            else:
                QMessageBox.warning(self, "Errore", "Impossibile aggiungere il feed RSS del canale.")
        except Exception as e:
            logger.error(f"Errore nell'aggiunta del feed RSS: {e}")
            QMessageBox.warning(
                self,
                "Errore",
                f"Errore nell'aggiunta del feed RSS: {str(e)}"
            )

    def add_to_playlist(self, playlist_id: int):
        if PlaylistService.add_to_playlist(playlist_id, self.video):
            playlist_name = next(p.name for p in PlaylistService.get_playlists() if p.id == playlist_id)
            self.main_window.statusBar().showMessage(f"✓ Video aggiunto a {playlist_name}")
        else:
            QMessageBox.warning(self, "Attenzione", "Video già presente in questa playlist")

    def toggle_watch_later(self):
        wl_playlist = PlaylistService.get_watch_later_playlist()
        if not wl_playlist:
            return
        existing_ids = {v.video_id for v in wl_playlist.videos}
        if self.video.video_id not in existing_ids:
            if PlaylistService.add_to_playlist(wl_playlist.id, self.video):
                QMessageBox.information(self, "Successo", "Video aggiunto a Guarda più tardi")
            else:
                QMessageBox.warning(self, "Errore", "Impossibile aggiungere il video")
        else:
            if QMessageBox.question(
                self, "Attenzione", "Video già in Guarda più tardi. Rimuovere?",
                QMessageBox.Yes | QMessageBox.No
            ) == QMessageBox.Yes:
                if PlaylistService.remove_from_playlist(wl_playlist.id, self.video.video_id):
                    self.watch_later_removed.emit()

    def remove_from_current_playlist(self):
        if self.playlist_id is not None and PlaylistService.remove_from_playlist(self.playlist_id, self.video.video_id):
            self.video_removed.emit(self.video.video_id)

    def download_video(self):
        download_dir = QFileDialog.getExistingDirectory(
            self,
            "Seleziona la cartella di destinazione",
            str(Path.home())
        )
        if not download_dir:
            return
        progress_dialog = QProgressDialog(
            f"Scaricamento: {self.video.title}",
            "Annulla",
            0,
            100,
            self
        )
        progress_dialog.setWindowTitle("Download in corso")
        progress_dialog.setWindowModality(Qt.WindowModal)
        progress_dialog.setAutoClose(False)
        progress_dialog.setValue(0)

        def update_progress(progress: int):
            progress_dialog.setValue(progress)

        def on_completed(filename: str, message: str):
            if not progress_dialog.wasCanceled():
                progress_dialog.setValue(100)
                QMessageBox.information(self, "Successo", f"Video salvato: {filename}")
            progress_dialog.close()

        def on_failed(url: str, error: str):
            if not progress_dialog.wasCanceled():
                QMessageBox.warning(self, "Errore", f"Download fallito: {error}")
            progress_dialog.close()

        worker = DownloadWorker(self.video.url, download_dir)
        worker.signals.progress.connect(update_progress)
        worker.signals.finished.connect(on_completed)
        worker.signals.error.connect(on_failed)
        progress_dialog.canceled.connect(worker.stop)
        self.thread_pool.start(worker)
        progress_dialog.exec_()

    def keyPressEvent(self, event):
        """Gestisce gli eventi della tastiera per la scheda video."""
        if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return:
            self.play_with_default_player()
        elif event.key() == Qt.Key_Space:
            self.play_with_default_player()
        elif event.key() == Qt.Key_C and event.modifiers() == Qt.ControlModifier:
            QApplication.clipboard().setText(self.video.url)
            self.main_window.statusBar().showMessage("📋 URL del video copiato negli appunti!")
        elif event.key() == Qt.Key_D and event.modifiers() == Qt.ControlModifier:
            self.download_video()
        elif event.key() == Qt.Key_W and self.playlist_id is None:
            self.toggle_watch_later()
        elif event.key() == Qt.Key_Delete and self.playlist_id is not None:
            self.remove_from_current_playlist()
        else:
            super().keyPressEvent(event)

class MPVVideoCard(VideoCard):
    def __init__(self, video: Video, playlist_id: int = None, main_window=None, show_selector: bool = False):
        super().__init__(video, playlist_id, main_window, show_selector=show_selector)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton and (not hasattr(self, 'checkbox') or not self.checkbox.underMouse()):
            self.play_with_mpv()

# --- Scheda Contenuto Feed ---
class FeedContentTab(QWidget):
    def __init__(self, main_window):
        super().__init__()
        self.main_window = main_window
        self.thread_pool = QThreadPool.globalInstance()
        self.is_loading = False
        self.current_page = 1
        self.items_per_page = 20
        self.all_items = []
        self.filtered_items = []
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setup_filters()
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.scroll_area.setStyleSheet(f"background: {Theme.BACKGROUND}; border: none;")
        self.scroll_area.verticalScrollBar().valueChanged.connect(self.on_scroll)
        self.scroll_content = QWidget()
        self.scroll_content.setStyleSheet(f"background: {Theme.BACKGROUND};")
        self.scroll_layout = QGridLayout(self.scroll_content)
        self.scroll_layout.setSpacing(16)
        self.scroll_layout.setContentsMargins(16, 16, 16, 16)
        self.scroll_area.setWidget(self.scroll_content)
        layout.addWidget(self.scroll_area)
        btn_widget = QWidget()
        btn_layout = QHBoxLayout(btn_widget)
        btn_layout.setContentsMargins(0, 0, 0, 0)
        refresh_btn = QPushButton("Aggiorna tutti i feed")
        refresh_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.PRIMARY};
                color: white;
                border: none;
                border-radius: 4px;
                padding: 8px 16px;
                margin: 8px;
            }}
            QPushButton:hover {{ background: {Theme.PRIMARY_HOVER}; }}
        """)
        clear_btn = QPushButton("Cancella tutti i feed")
        clear_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.DANGER};
                color: white;
                border: none;
                border-radius: 4px;
                padding: 8px 16px;
                margin: 8px;
            }}
            QPushButton:hover {{ background: #ff2222; }}
        """)
        btn_layout.addWidget(refresh_btn)
        btn_layout.addWidget(clear_btn)
        layout.addWidget(btn_widget)
        refresh_btn.clicked.connect(self.refresh_all_content_with_progress)
        clear_btn.clicked.connect(self.clear_all_feeds)
        self.main_window.sidebar.feed_content_clicked.connect(self.load_feed_content)
        self.auto_refresh_timer = QTimer(self)
        self.auto_refresh_timer.timeout.connect(self.refresh_all_content_with_progress)
        self.auto_refresh_timer.start(3600000)
        self.setFocusPolicy(Qt.StrongFocus)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.update_displayed_videos()

    def on_scroll(self, value):
        if self.is_loading:
            return
        scroll_bar = self.scroll_area.verticalScrollBar()
        if scroll_bar.value() >= scroll_bar.maximum() - 100:
            self.load_more_videos()

    def load_more_videos(self):
        if self.is_loading:
            return
        self.is_loading = True
        self.current_page += 1
        start_idx = (self.current_page - 1) * self.items_per_page
        end_idx = start_idx + self.items_per_page
        if start_idx >= len(self.filtered_items):
            self.is_loading = False
            return
        loading_label = QLabel("Caricamento di più video...")
        loading_label.setAlignment(Qt.AlignCenter)
        loading_label.setStyleSheet(f"color: {Theme.TEXT_SECONDARY}; font-size: 14px;")
        row = self.scroll_layout.rowCount()
        self.scroll_layout.addWidget(loading_label, row, 0, 1, -1)
        QTimer.singleShot(500, lambda: self.update_displayed_videos(start_idx, end_idx))

    def update_displayed_videos(self, start_idx=None, end_idx=None):
        if start_idx is None:
            start_idx = 0
        if end_idx is None:
            end_idx = start_idx + self.items_per_page
        for i in reversed(range(self.scroll_layout.count())):
            widget = self.scroll_layout.itemAt(i).widget()
            if isinstance(widget, QLabel) and widget.text() == "Caricamento di più video...":
                widget.deleteLater()
        columns = max(1, (self.scroll_area.width() - 32) // 380)
        page_items = self.filtered_items[start_idx:end_idx]
        for idx, item in enumerate(page_items):
            video = Video(
                title=item.title,
                url=item.url,
                thumbnail_url=item.thumbnail_url,
                duration=item.duration,
                video_id=item.video_id,
                platform=item.platform,
                author=item.author,
                published=item.published
            )
            card = VideoCard(video, main_window=self.main_window, show_selector=False)
            row = (idx + start_idx) // columns
            col = (idx + start_idx) % columns
            self.scroll_layout.addWidget(card, row, col)
        self.is_loading = False

    def on_feed_content_loaded(self, items):
        self.is_loading = False
        self.all_items = items
        self.current_page = 1
        self.filtered_items = self.apply_filters_to_items(items)
        self.update_channel_filter(items)
        self.update_displayed_videos(0, self.items_per_page)
        self.main_window.statusBar().showMessage(f"✅ Caricati {len(items)} video dai feed")

    def apply_filters(self):
        if not hasattr(self, 'all_items'):
            return
        self.filtered_items = self.apply_filters_to_items(self.all_items)
        self.current_page = 1
        for i in reversed(range(self.scroll_layout.count())):
            self.scroll_layout.itemAt(i).widget().deleteLater()
        self.update_displayed_videos(0, self.items_per_page)

    def apply_filters_to_items(self, items):
        filtered_items = items.copy()
        selected_channel = self.channel_filter.currentText()
        if selected_channel != "Tutti i Canali":
            filtered_items = [item for item in filtered_items
                            if hasattr(item, 'author') and item.author == selected_channel]
        date_filter = self.date_filter.currentText()
        now = datetime.now()
        if date_filter == "Ultime 24 ore":
            cutoff = now - timedelta(hours=24)
            filtered_items = [item for item in filtered_items if item.published >= cutoff]
        elif date_filter == "Ultima settimana":
            cutoff = now - timedelta(days=7)
            filtered_items = [item for item in filtered_items if item.published >= cutoff]
        elif date_filter == "Ultimo mese":
            cutoff = now - timedelta(days=30)
            filtered_items = [item for item in filtered_items if item.published >= cutoff]
        return filtered_items

    def update_channel_filter(self, items):
        self.channel_filter.clear()
        self.channel_filter.addItem("Tutti i Canali")
        channels = set()
        for item in items:
            if hasattr(item, 'author') and item.author:
                channels.add(item.author)
        for channel in sorted(channels):
            self.channel_filter.addItem(channel)

    def clear_all_feeds(self):
        if QMessageBox.question(
            self, "Conferma", "Rimuovere TUTTI i feed? Questa azione non può essere annullata.",
            QMessageBox.Yes | QMessageBox.No
        ) == QMessageBox.Yes:
            if RSSService.clear_all_feeds():
                for i in reversed(range(self.scroll_layout.count())):
                    self.scroll_layout.itemAt(i).widget().deleteLater()
                self.main_window.statusBar().showMessage("Tutti i feed sono stati rimossi")
                QMessageBox.information(self, "Successo", "Tutti i feed sono stati rimossi")
            else:
                QMessageBox.warning(self, "Errore", "Impossibile rimuovere i feed")

    def refresh_all_content_with_progress(self):
        try:
            progress_dialog = QProgressDialog("Aggiornamento feed...", "Annulla", 0, 100, self)
            progress_dialog.setWindowTitle("Aggiornamento Feed")
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setAutoClose(True)
            progress_dialog.show()
            worker = FeedRefreshWorker()
            worker.signals.progress.connect(progress_dialog.setValue)
            worker.signals.finished.connect(lambda: self.on_refresh_complete(progress_dialog))
            worker.signals.error.connect(lambda msg: self.on_refresh_error(progress_dialog, msg))
            self.thread_pool.start(worker)
        except Exception as e:
            QMessageBox.warning(self, "Errore", f"Errore durante l'aggiornamento: {str(e)}")

    def on_refresh_complete(self, progress_dialog):
        progress_dialog.close()
        self.load_feed_content()
        QMessageBox.information(self, "Successo", "Tutti i feed sono stati aggiornati!")

    def on_refresh_error(self, progress_dialog, error_msg):
        progress_dialog.close()
        QMessageBox.warning(self, "Errore", f"Impossibile aggiornare i feed: {error_msg}")

    def setup_filters(self):
        filter_widget = QWidget()
        filter_layout = QHBoxLayout(filter_widget)
        self.channel_filter = QComboBox()
        self.channel_filter.addItem("Tutti i Canali")
        self.channel_filter.currentTextChanged.connect(self.apply_filters)
        self.date_filter = QComboBox()
        self.date_filter.addItems(["Tutte le Date", "Ultime 24 ore", "Ultima settimana", "Ultimo mese"])
        self.date_filter.currentTextChanged.connect(self.apply_filters)
        filter_layout.addWidget(QLabel("Canale:"))
        filter_layout.addWidget(self.channel_filter)
        filter_layout.addWidget(QLabel("Data:"))
        filter_layout.addWidget(self.date_filter)
        filter_layout.addStretch()
        self.layout().insertWidget(0, filter_widget)

    def load_feed_content(self):
        if self.is_loading:
            return
        self.is_loading = True
        self.main_window.statusBar().showMessage("🔄 Caricamento contenuto feed...")
        worker = FeedContentWorker()
        worker.signals.finished.connect(self.on_feed_content_loaded)
        worker.signals.error.connect(self.on_feed_content_error)
        self.thread_pool.start(worker)

    def on_feed_content_error(self, error_msg):
        self.is_loading = False
        self.main_window.statusBar().showMessage(f"❌ Errore nel caricamento dei feed: {error_msg}")
        QMessageBox.warning(self, "Errore", f"Impossibile caricare il contenuto dei feed: {error_msg}")

    def keyPressEvent(self, event):
        """Gestisce gli eventi della tastiera per la scheda contenuto feed."""
        if event.key() == Qt.Key_Delete:
            self.clear_all_feeds()
        elif event.key() == Qt.Key_R and event.modifiers() == Qt.ControlModifier:
            self.refresh_all_content_with_progress()
        else:
            super().keyPressEvent(event)

# --- Griglia Video ---
class VideoGrid(QScrollArea):
    def __init__(self, main_window=None):
        super().__init__()
        self.main_window = main_window
        self.thread_pool = QThreadPool.globalInstance()
        self.setWidgetResizable(True)
        self.setStyleSheet(f"VideoGrid {{ background: {Theme.BACKGROUND}; border: none; }}")
        self.content = QWidget()
        self.content.setStyleSheet(f"background: {Theme.BACKGROUND};")
        self.layout = QGridLayout(self.content)
        self.layout.setSpacing(16)
        self.layout.setContentsMargins(16, 16, 16, 16)
        self.content.setLayout(self.layout)
        self.setWidget(self.content)
        self.video_cards = []
        self.current_page = 1
        self.total_pages = 1
        self.query = ""
        self.sort_by = "recent"
        self.playlist_id = None
        self.feed_id = None
        self.feed_items_cache = []
        self.language_filter = None
        self.category_filter = None
        self.duration_filter = None
        self.is_loading = False
        self.verticalScrollBar().valueChanged.connect(self.on_scroll)
        self.setFocusPolicy(Qt.StrongFocus)

    def on_scroll(self, value):
        if self.is_loading:
            return
        scroll_bar = self.verticalScrollBar()
        if scroll_bar.value() >= scroll_bar.maximum() - 200:
            self.load_more_videos()

    def load_more_videos(self):
        if self.is_loading or (self.playlist_id is not None) or (self.feed_id is not None):
            return
        self.is_loading = True
        self.current_page += 1
        videos, total_results = SearchService.search_videos(
            self.query,
            page=self.current_page,
            count=50,
            sort_by=self.sort_by,
            language=self.language_filter,
            category=self.category_filter,
            duration=self.duration_filter
        )
        if not videos:
            self.is_loading = False
            return
        columns = max(1, (self.width() - 32) // 380)
        for idx, video in enumerate(videos):
            show_selector = self.playlist_id is not None
            card = VideoCard(video, self.playlist_id, self.main_window, show_selector=show_selector)
            card.video_removed.connect(self.on_video_removed)
            self.video_cards.append(card)
            row, col = divmod(len(self.video_cards) - 1, columns)
            self.layout.addWidget(card, row, col)
        self.is_loading = False

    def update_videos(self, videos: List[Video], query: str = "", total_results: int = 0,
                     page: int = 1, playlist_id: Optional[int] = None, feed_id: Optional[int] = None):
        self.query = query
        self.current_page = page
        self.playlist_id = playlist_id
        self.feed_id = feed_id
        for card in self.video_cards:
            if card.thumbnail_loader:
                card.thumbnail_loader.stop()
            card.deleteLater()
        self.video_cards.clear()
        columns = max(1, (self.width() - 32) // 380)
        for idx, video in enumerate(videos):
            show_selector = playlist_id is not None
            card = VideoCard(video, playlist_id, self.main_window, show_selector=show_selector)
            card.video_removed.connect(self.on_video_removed)
            self.video_cards.append(card)
            row, col = divmod(idx, columns)
            self.layout.addWidget(card, row, col)

    def load_page(self, page: int):
        if page < 1:
            return
        if self.playlist_id is not None:
            videos = PlaylistService.get_playlist_videos(self.playlist_id)
            self.update_videos(videos, playlist_id=self.playlist_id)
        elif self.feed_id is not None and self.feed_id != -1:
            if not self.feed_items_cache:
                self.feed_items_cache = RSSService.get_feed_items(self.feed_id, limit=None)
            videos = [
                Video(
                    title=item.title,
                    url=item.url,
                    thumbnail_url=item.thumbnail_url,
                    duration=item.duration,
                    video_id=item.video_id,
                    platform=item.platform,
                    author=item.author,
                    published=item.published
                )
                for item in self.feed_items_cache
            ]
            self.update_videos(videos, playlist_id=None, feed_id=self.feed_id, page=1)
        else:
            videos, total_results = SearchService.search_videos(
                self.query,
                page=page,
                count=50,
                sort_by=self.sort_by,
                language=self.language_filter,
                category=self.category_filter,
                duration=self.duration_filter
            )
            self.total_pages = max(1, (total_results + 49) // 50)
            self.update_videos(videos, self.query, total_results, page, self.playlist_id)
        self.is_loading = False

    def delete_selected_videos(self):
        selected_videos = []
        for card in self.video_cards:
            if hasattr(card, 'checkbox') and card.checkbox.isChecked():
                selected_videos.append(card.video.video_id)
        if not selected_videos:
            QMessageBox.warning(self, "Attenzione", "Nessun video selezionato!")
            return
        if QMessageBox.question(
            self, "Conferma", f"Rimuovere {len(selected_videos)} video?",
            QMessageBox.Yes | QMessageBox.No
        ) == QMessageBox.Yes:
            if self.playlist_id is not None:
                for video_id in selected_videos:
                    PlaylistService.remove_from_playlist(self.playlist_id, video_id)
            elif self.feed_id is not None and self.feed_id != -1:
                QMessageBox.warning(self, "Attenzione", "Non puoi rimuovere singoli video da un feed. Rimuovi l'intero feed dalla gestione feed.")
                return
            self.refresh_videos()

    def refresh_videos(self):
        if self.playlist_id is not None:
            videos = PlaylistService.get_playlist_videos(self.playlist_id)
            self.update_videos(videos, playlist_id=self.playlist_id)
        elif self.feed_id is not None and self.feed_id != -1:
            self.feed_items_cache = RSSService.get_feed_items(self.feed_id, limit=None)
            videos = [
                Video(
                    title=item.title,
                    url=item.url,
                    thumbnail_url=item.thumbnail_url,
                    duration=item.duration,
                    video_id=item.video_id,
                    platform=item.platform,
                    author=item.author,
                    published=item.published
                )
                for item in self.feed_items_cache
            ]
            self.update_videos(videos, playlist_id=None, feed_id=self.feed_id)
        else:
            self.load_page(self.current_page)

    def on_video_removed(self, video_id):
        self.refresh_videos()

    def resizeEvent(self, event):
        super().resizeEvent(event)
        if self.video_cards:
            self.refresh_videos()

    def load_playlist_videos(self, playlist_id: int):
        self.playlist_id = playlist_id
        self.feed_id = None
        self.query = ""
        self.current_page = 1
        videos = PlaylistService.get_playlist_videos(playlist_id)
        self.update_videos(videos, playlist_id=playlist_id)

    def keyPressEvent(self, event):
        """Gestisce gli eventi della tastiera per la navigazione e la selezione."""
        if event.key() == Qt.Key_Up:
            self.navigate_videos(-1)
        elif event.key() == Qt.Key_Down:
            self.navigate_videos(1)
        elif event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return:
            self.play_selected_video()
        elif event.key() == Qt.Key_Delete:
            self.delete_selected_videos()
        else:
            super().keyPressEvent(event)

    def navigate_videos(self, direction):
        """Naviga tra i video usando i tasti freccia."""
        if not self.video_cards:
            return
        focused_widget = QApplication.focusWidget()
        if not isinstance(focused_widget, VideoCard):
            self.video_cards[0].setFocus()
            return
        current_index = self.video_cards.index(focused_widget)
        new_index = current_index + direction
        if 0 <= new_index < len(self.video_cards):
            self.video_cards[new_index].setFocus()

    def play_selected_video(self):
        """Riproduce il video attualmente in focus."""
        focused_widget = QApplication.focusWidget()
        if isinstance(focused_widget, VideoCard):
            focused_widget.play_with_default_player()

# --- Pannello Playlist ---
class PlaylistPanel(QWidget):
    def __init__(self, main_window):
        super().__init__()
        self.main_window = main_window
        self.setStyleSheet(f"background: {Theme.BACKGROUND};")
        layout = QVBoxLayout(self)
        layout.setContentsMargins(12, 12, 12, 12)
        self.playlist_list = QListWidget()
        self.playlist_list.setStyleSheet(f"""
            QListWidget {{
                background: {Theme.SURFACE};
                border: 1px solid {Theme.BORDER};
                border-radius: 6px;
                color: {Theme.TEXT_PRIMARY};
                font-family: 'Inter';
                font-size: 14px;
                padding: 6px;
            }}
            QListWidget::item {{
                padding: 8px;
                border-radius: 4px;
            }}
            QListWidget::item:selected {{
                background: {Theme.PRIMARY};
                color: white;
            }}
            QListWidget::item:hover {{
                background: {Theme.SURFACE_HOVER};
            }}
        """)
        self.playlist_list.itemClicked.connect(self.show_videos)
        self.playlist_list.verticalScrollBar().valueChanged.connect(self.on_scroll)
        self.playlist_list.setFocusPolicy(Qt.StrongFocus)
        self.current_page = 1
        self.items_per_page = 20
        self.is_loading = False
        btn_widget = QWidget()
        btn_layout = QHBoxLayout(btn_widget)
        btn_layout.setContentsMargins(0, 0, 0, 0)
        add_btn = QPushButton("Nuova Playlist")
        add_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.PRIMARY};
                color: white;
                border: none;
                border-radius: 4px;
                padding: 6px 12px;
                font-family: 'Inter';
            }}
            QPushButton:hover {{ background: {Theme.PRIMARY_HOVER}; }}
        """)
        remove_btn = QPushButton("Elimina Playlist")
        remove_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.DANGER};
                color: white;
                border: none;
                border-radius: 4px;
                padding: 6px 12px;
                font-family: 'Inter';
            }}
            QPushButton:hover {{ background: #ff5555; }}
        """)
        btn_layout.addWidget(add_btn)
        btn_layout.addWidget(remove_btn)
        layout.addWidget(btn_widget)
        layout.addWidget(self.playlist_list)
        add_btn.clicked.connect(self.add_playlist)
        remove_btn.clicked.connect(self.remove_playlist)
        self.refresh_playlists()

    def on_scroll(self, value):
        if self.is_loading:
            return
        scroll_bar = self.playlist_list.verticalScrollBar()
        if scroll_bar.value() >= scroll_bar.maximum() - 100:
            self.load_more_playlists()

    def load_more_playlists(self):
        if self.is_loading:
            return
        self.is_loading = True
        self.current_page += 1
        playlists = PlaylistService.get_playlists()
        start_idx = (self.current_page - 1) * self.items_per_page
        end_idx = start_idx + self.items_per_page
        if start_idx >= len(playlists):
            self.is_loading = False
            return
        for pl in playlists[start_idx:end_idx]:
            item = QListWidgetItem(pl.name)
            item.setSizeHint(QSize(0, 32))
            self.playlist_list.addItem(item)
        self.is_loading = False

    def refresh_playlists(self):
        self.playlist_list.clear()
        self.current_page = 1
        playlists = PlaylistService.get_playlists()
        for pl in playlists[:self.items_per_page]:
            item = QListWidgetItem(pl.name)
            item.setSizeHint(QSize(0, 32))
            self.playlist_list.addItem(item)

    def add_playlist(self):
        name, ok = QInputDialog.getText(self, "Nuova Playlist", "Nome:")
        if ok and name:
            try:
                pl = Playlist(name=name)
                PlaylistService.save_playlist(pl)
                self.refresh_playlists()
            except Exception as e:
                QMessageBox.critical(self, "Errore", f"Impossibile creare la playlist: {e}")

    def remove_playlist(self):
        item = self.playlist_list.currentItem()
        if not item:
            return
        name = item.text()
        if PlaylistService.remove_playlist(name):
            self.refresh_playlists()
        else:
            QMessageBox.warning(self, "Attenzione", "Non puoi rimuovere questa playlist")

    def show_videos(self, item):
        pl_name = item.text()
        playlists = PlaylistService.get_playlists()
        pl = next((p for p in playlists if p.name == pl_name), None)
        if pl:
            self.main_window.show_view("search")
            self.main_window.video_grid.load_playlist_videos(pl.id)
            self.main_window.statusBar().showMessage(f"🎵 Playlist '{pl_name}' caricata ({len(PlaylistService.get_playlist_videos(pl.id))} video)")

    def keyPressEvent(self, event):
        """Gestisce gli eventi della tastiera per il pannello playlist."""
        if event.key() == Qt.Key_Delete:
            self.remove_playlist()
        elif event.key() == Qt.Key_N and event.modifiers() == Qt.ControlModifier:
            self.add_playlist()
        else:
            super().keyPressEvent(event)

# --- Pannello Gestione Feed ---
class FeedManagerPanel(QWidget):
    def __init__(self, main_window):
        super().__init__()
        self.main_window = main_window
        self.setStyleSheet(f"background: {Theme.BACKGROUND};")
        layout = QVBoxLayout(self)
        layout.setContentsMargins(12, 12, 12, 12)
        self.feed_list = QListWidget()
        self.feed_list.setStyleSheet(f"""
            QListWidget {{
                background: {Theme.SURFACE};
                border: 1px solid {Theme.BORDER};
                border-radius: 6px;
                color: {Theme.TEXT_PRIMARY};
                font-family: 'Inter';
                font-size: 14px;
                padding: 6px;
            }}
            QListWidget::item {{
                padding: 8px;
                border-radius: 4px;
            }}
            QListWidget::item:selected {{
                background: {Theme.PRIMARY};
                color: white;
            }}
            QListWidget::item:hover {{
                background: {Theme.SURFACE_HOVER};
            }}
        """)
        self.feed_list.itemDoubleClicked.connect(self.show_feed_content)
        self.feed_list.setFocusPolicy(Qt.StrongFocus)
        btn_widget = QWidget()
        btn_layout = QHBoxLayout(btn_widget)
        btn_layout.setContentsMargins(0, 0, 0, 0)
        self.refresh_btn = QPushButton("Aggiorna Tutti")
        self.refresh_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.PRIMARY};
                color: white;
                border: none;
                border-radius: 4px;
                padding: 6px 12px;
                font-family: 'Inter';
            }}
            QPushButton:hover {{ background: {Theme.PRIMARY_HOVER}; }}
        """)
        remove_btn = QPushButton("Rimuovi Feed")
        remove_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.DANGER};
                color: white;
                border: none;
                border-radius: 4px;
                padding: 6px 12px;
                font-family: 'Inter';
            }}
            QPushButton:hover {{ background: #ff5555; }}
        """)
        clear_all_btn = QPushButton("Cancella Tutti i Feed")
        clear_all_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.DANGER};
                color: white;
                border: none;
                border-radius: 4px;
                padding: 6px 12px;
                font-family: 'Inter';
            }}
            QPushButton:hover {{ background: #ff2222; }}
        """)
        btn_layout.addWidget(self.refresh_btn)
        btn_layout.addWidget(remove_btn)
        btn_layout.addWidget(clear_all_btn)
        layout.addWidget(self.feed_list)
        layout.addWidget(btn_widget)
        self.refresh_btn.clicked.connect(self.refresh_all_feeds_with_progress)
        remove_btn.clicked.connect(self.remove_selected_feed)
        clear_all_btn.clicked.connect(self.clear_all_feeds)
        self.refresh_feed_list()

    def refresh_all_feeds_with_progress(self):
        try:
            progress_dialog = QProgressDialog("Aggiornamento feed...", "Annulla", 0, 100, self)
            progress_dialog.setWindowTitle("Aggiornamento Feed")
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setAutoClose(False)
            progress_dialog.setValue(0)
            if RSSService.refresh_all_feeds():
                progress_dialog.setValue(100)
                progress_dialog.close()
                self.refresh_feed_list()
                QMessageBox.information(self, "Successo", "Tutti i feed sono stati aggiornati!")
            else:
                QMessageBox.warning(self, "Errore", "Impossibile aggiornare i feed")
        except Exception as e:
            QMessageBox.warning(self, "Errore", f"Errore durante l'aggiornamento: {str(e)}")

    def refresh_feed_list(self):
        self.feed_list.clear()
        for feed in RSSService.get_feeds():
            item = QListWidgetItem()
            icon = QIcon.fromTheme("application-rss+xml")
            item.setIcon(icon)
            item_text = f"{feed.title} "
            if feed.is_channel:
                item_text += "👤 "
            else:
                item_text += "📋 "
            item_text += f"({len(RSSService.get_feed_items(feed.id, limit=None))} video)"
            if feed.last_updated:
                item_text += f"\nUltimo aggiornamento: {feed.last_updated.strftime('%d/%m/%Y %H:%M')}"
            item.setText(item_text)
            item.setData(Qt.UserRole, feed.id)
            item.setSizeHint(QSize(0, 50))
            self.feed_list.addItem(item)

    def show_feed_content(self, item):
        feed_id = item.data(Qt.UserRole)
        self.main_window.show_view("search")
        self.main_window.video_grid.feed_id = feed_id
        self.main_window.video_grid.feed_items_cache = RSSService.get_feed_items_cached(feed_id)
        self.main_window.video_grid.current_page = 1
        videos = [
            Video(
                title=item.title,
                url=item.url,
                thumbnail_url=item.thumbnail_url,
                duration=item.duration,
                video_id=item.video_id,
                platform=item.platform,
                author=item.author,
                published=item.published
            )
            for item in self.main_window.video_grid.feed_items_cache
        ]
        self.main_window.video_grid.update_videos(
            videos,
            query=f"Feed: {item.text().split('(')[0].strip()}",
            total_results=len(videos),
            page=1,
            feed_id=feed_id
        )
        self.main_window.statusBar().showMessage(f"📡 Feed caricato ({len(videos)} video)")

    def remove_selected_feed(self):
        item = self.feed_list.currentItem()
        if not item:
            return
        feed_id = item.data(Qt.UserRole)
        if QMessageBox.question(
            self, "Conferma", "Rimuovere questo feed?",
            QMessageBox.Yes | QMessageBox.No
        ) == QMessageBox.Yes:
            if RSSService.remove_feed(feed_id):
                self.refresh_feed_list()
            else:
                QMessageBox.warning(self, "Errore", "Impossibile rimuovere il feed")

    def clear_all_feeds(self):
        if QMessageBox.question(
            self, "Conferma", "Rimuovere TUTTI i feed? Questa azione non può essere annullata.",
            QMessageBox.Yes | QMessageBox.No
        ) == QMessageBox.Yes:
            if RSSService.clear_all_feeds():
                self.refresh_feed_list()
                QMessageBox.information(self, "Successo", "Tutti i feed sono stati rimossi")
            else:
                QMessageBox.warning(self, "Errore", "Impossibile rimuovere i feed")

    def keyPressEvent(self, event):
        """Gestisce gli eventi della tastiera per il pannello gestione feed."""
        if event.key() == Qt.Key_Delete:
            self.remove_selected_feed()
        elif event.key() == Qt.Key_R and event.modifiers() == Qt.ControlModifier:
            self.refresh_all_feeds_with_progress()
        else:
            super().keyPressEvent(event)

# --- Pannello Impostazioni ---
class SettingsPanel(QWidget):
    def __init__(self, main_window):
        super().__init__()
        self.main_window = main_window
        self.setStyleSheet(f"background: {Theme.BACKGROUND};")
        layout = QVBoxLayout(self)
        layout.setContentsMargins(12, 12, 12, 12)
        title = QLabel("Impostazioni")
        title.setStyleSheet(f"""
            QLabel {{
                font-size: 18px;
                font-weight: bold;
                color: {Theme.TEXT_PRIMARY};
                margin-bottom: 16px;
            }}
        """)
        clear_data_btn = QPushButton("Cancella TUTTI i dati utente")
        clear_data_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.DANGER};
                color: white;
                border: none;
                border-radius: 4px;
                padding: 10px 20px;
                font-family: 'Inter';
                font-size: 14px;
            }}
            QPushButton:hover {{ background: #ff2222; }}
        """)
        clear_data_btn.clicked.connect(self.clear_all_user_data)
        self.player_choice = QComboBox()
        self.player_choice.addItems(["Browser Predefinito", "mpv"])
        self.player_choice.setStyleSheet(f"""
            QComboBox {{
                background: {Theme.SURFACE};
                border: 1px solid {Theme.BORDER};
                border-radius: 6px;
                color: {Theme.TEXT_PRIMARY};
                padding: 8px 12px;
                min-width: 200px;
            }}
            QComboBox::drop-down {{ border: 0px; }}
            QComboBox QAbstractItemView {{
                background: {Theme.SURFACE};
                border: 1px solid {Theme.BORDER};
                color: {Theme.TEXT_PRIMARY};
                selection-background-color: {Theme.PRIMARY};
            }}
        """)
        self.player_choice.currentTextChanged.connect(self.save_player_preference)
        self.mpv_buffer_check = QCheckBox("Abilita buffer esteso per mpv (--demuxer-lavf-buffersize=327668)")
        self.mpv_buffer_check.setStyleSheet(f"color: {Theme.TEXT_PRIMARY};")
        self.mpv_buffer_check.stateChanged.connect(self.save_mpv_buffer_preference)
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT value FROM user_preferences WHERE key = 'player'")
            row = cursor.fetchone()
            if row:
                self.player_choice.setCurrentText(row[0])
            cursor.execute("SELECT value FROM user_preferences WHERE key = 'mpv_buffer'")
            row = cursor.fetchone()
            if row:
                self.mpv_buffer_check.setChecked(row[0] == "True")
        layout.addWidget(title)
        layout.addWidget(QLabel("Lettore predefinito:"))
        layout.addWidget(self.player_choice)
        layout.addWidget(self.mpv_buffer_check)
        layout.addWidget(clear_data_btn)
        layout.addStretch()
        self.setFocusPolicy(Qt.StrongFocus)

    def save_player_preference(self, choice):
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute(
                "INSERT OR REPLACE INTO user_preferences (key, value) VALUES (?, ?)",
                ("player", choice)
            )
            conn.commit()
        self.main_window.statusBar().showMessage(f"Lettore predefinito impostato su: {choice}")

    def save_mpv_buffer_preference(self, state):
        with db_manager.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute(
                "INSERT OR REPLACE INTO user_preferences (key, value) VALUES (?, ?)",
                ("mpv_buffer", "True" if state == Qt.Checked else "False")
            )
            conn.commit()
        self.main_window.statusBar().showMessage(
            f"Opzione buffer MPV {'abilitata' if state == Qt.Checked else 'disabilitata'}"
        )

    def clear_all_user_data(self):
        if QMessageBox.question(
            self, "Conferma",
            "Questa azione cancellerà TUTTE le tue playlist (eccetto 'Guarda più tardi'), "
            "tutti i feed RSS e le preferenze utente. \n\n"
            "Questa azione NON può essere annullata. \n\n"
            "Sei sicuro di voler continuare?",
            QMessageBox.Yes | QMessageBox.No
        ) == QMessageBox.Yes:
            if db_manager.clear_all_user_data():
                QMessageBox.information(self, "Successo", "Tutti i dati utente sono stati cancellati. L'applicazione verrà riavviata.")
                from PyQt5.QtCore import QProcess
                QApplication.quit()
                QProcess.startDetached(sys.executable, [sys.argv[0]])

# --- Finestra di Ricerca Avanzata ---
class AdvancedSearchDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Ricerca Avanzata")
        self.setFixedSize(650, 600)
        self.setStyleSheet(f"""
            QDialog {{
                background: {Theme.BACKGROUND};
                color: {Theme.TEXT_PRIMARY};
            }}
            QLabel {{
                font-size: 15px;
                color: {Theme.TEXT_PRIMARY};
                margin-bottom: 6px;
                font-weight: 500;
            }}
            QComboBox, QLineEdit {{
                background: {Theme.SURFACE};
                border: 1.5px solid {Theme.BORDER};
                border-radius: 8px;
                color: {Theme.TEXT_PRIMARY};
                padding: 12px 16px;
                font-size: 15px;
                min-height: 44px;
                selection-background-color: {Theme.PRIMARY}40;
            }}
            QComboBox::drop-down {{
                border: none;
                width: 30px;
            }}
            QComboBox::down-arrow {{
                image: none;
                border-left: 5px solid transparent;
                border-right: 5px solid transparent;
                border-top: 5px solid {Theme.TEXT_SECONDARY};
                width: 0px;
                height: 0px;
            }}
            QComboBox QAbstractItemView {{
                background: {Theme.SURFACE};
                border: 1px solid {Theme.BORDER};
                border-radius: 8px;
                outline: none;
                color: {Theme.TEXT_PRIMARY};
                padding: 8px;
                selection-background-color: {Theme.PRIMARY};
            }}
            QLineEdit:focus, QComboBox:focus {{
                border-color: {Theme.PRIMARY};
                background: {Theme.SURFACE_HOVER};
            }}
            QPushButton {{
                background: {Theme.PRIMARY};
                color: white;
                border: none;
                border-radius: 8px;
                padding: 14px 28px;
                font-size: 15px;
                font-weight: 600;
                min-height: 44px;
                min-width: 120px;
            }}
            QPushButton:hover {{
                background: {Theme.PRIMARY_HOVER};
                transform: translateY(-1px);
            }}
            QPushButton:pressed {{
                background: {Theme.PRIMARY};
                transform: translateY(0px);
            }}
        """)
        layout = QVBoxLayout(self)
        layout.setSpacing(24)
        layout.setContentsMargins(32, 32, 32, 32)
        header_layout = QVBoxLayout()
        header_layout.setSpacing(8)
        title_layout = QHBoxLayout()
        title_icon = QLabel("🔍")
        title_icon.setStyleSheet("font-size: 24px; margin-right: 12px;")
        title_label = QLabel("Ricerca Avanzata")
        title_label.setStyleSheet(f"""
            QLabel {{
                font-size: 22px;
                font-weight: bold;
                color: {Theme.TEXT_PRIMARY};
                margin: 0;
            }}
        """)
        title_layout.addWidget(title_icon)
        title_layout.addWidget(title_label)
        title_layout.addStretch()
        subtitle_label = QLabel("Raffina la tua ricerca con filtri specifici")
        subtitle_label.setStyleSheet(f"""
            QLabel {{
                font-size: 14px;
                color: {Theme.TEXT_SECONDARY};
                margin: 0;
            }}
        """)
        header_layout.addLayout(title_layout)
        header_layout.addWidget(subtitle_label)
        layout.addLayout(header_layout)
        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setStyleSheet(f"background: {Theme.BORDER}; margin: 16px 0;")
        layout.addWidget(separator)
        form_layout = QVBoxLayout()
        form_layout.setSpacing(20)
        query_layout = QVBoxLayout()
        query_layout.setSpacing(8)
        query_label = QLabel("Termine di Ricerca")
        self.query_input = QLineEdit()
        self.query_input.setPlaceholderText("Inserisci parole chiave, frasi o argomenti...")
        self.query_input.setClearButtonEnabled(True)
        query_layout.addWidget(query_label)
        query_layout.addWidget(self.query_input)
        form_layout.addLayout(query_layout)
        filters_grid = QGridLayout()
        filters_grid.setHorizontalSpacing(20)
        filters_grid.setVerticalSpacing(16)
        filters_grid.addWidget(QLabel("Lingua"), 0, 0)
        self.language_filter = QComboBox()
        self.language_filter.addItem("Qualsiasi Lingua", "")
        self.language_filter.addItem("Inglese", "en")
        self.language_filter.addItem("Cinese Semplificato", "zh-Hans")
        self.language_filter.addItem("Cinese Tradizionale", "zh-Hant")
        self.language_filter.addItem("Spagnolo", "es")
        self.language_filter.addItem("Francese", "fr")
        self.language_filter.addItem("Arabo", "ar")
        self.language_filter.addItem("Portoghese Brasiliano", "pt")
        self.language_filter.addItem("Portoghese", "pt-PT")
        self.language_filter.addItem("Russo", "ru")
        self.language_filter.addItem("Giapponese", "ja")
        self.language_filter.addItem("Tedesco", "de")
        self.language_filter.addItem("Turco", "tr")
        self.language_filter.addItem("Vietnamita", "vi")
        self.language_filter.addItem("Italiano", "it")
        self.language_filter.addItem("Farsi", "fa")
        self.language_filter.addItem("Polacco", "pl")
        self.language_filter.addItem("Ucraino", "uk")
        self.language_filter.addItem("Olandese", "nl")
        self.language_filter.addItem("Lao", "th")
        self.language_filter.addItem("Ungherese", "hu")
        self.language_filter.addItem("Greco", "el")
        self.language_filter.addItem("Ceco", "cs")
        self.language_filter.addItem("Svedese", "sv")
        self.language_filter.addItem("Norvegese", "no")
        self.language_filter.addItem("Finlandese", "fi")
        self.language_filter.addItem("Slovacco", "sk")
        self.language_filter.addItem("Croato", "hr")
        self.language_filter.addItem("Albanese", "sq")
        self.language_filter.addItem("Catalano", "ca")
        self.language_filter.addItem("Basco", "eu")
        self.language_filter.addItem("Gaelico Irlandese", "gd")
        self.language_filter.addItem("Islandese", "is")
        self.language_filter.addItem("Occitano", "oc")
        self.language_filter.addItem("Kabyle", "kab")
        self.language_filter.addItem("Esperanto", "eo")
        self.language_filter.addItem("Toki Pona", "tok")
        filters_grid.addWidget(self.language_filter, 0, 1)
        filters_grid.addWidget(QLabel("Categoria"), 1, 0)
        self.category_filter = QComboBox()
        self.category_filter.addItem("Qualsiasi Categoria", "")
        self.category_filter.addItem("💻 Scienza & Tecnologia", "15")
        self.category_filter.addItem("🛠️ HowTo", "12")
        self.category_filter.addItem("🎬 Film", "2")
        self.category_filter.addItem("📚 Istruzione", "13")
        self.category_filter.addItem("🍿 Intrattenimento", "10")
        self.category_filter.addItem("✊ Attivismo", "14")
        self.category_filter.addItem("🎵 Musica", "1")
        self.category_filter.addItem("🎤 Commedia", "9")
        self.category_filter.addItem("📰 Notizie & Politica", "11")
        self.category_filter.addItem("🎮 Gaming", "7")
        self.category_filter.addItem("⚽ Sport", "5")
        self.category_filter.addItem("🐱 Animali", "16")
        self.category_filter.addItem("🖌️ Arte", "4")
        self.category_filter.addItem("🧳 Viaggi", "6")
        self.category_filter.addItem("👥 Persone", "8")
        self.category_filter.addItem("🚗 Veicoli", "3")
        self.category_filter.addItem("🍎 Cibo", "18")
        filters_grid.addWidget(self.category_filter, 1, 1)
        filters_grid.addWidget(QLabel("Durata"), 2, 0)
        self.duration_filter = QComboBox()
        self.duration_filter.addItem("Qualsiasi Durata", "")
        self.duration_filter.addItem("⏱️ Breve (< 4 min)", "short")
        self.duration_filter.addItem("⏱️ Media (4-20 min)", "medium")
        self.duration_filter.addItem("⏱️ Lunga (> 20 min)", "long")
        filters_grid.addWidget(self.duration_filter, 2, 1)
        form_layout.addLayout(filters_grid)
        layout.addLayout(form_layout)
        layout.addStretch()
        button_layout = QHBoxLayout()
        button_layout.setSpacing(16)
        clear_btn = QPushButton("Cancella Filtri")
        clear_btn.setStyleSheet(f"""
            QPushButton {{
                background: transparent;
                color: {Theme.TEXT_SECONDARY};
                border: 1.5px solid {Theme.BORDER};
                border-radius: 8px;
                padding: 14px 28px;
                font-size: 15px;
                font-weight: 600;
                min-height: 44px;
                min-width: 120px;
            }}
            QPushButton:hover {{
                background: {Theme.SURFACE_HOVER};
                color: {Theme.TEXT_PRIMARY};
                border-color: {Theme.TEXT_SECONDARY};
            }}
        """)
        clear_btn.clicked.connect(self.clear_filters)
        cancel_btn = QPushButton("Annulla")
        cancel_btn.setStyleSheet(f"""
            QPushButton {{
                background: {Theme.SECONDARY};
                color: {Theme.TEXT_PRIMARY};
                border: none;
                border-radius: 8px;
                padding: 14px 28px;
                font-size: 15px;
                font-weight: 600;
                min-height: 44px;
                min-width: 120px;
            }}
            QPushButton:hover {{
                background: {Theme.SECONDARY_HOVER};
            }}
        """)
        cancel_btn.clicked.connect(self.reject)
        search_btn = QPushButton("Cerca")
        search_btn.setDefault(True)
        search_btn.clicked.connect(self.accept)
        button_layout.addWidget(clear_btn)
        button_layout.addStretch()
        button_layout.addWidget(cancel_btn)
        button_layout.addWidget(search_btn)
        layout.addLayout(button_layout)

    def clear_filters(self):
        self.query_input.clear()
        self.language_filter.setCurrentIndex(0)
        self.duration_filter.setCurrentIndex(0)
        self.category_filter.setCurrentIndex(0)
        self.query_input.setFocus()

    def get_search_params(self):
        return {
            "query": self.query_input.text().strip(),
            "language": self.language_filter.currentData(),
            "duration": self.duration_filter.currentData(),
            "category": self.category_filter.currentData(),
        }

# --- Navigazione Sidebar ---
class Sidebar(QWidget):
    view_changed = pyqtSignal(str)
    feed_manager_clicked = pyqtSignal()
    feed_content_clicked = pyqtSignal()
    playlists_clicked = pyqtSignal()

    def __init__(self, main_window):
        super().__init__()
        self.main_window = main_window
        self.setObjectName("sidebar")
        self.setStyleSheet(Theme.sidebar_style())
        self.setFixedWidth(280)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        title = QLabel("PurpleTube Player")
        title.setStyleSheet(f"""
            QLabel {{
                font-size: 22px;
                font-weight: bold;
                color: {Theme.TEXT_PRIMARY};
                padding: 12px 16px;
                margin-bottom: 6px;
            }}
        """)
        layout.addWidget(title)
        self.buttons = {
            "search": QPushButton("🔍 Ricerca"),
            "playlist": QPushButton("🎵 Playlist"),
            "feed_manager": QPushButton("📡 Gestione Feed"),
            "feed_content": QPushButton("📰 Contenuto Feed"),
            "settings": QPushButton("⚙️ Impostazioni")
        }
        for btn in self.buttons.values():
            btn.setCheckable(True)
            btn.setStyleSheet(f"""
                QPushButton {{
                    background: transparent;
                    color: {Theme.TEXT_SECONDARY};
                    border: none;
                    text-align: left;
                    padding: 8px 16px;
                    font-size: 22px;
                    border-radius: 4px;
                    margin: 2px 6px;
                }}
                QPushButton:hover {{
                    background: {Theme.SURFACE_HOVER};
                    color: {Theme.TEXT_PRIMARY};
                }}
                QPushButton:checked {{
                    background: {Theme.PRIMARY};
                    color: white;
                }}
            """)
            btn.clicked.connect(self.on_button_clicked)
        self.buttons["playlist"].clicked.connect(self.on_playlist_clicked)
        layout.addWidget(self.buttons["search"])
        layout.addWidget(self.buttons["playlist"])
        layout.addWidget(self.buttons["feed_manager"])
        layout.addWidget(self.buttons["feed_content"])
        layout.addWidget(self.buttons["settings"])
        layout.addStretch()
        self.buttons["search"].setChecked(True)
        self.buttons["search"].setProperty("active", True)

    def on_playlist_clicked(self):
        self.playlists_clicked.emit()
        self.on_button_clicked()

    def on_button_clicked(self):
        sender = self.main_window.sender()
        for btn in self.buttons.values():
            btn.setChecked(False)
            btn.setProperty("active", False)
            btn.style().unpolish(btn)
            btn.style().polish(btn)
        sender.setChecked(True)
        sender.setProperty("active", True)
        sender.style().unpolish(sender)
        sender.style().polish(sender)
        view_name = None
        for name, btn in self.buttons.items():
            if btn is sender:
                view_name = name
                break
        if view_name == "feed_manager":
            self.feed_manager_clicked.emit()
        elif view_name == "feed_content":
            self.feed_content_clicked.emit()
        if view_name:
            self.view_changed.emit(view_name)

    def set_active_view(self, view_name):
        for btn_name, btn in self.buttons.items():
            if btn_name == view_name:
                btn.setChecked(True)
                btn.setProperty("active", True)
                btn.style().unpolish(btn)
                btn.style().polish(btn)
            else:
                btn.setChecked(False)
                btn.setProperty("active", False)
                btn.style().unpolish(btn)
                btn.style().polish(btn)

# --- Finestra Principale ---
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowIcon(QIcon("purpletube.svg"))
        self.setWindowTitle("PurpleTube Player")
        self.resize(1200, 800)
        self.setStyleSheet(Theme.global_stylesheet())
        for toolbar in self.findChildren(QToolBar):
            toolbar.hide()
        self.statusBar().setStyleSheet(f"""
            QStatusBar {{
                background: {Theme.SURFACE};
                color: {Theme.TEXT_SECONDARY};
                font-family: 'Inter';
                padding: 6px;
            }}
        """)
        central_widget = QWidget()
        main_layout = QHBoxLayout(central_widget)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)
        self.sidebar = Sidebar(self)
        main_layout.addWidget(self.sidebar)
        content_widget = QWidget()
        content_layout = QVBoxLayout(content_widget)
        content_layout.setContentsMargins(12, 12, 12, 12)
        content_layout.setSpacing(12)
        self.stacked_widget = QStackedWidget()
        self.video_grid = VideoGrid(self)
        self.playlist_panel = PlaylistPanel(self)
        self.feed_manager = FeedManagerPanel(self)
        self.feed_content_tab = FeedContentTab(self)
        self.settings_panel = SettingsPanel(self)
        self.stacked_widget.addWidget(self.video_grid)
        self.stacked_widget.addWidget(self.playlist_panel)
        self.stacked_widget.addWidget(self.feed_manager)
        self.stacked_widget.addWidget(self.feed_content_tab)
        self.stacked_widget.addWidget(self.settings_panel)
        search_widget = self._create_search_widget()
        content_layout.addWidget(search_widget)
        content_layout.addWidget(self.stacked_widget)
        main_layout.addWidget(content_widget, stretch=1)
        self.setCentralWidget(central_widget)
        self.sidebar.view_changed.connect(self.show_view)
        self.sidebar.feed_manager_clicked.connect(self.feed_manager.refresh_feed_list)
        self.sidebar.feed_content_clicked.connect(self.feed_content_tab.load_feed_content)
        self.sidebar.playlists_clicked.connect(self.playlist_panel.refresh_playlists)
        self.show_view("search")
        self._create_menu_bar()
        self.setup_shortcuts()

    def setup_shortcuts(self):
        """Configura le scorciatoie da tastiera per l'applicazione."""
        # Scorciatoia ricerca (Ctrl+F)
        search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self)
        search_shortcut.activated.connect(lambda: self.search_bar.setFocus())
        # Scorciatoia ricerca avanzata (Ctrl+Shift+F)
        advanced_search_shortcut = QShortcut(QKeySequence("Ctrl+Shift+F"), self)
        advanced_search_shortcut.activated.connect(self.open_advanced_search)
        # Scorciatoia aggiungi feed (Ctrl+Shift+A)
        add_feed_shortcut = QShortcut(QKeySequence("Ctrl+Shift+A"), self)
        add_feed_shortcut.activated.connect(self.add_feed_from_url)
        # Scorciatoia aggiorna feed (F5)
        refresh_shortcut = QShortcut(QKeySequence("F5"), self)
        refresh_shortcut.activated.connect(self.feed_content_tab.refresh_all_content_with_progress)
        # Scorciatoia mostra/nascondi sidebar (Ctrl+B)
        toggle_sidebar_shortcut = QShortcut(QKeySequence("Ctrl+B"), self)
        toggle_sidebar_shortcut.activated.connect(self.toggle_sidebar)
        # Scorciatoie cambio vista (Ctrl+1 a Ctrl+5)
        search_view_shortcut = QShortcut(QKeySequence("Ctrl+1"), self)
        search_view_shortcut.activated.connect(lambda: self.show_view("search"))
        playlist_view_shortcut = QShortcut(QKeySequence("Ctrl+2"), self)
        playlist_view_shortcut.activated.connect(lambda: self.show_view("playlist"))
        feed_manager_shortcut = QShortcut(QKeySequence("Ctrl+3"), self)
        feed_manager_shortcut.activated.connect(lambda: self.show_view("feed_manager"))
        feed_content_shortcut = QShortcut(QKeySequence("Ctrl+4"), self)
        feed_content_shortcut.activated.connect(lambda: self.show_view("feed_content"))
        settings_shortcut = QShortcut(QKeySequence("Ctrl+5"), self)
        settings_shortcut.activated.connect(lambda: self.show_view("settings"))
        # Scorciatoia riproduci/pausa video (Spazio)
        play_pause_shortcut = QShortcut(QKeySequence("Space"), self)
        play_pause_shortcut.activated.connect(self.toggle_play_pause)
        # Scorciatoia elimina video selezionati (Canc)
        delete_shortcut = QShortcut(QKeySequence("Delete"), self)
        delete_shortcut.activated.connect(self.video_grid.delete_selected_videos)
        # Scorciatoia esci dall'applicazione (Ctrl+Q)
        quit_shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
        quit_shortcut.activated.connect(QApplication.quit)

    def toggle_sidebar(self):
        """Mostra/nasconde la sidebar."""
        if self.sidebar.isVisible():
            self.sidebar.hide()
        else:
            self.sidebar.show()

    def toggle_play_pause(self):
        """Attiva/disattiva la riproduzione per la scheda video attualmente in focus."""
        focused_widget = QApplication.focusWidget()
        if isinstance(focused_widget, VideoCard):
            focused_widget.play_with_default_player()

    def _create_search_widget(self):
        search_widget = QWidget()
        search_layout = QHBoxLayout(search_widget)
        search_layout.setContentsMargins(0, 0, 0, 0)
        search_layout.setSpacing(8)
        self.search_bar = QLineEdit()
        self.search_bar.setPlaceholderText("Cerca su SepiaSearch (es. 'linux')...")
        self.search_bar.setFixedHeight(32)
        self.search_bar.setStyleSheet(Theme.search_bar_style())
        search_btn = QPushButton("Cerca")
        search_btn.setFixedHeight(32)
        search_btn.setStyleSheet(Theme.global_stylesheet())
        advanced_search_btn = QPushButton("Ricerca Avanzata")
        advanced_search_btn.setFixedHeight(32)
        advanced_search_btn.setStyleSheet(Theme.global_stylesheet())
        add_feed_btn = QPushButton("Aggiungi Feed")
        add_feed_btn.setFixedHeight(32)
        add_feed_btn.setStyleSheet(Theme.global_stylesheet())
        search_btn.clicked.connect(self.search)
        advanced_search_btn.clicked.connect(self.open_advanced_search)
        add_feed_btn.clicked.connect(self.add_feed_from_url)
        self.search_bar.returnPressed.connect(self.search)
        search_layout.addWidget(self.search_bar)
        search_layout.addWidget(search_btn)
        search_layout.addWidget(advanced_search_btn)
        search_layout.addWidget(add_feed_btn)
        return search_widget

    def open_advanced_search(self):
        dialog = AdvancedSearchDialog(self)
        if dialog.exec_() == QDialog.Accepted:
            search_params = dialog.get_search_params()
            query = search_params["query"]
            language = search_params["language"]
            duration = search_params["duration"]
            category = search_params["category"]
            if not query:
                self.statusBar().showMessage("Inserisci un termine di ricerca...")
                return
            self.statusBar().showMessage(f"🔍 Ricerca di '{query}' con filtri...")
            self.video_grid.language_filter = language
            self.video_grid.category_filter = category
            self.video_grid.duration_filter = duration
            videos, total_results = SearchService.search_videos(
                query=query,
                page=1,
                count=50,
                sort_by=self.video_grid.sort_by,
                language=language,
                category=category,
                duration=duration
            )
            self.video_grid.total_pages = max(1, (total_results + 49) // 50)
            self.video_grid.update_videos(
                videos, query, total_results, page=1
            )
            filter_description = []
            if language:
                filter_description.append(f"lingua={language}")
            if category:
                filter_description.append(f"categoria={category}")
            if duration:
                filter_description.append(f"durata={duration}")
            filter_text = ", ".join(filter_description) if filter_description else "nessun filtro"
            self.statusBar().showMessage(
                f"📊 Trovati {len(videos)} video per '{query}' con filtri: {filter_text} "
                f"(pagina 1/{self.video_grid.total_pages})"
            )

    def add_feed_from_url(self):
        url, ok = QInputDialog.getText(
            self,
            "Aggiungi Feed RSS",
            "Incolla l'URL del canale PeerTube o l'URL del feed RSS:"
        )
        if ok and url:
            if "/feeds/videos.xml" in url:
                if RSSService.add_feed(url):
                    QMessageBox.information(self, "Successo", "Feed RSS aggiunto con successo!")
                    self.statusBar().showMessage("✓ Feed RSS aggiunto.")
                else:
                    QMessageBox.warning(self, "Errore", "Impossibile aggiungere il feed RSS.")
            else:
                feed_url = extract_channel_feed_url(url)
                if feed_url:
                    if RSSService.add_feed(feed_url):
                        QMessageBox.information(self, "Successo", "Feed RSS del canale aggiunto con successo!")
                        self.statusBar().showMessage(f"✓ Feed RSS del canale '{url}' aggiunto.")
                    else:
                        QMessageBox.warning(self, "Errore", "Impossibile aggiungere il feed RSS del canale.")
                else:
                    QMessageBox.warning(self, "Errore", "Impossibile estrarre il feed RSS dall'URL fornito.")

    def _create_menu_bar(self):
        menu_bar = self.menuBar()
        # Menu File
        file_menu = menu_bar.addMenu("&File")
        exit_action = QAction("Esci", self)
        exit_action.triggered.connect(QApplication.quit)
        file_menu.addAction(exit_action)
        # Menu Modifica
        edit_menu = menu_bar.addMenu("&Modifica")
        clear_data_action = QAction("Cancella tutti i dati utente", self)
        clear_data_action.triggered.connect(self.settings_panel.clear_all_user_data)
        edit_menu.addAction(clear_data_action)
        # Menu Aiuto
        help_menu = menu_bar.addMenu("&Aiuto")
        # Azione Informazioni
        about_action = QAction("Informazioni", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)
        # Azione Scorciatoie
        shortcuts_action = QAction("Scorciatoie", self)
        shortcuts_action.triggered.connect(self.show_shortcuts)
        help_menu.addAction(shortcuts_action)

    def show_shortcuts(self):
        shortcuts_text = """
        <h2>Scorciatoie da Tastiera</h2>
        <table border="0" cellpadding="5">
            <tr><td><b>Ctrl+F</b></td><td>Focus sulla barra di ricerca</td></tr>
            <tr><td><b>Ctrl+Shift+F</b></td><td>Apri ricerca avanzata</td></tr>
            <tr><td><b>Ctrl+Shift+A</b></td><td>Aggiungi feed RSS</td></tr>
            <tr><td><b>F5</b></td><td>Aggiorna tutti i feed</td></tr>
            <tr><td><b>Ctrl+B</b></td><td>Mostra/nascondi sidebar</td></tr>
            <tr><td><b>Spazio</b></td><td>Riproduci/pausa video</td></tr>
            <tr><td><b>Canc</b></td><td>Elimina video selezionati</td></tr>
            <tr><td><b>Ctrl+1</b></td><td>Passa alla vista Ricerca</td></tr>
            <tr><td><b>Ctrl+2</b></td><td>Passa alla vista Playlist</td></tr>
            <tr><td><b>Ctrl+3</b></td><td>Passa alla vista Gestione Feed</td></tr>
            <tr><td><b>Ctrl+4</b></td><td>Passa alla vista Contenuto Feed</td></tr>
            <tr><td><b>Ctrl+5</b></td><td>Passa alla vista Impostazioni</td></tr>
            <tr><td><b>Ctrl+Q</b></td><td>Esci dall'applicazione</td></tr>
        </table>
        """
        QMessageBox.about(self, "Scorciatoie da Tastiera", shortcuts_text)

    def show_view(self, view_name):
        self.sidebar.set_active_view(view_name)
        view_index = {
            "search": 0,
            "playlist": 1,
            "feed_manager": 2,
            "feed_content": 3,
            "settings": 4
        }.get(view_name, 0)
        self.stacked_widget.setCurrentIndex(view_index)
        if view_name == "search":
            if not self.video_grid.query and self.video_grid.feed_id is None and self.video_grid.playlist_id is None:
                self.video_grid.load_page(1)

    def search(self):
        query = self.search_bar.text().strip()
        if not query:
            self.statusBar().showMessage("Inserisci un termine di ricerca...")
            return
        self.video_grid.language_filter = None
        self.video_grid.category_filter = None
        self.video_grid.duration_filter = None
        self.statusBar().showMessage(f"🔍 Ricerca di '{query}'...")
        videos, total_results = SearchService.search_videos(
            query, page=1, count=50, sort_by=self.video_grid.sort_by
        )
        self.video_grid.total_pages = max(1, (total_results + 49) // 50)
        self.video_grid.update_videos(
            videos, query, total_results, page=1
        )
        self.statusBar().showMessage(
            f"📊 Trovati {len(videos)} video per '{query}' "
            f"(pagina 1/{self.video_grid.total_pages})"
        )

    def show_about(self):
        about_text = """
        <h2>PurpleTube Player</h2>
        <p>Un lettore video open-source per contenuti decentralizzati PeerTube, progettato per promuovere la libertà digitale e l'accesso etico ai media.</p>
        <p><strong>Versione:</strong> 1.0</p>
        <p><strong>Licenza:</strong> <a href="https://www.gnu.org/licenses/gpl-3.0.en.html">GNU GPL v3</a></p>
        <p><strong>Sviluppatore:</strong> Angelo Massaro</p>
        <p>Per segnalare bug o richiedere informazioni, contatta: <a href="mailto:amassaro@boostmedia.it">amassaro@boostmedia.it</a></p>
        <p>Parte del progetto <a href="https://boostmedia.it/">BoostMedia APS</a>, un'iniziativa per promuovere strumenti digitali etici e open-source.</p>
        """
        QMessageBox.about(self, "Informazioni su PurpleTube Player", about_text)

# --- Main ---
if __name__ == "__main__":
    check_yt_dlp_version()
    check_mpv_installed()
    app = QApplication(sys.argv)
    font = QFont()
    font.setFamily("Inter")
    font.setPointSize(10)
    app.setFont(font)
    palette = QPalette()
    palette.setColor(QPalette.Window, QColor(15, 15, 15))
    palette.setColor(QPalette.Base, QColor(35, 35, 35))
    palette.setColor(QPalette.AlternateBase, QColor(50, 50, 50))
    palette.setColor(QPalette.WindowText, Qt.white)
    palette.setColor(QPalette.Text, Qt.white)
    palette.setColor(QPalette.ButtonText, Qt.white)
    palette.setColor(QPalette.ToolTipText, Qt.white)
    palette.setColor(QPalette.Button, QColor(30, 30, 30))
    palette.setColor(QPalette.Highlight, QColor(106, 90, 205))
    palette.setColor(QPalette.HighlightedText, Qt.white)
    palette.setColor(QPalette.Link, QColor(106, 90, 205))
    palette.setColor(QPalette.BrightText, QColor(255, 204, 0))
    palette.setColor(QPalette.ToolTipBase, QColor(60, 60, 60))
    palette.setColor(QPalette.ToolTipText, Qt.white)
    app.setPalette(palette)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

