<?php
// /gallery.php

// --- 1. GESTIÓN DE SESIÓN Y DEPENDENCIAS ---
session_start();
if (!isset($_SESSION['user_id'])) {
    header('Location: index.php');
    exit;
}

require 'db.php';

$user_rol = $_SESSION['user_rol'];
$user_id = $_SESSION['user_id'];
if (!defined('MEDIA_PER_PAGE')) {
    define('MEDIA_PER_PAGE', $_SESSION['records_per_page'] ?? 8);
}

// --- OBTENER PREFERENCIA DE FILTRO Y VISTA DEL USUARIO ---
$filtro_defecto = 0;
$user_view_prefs_json = '{}'; // Por defecto, un JSON vacío
if (in_array($user_rol, ['superadmin', 'fisio'])) {
    // Leemos ambas columnas de preferencias
    $stmt_prefs = $db->prepare("SELECT filtro_personal_defecto, vista_defecto FROM cuentas WHERE id = ?");
    $stmt_prefs->execute([$user_id]);
    if ($user_prefs = $stmt_prefs->fetch(PDO::FETCH_ASSOC)) {
        $filtro_defecto = $user_prefs['filtro_personal_defecto'];
        // Aseguramos que pasamos un JSON válido a JS, incluso si es null en la BD
        $user_view_prefs_json = $user_prefs['vista_defecto'] ?? '{}';
    }
}

// --- FUNCIÓN AUXILIAR PARA OBTENER URL DE INCRUSTACIÓN ---
function getEmbedUrl($url) {
    $embedUrl = null;
    // --- MODIFICACIÓN: Eliminada la lógica de Vimeo ---
    if (preg_match('/(youtube\.com\/(watch\?v=|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9\-_]+)/', $url, $matches)) {
        $embedUrl = "https://www.youtube.com/embed/" . $matches[3];
    } else if (filter_var($url, FILTER_VALIDATE_URL)) {
       // Aceptar otras URLs directas
       $embedUrl = $url;
    }
    return $embedUrl;
}


// --- 2. GESTOR DE PETICIONS AJAX ---
if (isset($_REQUEST['ajax'])) {
    header('Content-Type: application/json');
    $action = $_REQUEST['action'] ?? 'read';
    $type = $_REQUEST['type'] ?? 'video';
    $table = ($type === 'video') ? 'videos' : 'images';
    $folder = ($type === 'video') ? 'videos/' : 'images/';
    $placeholder = ($type === 'video') ? 'placeholder.mp4' : 'images/placeholder.jpg'; // Corregido placeholder de imagen


    try {
        switch ($action) {
            case 'read':
                $page = (int)($_GET['page'] ?? 1);
                $search = $_GET['search'] ?? '';
                // --- MODIFICACIÓN 1 (PHP): Añadir 'uploader_name' a los sorts válidos ---
                $sort = in_array($_GET['sort'] ?? '', ['upload_date', 'title', 'uploader_name']) ? $_GET['sort'] : 'upload_date';
                $order = ($_GET['order'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
                $filterMine = ($_GET['filterMine'] ?? 'false') === 'true';

                $params = [':search' => '%' . $search . '%'];
                $joins = "JOIN cuentas c ON m.id_uploader = c.id";
                $whereClause = "WHERE (m.title LIKE :search OR m.description LIKE :search)";

                if ($filterMine && in_array($user_rol, ['superadmin', 'fisio'])) {
                    $whereClause .= " AND m.id_uploader = :user_id";
                    $params[':user_id'] = $user_id;
                }

                if (in_array($user_rol, ['superadmin', 'fisio'])) {
                    $whereClause .= " AND NOT EXISTS (SELECT 1 FROM archivado_personal ap WHERE ap.item_id = m.id AND ap.item_type = :type AND ap.fisio_id = :current_fisio_id)";
                    $params[':type'] = $type;
                    $params[':current_fisio_id'] = $user_id;
                }

                $totalQuery = "SELECT COUNT(m.id) FROM $table m $whereClause";
                $totalStmt = $db->prepare($totalQuery);
                $totalStmt->execute($params);
                $totalRecords = $totalStmt->fetchColumn();
                $totalPages = ceil($totalRecords / MEDIA_PER_PAGE);

                $offset = ($page - 1) * MEDIA_PER_PAGE;

                // --- MODIFICACIÓN 2 (PHP): Añadir 'uploader_name' al match de ordenación ---
                $orderByColumn = match ($sort) {
                    'title' => 'LOWER(m.title)',
                    'uploader_name' => 'LOWER(c.apellido)', // Ordenar por apellido
                    default => 'm.upload_date',
                };

                // --- MODIFICACIÓN 3 (PHP): Usar CONCAT para el nombre completo ---
                $dataQuery = "SELECT m.*, CONCAT(c.nombre, ' ', c.apellido) AS uploader_name FROM $table m $joins $whereClause ORDER BY $orderByColumn $order LIMIT :limit OFFSET :offset";

                $dataStmt = $db->prepare($dataQuery);
                foreach ($params as $key => &$val) $dataStmt->bindParam($key, $val);
                $dataStmt->bindValue(':limit', MEDIA_PER_PAGE, PDO::PARAM_INT);
                $dataStmt->bindValue(':offset', $offset, PDO::PARAM_INT);
                $dataStmt->execute();

                echo json_encode(['status' => 'success', 'media' => $dataStmt->fetchAll(), 'pagination' => ['currentPage' => $page, 'totalPages' => $totalPages, 'currentSort' => $sort, 'currentOrder' => $order]]);
                break;

            case 'upload':
            case 'update':
                // --- ESTA LÓGICA AHORA SOLO MANEJA URLs O EDICIÓN DE TEXTO ---
                if (!in_array($user_rol, ['superadmin', 'fisio'])) { throw new Exception('No tens permís per a pujar arxius.'); }
                $title = trim($_POST['title']);
                if (empty($title)) { throw new Exception("El títol és obligatori."); }

                // Comprobar si es un envío de archivo (legacy, ahora solo para imágenes) o URL
                $is_replacing_media = ($action === 'update') && (!empty($_POST['media_url']) || (isset($_FILES['media_file']) && $_FILES['media_file']['error'] === UPLOAD_ERR_OK));
                $is_uploading_new = ($action === 'upload') && (!empty($_POST['media_url']) || (isset($_FILES['media_file']) && $_FILES['media_file']['error'] === UPLOAD_ERR_OK));

                $current_filename = null;
                if ($is_uploading_new || $is_replacing_media) {
                    if ($type === 'video') {
                        // Solo procesar URL para vídeos aquí. La subida de archivos va por 'upload_chunk'
                        if (!empty($_POST['media_url'])) {
                            $current_filename = getEmbedUrl(trim($_POST['media_url']));
                            if (!$current_filename) { throw new Exception('La URL del vídeo no és vàlida o no és compatible.'); }
                        } else if ($action === 'upload') {
                            // Si es 'upload' (nuevo) y no es 'upload_chunk' Y no hay URL, es un error (o es una imagen)
                             if (!isset($_FILES['media_file'])) { // Solo para el caso de que no sea chunking
                                throw new Exception("Has de seleccionar un arxiu o introduir una URL.");
                             }
                        }
                    }

                    // Lógica para imágenes (sigue siendo subida simple)
                    if ($type === 'image') {
                        if (isset($_FILES['media_file']) && $_FILES['media_file']['error'] === UPLOAD_ERR_OK) {
                            $file = $_FILES['media_file'];
                            // --- ★ MODIFICACIÓN: Límite de imagen reducido a 3MB ---
                            if ($file['size'] > 3 * 1024 * 1024) { throw new Exception('La imatge és massa gran (màx. 3 MB).'); }
                            if (!in_array($file['type'], ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) { throw new Exception('Format d\'imatge no permès.'); }
                            $current_filename = time() . '_' . uniqid() . '_' . basename(preg_replace('/[^A-Za-z0-9.\-_]/', '', $file['name']));
                            if (!move_uploaded_file($file['tmp_name'], $folder . $current_filename)) { throw new Exception("Error en moure l'arxiu pujat."); }
                        } else { if($action === 'upload') throw new Exception("Has de seleccionar un arxiu d'imatge."); }
                    }
                }

                if ($action === 'upload') {
                    if ($current_filename === null) { throw new Exception("No s'ha proporcionat cap arxiu o URL."); }
                    $stmt = $db->prepare("INSERT INTO $table (filename, title, description, id_uploader) VALUES (?, ?, ?, ?)");
                    $stmt->execute([$current_filename, $title, $_POST['description'] ?? '', $user_id]);
                    $newMediaId = $db->lastInsertId();
                    echo json_encode(['status' => 'success', 'message' => ucfirst($type) . ' afegit correctament.', 'saved_media_id' => $newMediaId]);
                } else { // update
                    $id = (int)$_POST['id'];
                    $stmt_check = $db->prepare("SELECT id_uploader, filename FROM $table WHERE id = ?");
                    $stmt_check->execute([$id]);
                    $media_data = $stmt_check->fetch();
                    if (!$media_data || ($user_rol !== 'superadmin' && $media_data['id_uploader'] != $user_id)) {
                        throw new Exception(ucfirst($type) . " no trobat o sense permisos d'edició.");
                    }

                    if ($is_replacing_media && $current_filename !== null) { // Si SÍ se reemplaza (URL de vídeo o archivo de imagen)
                        $stmt = $db->prepare("UPDATE $table SET title = ?, description = ?, filename = ? WHERE id = ?");
                        $stmt->execute([$title, $_POST['description'] ?? '', $current_filename, $id]);

                        $old_filename = $media_data['filename'];
                        if (!filter_var($old_filename, FILTER_VALIDATE_URL) && file_exists($folder . $old_filename) && $old_filename !== $placeholder) {
                            unlink($folder . $old_filename);
                        }
                        echo json_encode(['status' => 'success', 'message' => ucfirst($type) . ' reemplaçat i dades actualitzades.', 'saved_media_id' => $id]);
                    } else { // Si NO se reemplaza (solo texto)
                        $stmt = $db->prepare("UPDATE $table SET title = ?, description = ? WHERE id = ?");
                        $stmt->execute([$title, $_POST['description'] ?? '', $id]);
                        echo json_encode(['status' => 'success', 'message' => 'Dades actualitzades.', 'saved_media_id' => $id]);
                    }
                }
                break;

            case 'get':
                $id = (int)$_GET['id'];
                $stmt = $db->prepare("SELECT id, title, description, filename, id_uploader, upload_date FROM $table WHERE id = ?");
                $stmt->execute([$id]);
                $media = $stmt->fetch();
                if (!$media) { throw new Exception(ucfirst($type) . " no trobat."); }
                echo json_encode(['status' => 'success', 'media' => $media]);
                break;

                case 'personal_archive':
                                if (!in_array($user_rol, ['superadmin', 'fisio'])) { throw new Exception('Accés denegat.'); }
                                $archive_type = $_POST['type'];
                                if (!in_array($archive_type, ['video', 'image'])) { throw new Exception('Tipus d\'arxiu no vàlid.'); }
                                // --- CORRECCIÓN MYSQL ---
                                $stmt = $db->prepare("INSERT IGNORE INTO archivado_personal (fisio_id, item_id, item_type) VALUES (?, ?, ?)");
                                // --- FIN CORRECCIÓN ---
                                $stmt->execute([$user_id, (int)$_POST['id'], $archive_type]);
                                echo json_encode(['status' => 'success', 'message' => ucfirst($archive_type) . ' arxivat correctament a la teua vista.']);
                                break;

            case 'check_media_usage':
                $id = (int)$_GET['id'];
                $usage_col = ($type === 'video') ? 'id_video' : 'id_image';
                $stmt = $db->prepare("SELECT title FROM ejercicios WHERE $usage_col = :id");
                $stmt->execute([':id' => $id]);
                $usage = $stmt->fetchAll(PDO::FETCH_COLUMN);
                echo json_encode(['is_usable_for_action' => (count($usage) === 0), 'usage' => $usage]);
                break;

            case 'delete':
                $id = (int)$_POST['id'];

                $usage_col = ($type === 'video') ? 'id_video' : 'id_image';
                $usageStmt = $db->prepare("SELECT COUNT(id) FROM ejercicios WHERE $usage_col = ?");
                $usageStmt->execute([$id]);
                if ($usageStmt->fetchColumn() > 0 && $user_rol !== 'superadmin') {
                    throw new Exception("Aquest arxiu està en ús en un o més exercicis i no pot ser eliminat.");
                }

                $stmt_media = $db->prepare("SELECT filename, id_uploader FROM $table WHERE id = ?");
                $stmt_media->execute([$id]);
                $media = $stmt_media->fetch();

                if (!$media) {
                    throw new Exception('El medi a eliminar no existeix.');
                }

                if ($user_rol === 'fisio' && $media['id_uploader'] != $user_id) {
                    throw new Exception('No tens permís per a eliminar aquest medi.');
                }

                $deleteStmt = $db->prepare("DELETE FROM $table WHERE id = ?");
                $deleteStmt->execute([$id]);

                if ($deleteStmt->rowCount() > 0) {
                    $filepath = ($type === 'video') ? 'videos/' . $media['filename'] : 'images/' . $media['filename'];
                    if (!filter_var($media['filename'], FILTER_VALIDATE_URL) && file_exists($filepath) && $media['filename'] !== $placeholder) {
                        unlink($filepath);
                    }
                }

                echo json_encode(['status' => 'success', 'message' => ucfirst($type) . ' eliminat permanentment.']);
                break;

            // --- INICIO DE NUEVOS CASES PARA SUBIDA POR TROZOS ---
            case 'upload_chunk':
                if (!in_array($user_rol, ['superadmin', 'fisio'])) {
                    throw new Exception('No tens permís per a pujar arxius.');
                }

                $upload_id = $_POST['upload_id'] ?? null;
                $chunk_index = (int)($_POST['current_chunk_index'] ?? -1);

                if (empty($upload_id) || $chunk_index < 0 || !isset($_FILES['media_chunk'])) {
                    throw new Exception('Dades del tros (chunk) invàlides.');
                }

                // Validar el upload_id para evitar "directory traversal"
                if (!preg_match('/^[a-zA-Z0-9-.]+$/', $upload_id)) {
                     throw new Exception('ID de pujada no vàlid.');
                }

                // Crear directorio temporal si no existe
                $temp_dir = 'uploads_temp/';
                if (!is_dir($temp_dir)) {
                    if (!mkdir($temp_dir, 0777, true)) {
                        throw new Exception("No s'ha pogut crear el directori temporal general.");
                    }
                }

                $temp_dir_upload = $temp_dir . $upload_id;
                if (!is_dir($temp_dir_upload)) {
                    if (!mkdir($temp_dir_upload, 0777, true)) {
                        throw new Exception("No s'ha pogut crear el directori temporal de la pujada.");
                    }
                }

                // Mover el trozo a su ubicación temporal
                $chunk_path = $temp_dir_upload . '/' . $chunk_index . '.chunk';
                if (!move_uploaded_file($_FILES['media_chunk']['tmp_name'], $chunk_path)) {
                    throw new Exception("Error en moure el tros (chunk) pujat.");
                }

                echo json_encode(['status' => 'chunk_uploaded', 'chunk_index' => $chunk_index]);
                break;

            case 'assemble_chunks':
                if (!in_array($user_rol, ['superadmin', 'fisio'])) {
                    throw new Exception('No tens permís per a pujar arxius.');
                }

                $upload_id = $_POST['upload_id'] ?? null;
                $title = trim($_POST['title']);
                $description = trim($_POST['description'] ?? '');
                $type = $_POST['type'] ?? 'video'; // 'video' o 'image'
                $original_filename = $_POST['original_filename'] ?? 'uploaded_file'; // Para la extensión

                if (empty($upload_id) || empty($title) || !in_array($type, ['video', 'image'])) {
                     throw new Exception('Dades finals invàlides.');
                }
                if (!preg_match('/^[a-zA-Z0-9-.]+$/', $upload_id)) {
                     throw new Exception('ID de pujada no vàlid.');
                }

                $temp_dir = 'uploads_temp/' . $upload_id;
                $table = ($type === 'video') ? 'videos' : 'images';
                $folder = ($type === 'video') ? 'videos/' : 'images/';

                if (!is_dir($temp_dir)) {
                    throw new Exception('Directori de pujada no trobat. La pujada ha fallat.');
                }

                // Contar los trozos
                $files = scandir($temp_dir);
                $total_chunks = 0;
                foreach ($files as $file) {
                    if (strpos($file, '.chunk') !== false) {
                        $total_chunks++;
                    }
                }
                if ($total_chunks === 0) {
                     throw new Exception('No s\'han trobat trossos (chunks) per a ensamblar.');
                }

                // Generar nombre final único (basado en el original para la extensión)
                $extension = pathinfo($original_filename, PATHINFO_EXTENSION);
                $safe_extension = preg_replace('/[^A-Za-z0-9]/', '', $extension); // Limpiar la extensión
                $final_filename = $upload_id . '.' . $safe_extension;
                $final_path = $folder . $final_filename;

                // Ensamblar el archivo
                $fp = fopen($final_path, 'wb');
                if (!$fp) {
                    throw new Exception("No s'ha pogut obrir l'arxiu final per a escriptura.");
                }

                for ($i = 0; $i < $total_chunks; $i++) {
                    $chunk_path = $temp_dir . '/' . $i . '.chunk';
                    $chunk_content = file_get_contents($chunk_path);
                    if ($chunk_content === false) {
                        fclose($fp);
                        throw new Exception("No s'ha pogut llegir el tros $i.");
                    }
                    fwrite($fp, $chunk_content);
                    unlink($chunk_path); // Limpiar el trozo
                }
                fclose($fp);
                rmdir($temp_dir); // Limpiar el directorio temporal

                // --- ★ INICIO DE LA CORRECCIÓN (Lógica de Actualizar vs. Insertar) ★ ---
                $media_id = (int)($_POST['media_id'] ?? 0);

                if ($media_id > 0) {
                    // --- ES UNA ACTUALIZACIÓN ---

                    // 1. Verificar permisos y obtener nombre de archivo antiguo
                    $stmt_check = $db->prepare("SELECT id_uploader, filename FROM $table WHERE id = ?");
                    $stmt_check->execute([$media_id]);
                    $media_data = $stmt_check->fetch();
                    if (!$media_data || ($user_rol !== 'superadmin' && $media_data['id_uploader'] != $user_id)) {
                        // Si no se encuentra o no hay permisos, borra el archivo subido y lanza error
                        @unlink($final_path);
                        throw new Exception(ucfirst($type) . " no trobat o sense permisos d'edició.");
                    }

                    // 2. Actualizar la base de datos
                    $stmt = $db->prepare("UPDATE $table SET title = ?, description = ?, filename = ? WHERE id = ?");
                    $stmt->execute([$title, $description, $final_filename, $media_id]);

                    // 3. Borrar el archivo antiguo si existe y no es una URL
                    $old_filename = $media_data['filename'];
                    $placeholder = ($type === 'video') ? 'placeholder.mp4' : 'images/placeholder.jpg';
                    $old_filepath = $folder . $old_filename;
                    if (!filter_var($old_filename, FILTER_VALIDATE_URL) && file_exists($old_filepath) && $old_filename !== $placeholder) {
                        @unlink($old_filepath);
                    }

                    echo json_encode(['status' => 'success', 'message' => ucfirst($type) . ' reemplaçat i dades actualitzades.', 'saved_media_id' => $media_id]);

                } else {
                    // --- ES UNA CREACIÓN NUEVA (Lógica original) ---
                    $stmt = $db->prepare("INSERT INTO $table (filename, title, description, id_uploader) VALUES (?, ?, ?, ?)");
                    $stmt->execute([$final_filename, $title, $description, $user_id]);
                    $newMediaId = $db->lastInsertId();

                    echo json_encode(['status' => 'success', 'message' => ucfirst($type) . ' pujat i ensamblat correctament.', 'saved_media_id' => $newMediaId]);
                }
                // --- ★ FIN DE LA CORRECCIÓN ★ ---
                break;
            // --- FIN DE NUEVOS CASES ---

        }
    } catch (Exception $e) {
        http_response_code(400);
        echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
    }
    exit;
}

$page_title = "Galeria de Medis";
include 'partials/header.php';
?>
<style>
    .sortable-header {
        cursor: pointer;
        user-select: none;
    }
    .sortable-header .bi-arrow-down-up {
        opacity: 0.4;
    }
    .list-thumbnail {
        width: 120px;
        height: 67px;
        object-fit: cover;
        border-radius: 0.25rem;
    }
    .dropdown-item i.bi { margin-right: 0.5rem; }
    .dropdown .btn.dropdown-toggle:hover {
        color: #FFF !important;
    }

    /* REGLA CLAVE para los menús desplegables */
    td .dropdown, .card-footer .dropdown {
        position: static;
    }

    /* --- ESTILOS PARA CHUNKING --- */
    .border-dashed {
        border-style: dashed !important;
    }
    .progress-bar-animated {
        animation: progress-bar-stripes 1s linear infinite;
    }
    @keyframes progress-bar-stripes {
        from { background-position-x: 1rem; }
        to { background-position-x: 0; }
    }

    /* ★★★ INICIO DE LA MODIFICACIÓN PARA MÓVIL ★★★ */
    @media (max-width: 767.98px) {
        /* Ocultamos los botones de cambiar vista (Grid/List) en móvil */
        .toggle-view-btn {
            display: none !important;
        }
    }
    /* ★★★ FIN DE LA MODIFICACIÓN PARA MÓVIL ★★★ */
</style>
<main class="main-content container mt-4" style="max-width: 1420px;">

    <!-- ★★★ MODIFICACIÓN: Añadida clase 'gallery-header-bar' para CSS móvil ★★★ -->
    <div class="d-flex justify-content-between align-items-center mb-3 gallery-header-bar">
        <h4><i class="bi bi-collection-play me-2"></i> Galeria de Medis</h4>
        <?php if (in_array($user_rol, ['superadmin', 'fisio'])): ?>
        <div>
            <button class="btn btn-primary" id="addVideoBtn"><i class="bi bi-plus-circle me-2"></i>Afegir Vídeo</button>
            <button class="btn btn-primary d-none" id="addImageBtn"><i class="bi bi-plus-circle me-2"></i>Afegir Imatge</button>
        </div>
        <?php endif; ?>
    </div>

    <div class="card shadow-sm">
        <div class="card-body">
            <ul class="nav nav-tabs mb-3" id="mediaTabs" role="tablist">
                <li class="nav-item" role="presentation"><button class="nav-link active" id="videos-tab" data-bs-toggle="tab" data-bs-target="#videos-panel" type="button"><i class="bi bi-film me-2"></i>Vídeos</button></li>
                <li class="nav-item" role="presentation"><button class="nav-link" id="images-tab" data-bs-toggle="tab" data-bs-target="#images-panel" type="button"><i class="bi bi-image me-2"></i>Imatges</button></li>
            </ul>

            <div class="tab-content" id="mediaTabsContent">
                <div class="tab-pane fade show active" id="videos-panel" role="tabpanel">

                    <!-- ★★★ INICIO: Bloque de Controles Responsive (Móvil) ★★★ -->
                    <div class="row g-2 mb-3">

                        <!-- Columna 1 (Móvil): Switch -->
                        <div class="col-12 col-md-auto">
                            <div class="d-flex align-items-center h-100">
                                <?php if (in_array($user_rol, ['superadmin', 'fisio'])): ?>
                                <div class="form-check form-switch">
                                    <input class="form-check-input" type="checkbox" role="switch" id="filterMyVideos">
                                    <label class="form-check-label" for="filterMyVideos">Només els meus</label>
                                </div>
                                <?php endif; ?>
                            </div>
                        </div>

                        <!-- Columna 2 (Móvil): Buscador y Botones -->
                        <div class="col-12 col-md">
                            <div class="d-flex align-items-center justify-content-start justify-content-md-end flex-wrap gap-2">

                                <!-- Search -->
                                <div class="input-group mobile-filter-full-width" style="max-width: 400px; position: relative;">
                                    <span class="input-group-text"><i class="bi bi-search"></i></span>
                                    <input type="text" id="videoSearchInput" class="form-control" placeholder="Cercar vídeos...">
                                    <button class="clear-search-btn" type="button" id="clearVideoSearchBtn"><i class="bi bi-x"></i></button>
                                </div>

                                <!-- View Buttons -->
                                <div class="btn-group mobile-filter-btn-group" role="group">
                                    <button type="button" class="btn btn-outline-secondary toggle-view-btn active" data-type="video" id="videoGridViewBtn" title="Vista en Quadrícula"><i class="bi bi-grid-fill"></i></button>
                                    <button type="button" class="btn btn-outline-secondary toggle-view-btn" data-type="video" id="videoListViewBtn" title="Vista en Llista"><i class="bi bi-list"></i></button>
                                </div>

                                <!-- Sort Buttons -->
                                <div class="btn-group mobile-filter-btn-group" id="video-sort-buttons-group">
                                    <button class="btn btn-outline-secondary sort-btn active" data-type="video" data-sort="upload_date" data-order="DESC" title="Ordenar per Data">
                                        <i class="bi bi-calendar3"></i>
                                    </button>
                                    <button class="btn btn-outline-secondary sort-btn" data-type="video" data-sort="title" data-order="ASC" title="Ordenar per Títol">
                                        <i class="bi bi-sort-alpha-down"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                    <!-- ★★★ FIN: Bloque de Controles Responsive ★★★ -->

                    <div id="video-gallery-container" class="row g-4"></div>
                    <div id="video-pagination-container" class="mt-4"></div>
                </div>

                <div class="tab-pane fade" id="images-panel" role="tabpanel">

                    <!-- ★★★ INICIO: Bloque de Controles Responsive (Móvil) ★★★ -->
                    <div class="row g-2 mb-3">

                        <!-- Columna 1 (Móvil): Switch -->
                        <div class="col-12 col-md-auto">
                             <div class="d-flex align-items-center h-100">
                                <?php if (in_array($user_rol, ['superadmin', 'fisio'])): ?>
                                <div class="form-check form-switch">
                                    <input class="form-check-input" type="checkbox" role="switch" id="filterMyImages">
                                    <label class="form-check-label" for="filterMyImages">Només les meues</label>
                                </div>
                                <?php endif; ?>
                            </div>
                        </div>

                        <!-- Columna 2 (Móvil): Buscador y Botones -->
                        <div class="col-12 col-md">
                            <div class="d-flex align-items-center justify-content-start justify-content-md-end flex-wrap gap-2">

                                <!-- Search -->
                                <div class="input-group mobile-filter-full-width" style="max-width: 400px; position: relative;">
                                    <span class="input-group-text"><i class="bi bi-search"></i></span>
                                    <input type="text" id="imageSearchInput" class="form-control" placeholder="Cercar imatges...">
                                    <button class="clear-search-btn" type="button" id="clearImageSearchBtn"><i class="bi bi-x"></i></button>
                                </div>

                                <!-- View Buttons -->
                                <div class="btn-group mobile-filter-btn-group" role="group">
                                    <button type="button" class="btn btn-outline-secondary toggle-view-btn active" data-type="image" id="imageGridViewBtn" title="Vista en Quadrícula"><i class="bi bi-grid-fill"></i></button>
                                    <button type="button" class="btn btn-outline-secondary toggle-view-btn" data-type="image" id="imageListViewBtn" title="Vista en Llista"><i class="bi bi-list"></i></button>
                                </div>

                                <!-- Sort Buttons -->
                                <div class="btn-group mobile-filter-btn-group" id="image-sort-buttons-group">
                                    <button class="btn btn-outline-secondary sort-btn active" data-type="image" data-sort="upload_date" data-order="DESC" title="Ordenar per Data">
                                        <i class="bi bi-calendar3"></i>
                                    </button>
                                    <button class="btn btn-outline-secondary sort-btn" data-type="image" data-sort="title" data-order="ASC" title="Ordenar per Títol">
                                        <i class="bi bi-sort-alpha-down"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                    <!-- ★★★ FIN: Bloque de Controles Responsive ★★★ -->

                    <div id="image-gallery-container" class="row g-4"></div>
                    <div id="image-pagination-container" class="mt-4"></div>
                </div>
            </div>
        </div>
    </div>
</main>

<div class="modal fade" id="mediaModal" tabindex="-1">
    <div class="modal-dialog modal-lg"><div class="modal-content">
        <div class="modal-header"><h5 class="modal-title" id="mediaModalLabel"></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body">
            <form id="mediaForm" enctype="multipart/form-data">
                <input type="hidden" id="mediaId" name="id">
                <input type="hidden" id="mediaType" name="type">

                <div class="mb-3"><label for="mediaTitle" class="form-label">Títol <span class="text-danger">*</span></label><input type="text" class="form-control" id="mediaTitle" name="title" required></div>
                <div class="mb-3"><label for="mediaDescription" class="form-label">Descripció</label><textarea class="form-control" id="mediaDescription" name="description" rows="3"></textarea></div>

                <div id="edit-mode-notice" class="alert alert-info d-none">Estàs editant les dades. Pots reemplaçar l'arxiu opcionalment.</div>

                <div id="upload-content">
                    <ul class="nav nav-tabs" id="addMediaTabs" role="tablist">
                        <li class="nav-item" role="presentation"><button class="nav-link active" id="upload-file-tab" data-bs-toggle="tab" data-bs-target="#upload-file-panel" type="button">Pujar Arxiu</button></li>
                        <li class="nav-item" role="presentation" id="url-tab-item"><button class="nav-link" id="add-url-tab" data-bs-toggle="tab" data-bs-target="#add-url-panel" type="button">Afegir per URL</button></li>
                    </ul>
                    <div class="tab-content pt-3">
                        <div class="tab-pane fade show active" id="upload-file-panel" role="tabpanel">
                            <div id="drop-area" class="text-center p-5 border border-2 border-dashed rounded mb-3" style="cursor: pointer;"><div id="drop-area-text"></div></div>

                            <div id="video-upload-recommendation" class="alert alert-info small mt-3" style="display: none;">
                                <i class="bi bi-info-circle-fill me-2"></i>
                                <strong>Recomanació:</strong> Es recomana encaridament pujar vídeos a un servei extern (com YouTube en mode "No llistat") i enganxar la URL a la pestanya "Afegir per URL". Això garanteix una càrrega més ràpida per al pacient.
                            </div>
                            <input type="file" id="mediaFile" name="media_file" class="d-none">
                            <div id="file-info" class="alert alert-info d-none"></div>
                        </div>
                        <div class="tab-pane fade" id="add-url-panel" role="tabpanel">
                            <div class="mb-3"><label for="mediaUrl" class="form-label">URL del Vídeo</label><input type="url" class="form-control" id="mediaUrl" name="media_url" placeholder="https://www.youtube.com/watch?v=..."><div class="form-text">Compatible amb YouTube o enllaç directe a .mp4, .webm, etc.</div></div>

                            <div id="url-preview-container" class="mb-3" style="display: none;">
                                <label class="form-label">Previsualització:</label>
                                <div id="url-preview-content" class="ratio ratio-16x9" style="max-height: 200px; border-radius: .25rem; overflow: hidden; background: #eee;">
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div id="upload-progress-section" class="d-none mt-3">
                    <p class="mb-1 small">Pujant arxiu... <span id="chunk-counter"></span></p>

                    <div class="progress mb-2" style="height: 20px;">
                        <div id="total-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
                            <span id="total-progress-text">0%</span>
                        </div>
                    </div>

                    <p class="mb-1 small text-muted">Progreso del trozo actual:</p>
                    <div class="progress" style="height: 5px;">
                        <div id="chunk-progress-bar" class="progress-bar bg-info" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
                    </div>
                </div>
                <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancel-upload-btn">Cancel·lar</button><button type="submit" class="btn btn-primary" id="submit-media-btn">Guardar</button></div>
            </form>
        </div>
    </div></div>
</div>

<div class="modal fade" id="manageModal" tabindex="-1">
    <div class="modal-dialog"><div class="modal-content">
        <div class="modal-header"><h5 class="modal-title" id="manageModalLabel"></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body" id="manageModalBody"></div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel·lar</button>
            <button type="button" class="btn btn-warning" id="archiveBtn">Arxivar</button>
            <button type="button" class="btn btn-danger" id="deleteBtn">Eliminar</button>
        </div>
    </div></div>
</div>

<div class="modal fade" id="viewMediaModal" tabindex="-1">
    <div class="modal-dialog modal-lg"><div class="modal-content">
        <div class="modal-header"><h5 class="modal-title" id="viewMediaModalLabel"></h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
        <div class="modal-body" id="viewMediaModalBody"></div>
        <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Tancar</button></div>
    </div></div>
</div>

<?php include 'partials/footer.php'; ?>

  <script>
    const USER_VIEW_PREFERENCES = <?php echo $user_view_prefs_json; ?>;
</script>
<script>
$(document).ready(function() {
    const IS_SUPERADMIN = '<?= $user_rol ?>' === 'superadmin';
    const CURRENT_USER_ID = <?= $user_id ?>;
    const FILTER_MINE_DEFAULT = <?= $filtro_defecto ?> == 1;
    const mediaModal = new bootstrap.Modal('#mediaModal');
    const manageModal = new bootstrap.Modal('#manageModal');
    const viewMediaModal = new bootstrap.Modal('#viewMediaModal');

    function isMobileView() {
        return window.innerWidth < 767.98;
    }

    let itemToManage = { id: null, name: '', type: '', ownerId: null };
    let currentPreviewUrl = null; // Para previsualización de archivos subidos

    // --- INICIO VARIABLES PARA SUBIDA POR TROZOS ---
    const CHUNK_SIZE = 2 * 1024 * 1024; // 2MB
    let currentUploadId = null;
    let currentFile = null; // Aquí guardaremos el archivo seleccionado
    // --- FIN VARIABLES PARA SUBIDA POR TROZOS ---


    // --- *** INICIO CORRECCIÓN 1: Añadir función getImagePath *** ---
    function getImagePath(filename) {
        if (!filename) return '';
        const systemImages = ['logo.jpg', 'logo2.jpg', 'img_defecte.jpg'];
        const folder = systemImages.includes(filename) ? 'logos/' : 'images/';
        return `${folder}${filename}`;
    }
    // --- *** FIN CORRECCIÓN 1 *** ---

    const initialVideoView = USER_VIEW_PREFERENCES['gallery_video'] || 'grid';
    const initialImageView = USER_VIEW_PREFERENCES['gallery_image'] || 'grid';

// ★★★ MODIFICACIÓN AQUÍ: Usamos isMobileView() ★★★
let videoState = {
    page: 1,
    search: '',
    sort: 'upload_date',
    order: 'DESC',
    filterMine: FILTER_MINE_DEFAULT,
    viewMode: isMobileView() ? 'grid' : initialVideoView // <-- Forzar grid en móvil
};

let imageState = {
    page: 1,
    search: '',
    sort: 'upload_date',
    order: 'DESC',
    filterMine: FILTER_MINE_DEFAULT,
    viewMode: isMobileView() ? 'grid' : initialImageView // <-- Forzar grid en móvil
};
// ★★★ FIN MODIFICACIÓN ★★★

    // --- MODIFICACIÓN 4 (JS): Actualizar getInitialSortIcon ---
    function getInitialSortIcon(currentSort, currentOrder, columnName) {
        if (currentSort === columnName) {
            return currentOrder === 'ASC' ? '<i class="bi bi-caret-up-fill ms-2"></i>' : '<i class="bi bi-caret-down-fill ms-2"></i>';
        }
        const validCols = ['upload_date', 'title', 'uploader_name'];
        if (!validCols.includes(columnName)) return '';
        return '<i class="bi bi-arrow-down-up ms-2 text-muted opacity-50"></i>';
    }

    // --- ★ TAREA 3 (Spinner Suave) ---
    function fetchMedia(type = 'video') {
        const state = type === 'video' ? videoState : imageState;
        const containerId = `#${type}-gallery-container`;
        // ATENUAMOS la lista, no la borramos
        $(containerId).css('opacity', 0.5);

        return $.getJSON('gallery.php', { ajax: true, action: 'read', type, ...state })
        .done(res => {
            $(containerId).css('opacity', 1); // RESTAURAMOS
            if (res.status === 'success') {
                if (type === 'video') renderVideoGallery(res.media, res.pagination);
                else renderImageGallery(res.media, res.pagination);
                renderPagination(res.pagination, type);
            }
        })
        .fail(() => $(containerId).css('opacity', 1).html(`<div class="alert alert-danger">Error en carregar la galeria.</div>`));
    }
    // --- ★ FIN TAREA 3 ---

    function renderVideoGallery(videos, pagination) {
            const container = $('#video-gallery-container');
            const { currentSort, currentOrder } = pagination;
            container.empty();
            if (videos.length === 0) { container.html('<div class="col-12"><div class="alert alert-secondary text-center">No s\'han trobat vídeos.</div></div>'); return; }

            updateSortButtons('video', currentSort, currentOrder);

            if (videoState.viewMode === 'grid') {
                container.removeClass('table-responsive').addClass('row g-4');
                videos.forEach(video => {
                    const isEmbed = video.filename.startsWith('http');
                    const videoSrc = isEmbed ? video.filename : `videos/${video.filename}`;
                    // Para la vista en rejilla, mantenemos el player de vídeo/iframe para la previsualización directa
                    const playerHtml = isEmbed ? `<iframe src="${videoSrc}" frameborder="0" allowfullscreen></iframe>` : `<video oncontextmenu="return false;" controlsList="nodownload" preload="metadata" src="${videoSrc}#t=0.5" class="w-100 h-100"></video>`;
                    container.append(renderMediaCard('video', video, playerHtml));
                });
            } else {
                container.removeClass('row g-4').addClass('table-responsive');
                let tableHtml = `<table class="table table-hover align-middle video-list-view">
                    <thead>
                        <tr>
                            <th style="width: 150px;">Recurs</th>
                            <th class="sortable-header" data-sort="title">Títol${getInitialSortIcon(currentSort, currentOrder, 'title')}</th>
                            <th class="sortable-header" data-sort="uploader_name">Pujat per${getInitialSortIcon(currentSort, currentOrder, 'uploader_name')}</th>
                            <th class="sortable-header" data-sort="upload_date">Data${getInitialSortIcon(currentSort, currentOrder, 'upload_date')}</th>
                            <th class="text-end">Accions</th>
                        </tr>
                    </thead>
                    <tbody>`;
                videos.forEach(video => {
                    const isEmbed = video.filename.startsWith('http');
                    let thumbnailHtml;
                    if (isEmbed) {
                        // *** CORRECCIÓN CLAVE: Usamos la URL de embed que viene de PHP ***
                        const ytMatch = video.filename.match(/youtube\.com\/embed\/([a-zA-Z0-9\-_]+)/);
                        const videoId = ytMatch ? ytMatch[1] : null;

                        if (videoId) {
                             // Usar la miniatura de YouTube (mqdefault)
                             thumbnailHtml = `<img src="https://img.youtube.com/vi/${videoId}/mqdefault.jpg" class="list-thumbnail" alt="Miniatura Vídeo">`;
                        } else {
                            // ★ CORRECCIÓN: Si es URL externa (MP4), usamos etiqueta VIDEO como miniatura
                            thumbnailHtml = `<video src="${video.filename}#t=0.5" class="list-thumbnail" preload="metadata"></video>`;
                        }
                    } else {
                        // Vídeo local (El navegador suele mostrar el primer frame o un placeholder)
                        thumbnailHtml = `<video src="videos/${video.filename}#t=0.5" class="list-thumbnail"></video>`;
                    }

                    const uploadDate = new Date(video.upload_date).toLocaleDateString('ca-ES');
                    tableHtml += `<tr>
                        <td>${thumbnailHtml}</td>
                        <td><strong>${video.title}</strong><br><small class="text-muted">${video.description || ''}</small></td>
                        <td>${video.uploader_name}</td>
                        <td>${uploadDate}</td>
                        <td class="text-end">${renderCardButtons('video', video)}</td>
                    </tr>`;
                });
                container.html(tableHtml + '</tbody></table>');
            }
        }

    function renderImageGallery(images, pagination) {
        const container = $('#image-gallery-container');
        const { currentSort, currentOrder } = pagination;
        container.empty();
        if (images.length === 0) { container.html('<div class="col-12"><div class="alert alert-secondary text-center">No s\'han trobat imatges.</div></div>'); return; }

        updateSortButtons('image', currentSort, currentOrder);

        if (imageState.viewMode === 'grid') {
            container.removeClass('table-responsive').addClass('row g-4');
            images.forEach(image => {
                const playerHtml = `<img src="${getImagePath(image.filename)}" class="w-100 h-100" style="object-fit: cover;" alt="${image.title}">`;
                container.append(renderMediaCard('image', image, playerHtml));
            });
        } else {
            container.removeClass('row g-4').addClass('table-responsive');
            // --- MODIFICACIÓN 6 (JS): Añadir sortable header a 'Pujat per' ---
            let tableHtml = `<table class="table table-hover align-middle image-list-view">
                <thead>
                    <tr>
                        <th style="width: 150px;">Miniatura</th>
                        <th class="sortable-header" data-sort="title">Títol${getInitialSortIcon(currentSort, currentOrder, 'title')}</th>
                        <th class="sortable-header" data-sort="uploader_name">Pujat per${getInitialSortIcon(currentSort, currentOrder, 'uploader_name')}</th>
                        <th class="sortable-header" data-sort="upload_date">Data${getInitialSortIcon(currentSort, currentOrder, 'upload_date')}</th>
                        <th class="text-end">Accions</th>
                    </tr>
                </thead>
                <tbody>`;
            images.forEach(image => {
                const thumbnailHtml = `<img src="${getImagePath(image.filename)}" class="list-thumbnail">`;
                const uploadDate = new Date(image.upload_date).toLocaleDateString('ca-ES');
                tableHtml += `<tr>
                    <td>${thumbnailHtml}</td>
                    <td><strong>${image.title}</strong><br><small class="text-muted">${image.description || ''}</small></td>
                    <td>${image.uploader_name}</td>
                    <td>${uploadDate}</td>
                    <td class="text-end">${renderCardButtons('image', image)}</td>
                </tr>`;
            });
            container.html(tableHtml + '</tbody></table>');
        }
    }

    // --- ★ TAREA 2 (Botones UI) ---
    function updateSortButtons(type, currentSort, currentOrder) {
        const state = type === 'video' ? videoState : imageState;
        const groupSelector = `#${type}-sort-buttons-group`; // Usar el ID
        $(groupSelector + ' .sort-btn').removeClass('active');

        if (state.viewMode === 'grid') { // Solo mostrar activo en Grid
             const buttonToActivate = $(groupSelector).find(`.sort-btn[data-sort="${currentSort}"]`);
             if (buttonToActivate.length > 0) {
                 buttonToActivate.addClass('active');
             }
        }
        // Si es 'list', no se activa nada
    }
    // --- ★ FIN TAREA 2 ---

    function renderCardButtons(type, media) {
        const canModify = IS_SUPERADMIN || media.id_uploader == CURRENT_USER_ID;
        const manage_data = `data-type="${type}" data-id="${media.id}" data-name="${escapeHtml(media.title)}" data-owner-id="${media.id_uploader}"`;

        let editButtonHtml = '';
        if (canModify) {
            editButtonHtml = `<li><a class="dropdown-item edit-media-btn" href="#" data-type="${type}" data-id="${media.id}"><i class="bi bi-pencil"></i> Editar</a></li>`;
        }

        return `
            <div class="dropdown">
                <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="Gestionar Arxiu">
                    <i class="bi bi-gear-fill"></i>
                </button>
                <ul class="dropdown-menu dropdown-menu-end">
                    <li><a class="dropdown-item view-media-btn" href="#" data-type="${type}" data-id="${media.id}"><i class="bi bi-eye"></i> Veure</a></li>
                    ${editButtonHtml}
                    <li><hr class="dropdown-divider"></li>
                    <li><a class="dropdown-item manage-media-btn" href="#" ${manage_data}><i class="bi bi-shield-slash"></i> Arxivar / Eliminar</a></li>
                </ul>
            </div>
        `;
    }

    function renderMediaCard(type, media, playerHtml) {
        return `<div class="col-lg-3 col-md-6"><div class="card h-100 shadow-sm">
            <div class="card-img-top ratio ratio-16x9 bg-dark">${playerHtml}</div>
            <div class="card-body d-flex flex-column">
                <h5 class="card-title">${escapeHtml(media.title)}</h5>
                <p class="card-text small text-muted flex-grow-1">${escapeHtml(media.description) || ''}</p>
            </div>
            <div class="card-footer d-flex justify-content-between align-items-center">
                <small class="text-muted">Pujat per: ${escapeHtml(media.uploader_name)}</small>
                ${renderCardButtons(type, media)}
            </div>
        </div></div>`;
    }

    function renderPagination(pagination, type) {
        const container = $(`#${type}-pagination-container`);
        container.empty();
        if (pagination.totalPages <= 1) return;
        let paginationHtml = `<nav><ul class="pagination justify-content-center">`;
        for (let i = 1; i <= pagination.totalPages; i++) {
            paginationHtml += `<li class="page-item ${i === pagination.currentPage ? 'active' : ''}"><a class="page-link" href="#" data-page="${i}" data-type="${type}">${i}</a></li>`;
        }
        container.html(paginationHtml + '</ul></nav>');
    }

    function setupSearch(inputId, clearBtnId, type) {
        let searchTimeout;
        $(inputId).on('keyup', function() {
            $(clearBtnId).toggle($(this).val().length > 0);
            clearTimeout(searchTimeout);
            searchTimeout = setTimeout(() => {
                const state = type === 'video' ? videoState : imageState;
                state.search = $(this).val();
                state.page = 1;
                fetchMedia(type);
            }, 300);
        });
        $(clearBtnId).on('click', () => $(inputId).val('').trigger('keyup').focus());
    }

    setupSearch('#videoSearchInput', '#clearVideoSearchBtn', 'video');
    setupSearch('#imageSearchInput', '#clearImageSearchBtn', 'image');

    // --- *** INICIO CORRECCIÓN 2: Nueva función showUrlPreview *** ---
    /**
     * Muestra una previsualización de iframe para URLs de YouTube/Vimeo.
     * @param {string} url - La URL completa pegada por el usuario.
     */
    function showUrlPreview(url) {
        const container = $('#url-preview-container');
        const content = $('#url-preview-content');
        content.empty(); // Limpiar preview anterior

        if (!url) {
            container.hide();
            return;
        }

        let embedUrl = null;
        // Intenta hacer match con YouTube (incluye youtu.be)
        const ytMatch = url.match(/(youtube\.com\/(watch\?v=|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9\-_]+)/);
        // --- MODIFICACIÓN: Eliminada la lógica de Vimeo ---

        if (ytMatch && ytMatch[3]) {
            embedUrl = `https://www.youtube.com/embed/${ytMatch[3]}`;
        }

        if (embedUrl) {
            // Si es un enlace compatible, muestra el iframe
            content.html(`<iframe src="${embedUrl}" allowfullscreen frameborder="0" style="width:100%; height:100%;"></iframe>`);
            container.show();
        } else {
            // Si no es compatible (ej. un enlace directo a .mp4), no muestra nada
            container.hide();
        }
    }
    // --- *** FIN CORRECCIÓN 2 *** ---

    // --- *** INICIO CORRECCIÓN 3: Reemplazar openMediaModal *** ---
    function openMediaModal(type, id = null) {
        const form = $('#mediaForm');
        form[0].reset();
        $('#mediaId').val(id || '');
        $('#mediaType').val(type);
        $('#edit-mode-notice').toggleClass('d-none', !id);

        // --- INICIO RESET CHUNKING ---
        // Resetear la UI de subida por trozos CADA VEZ que se abre el modal
        resetUploadUI();
        // --- FIN RESET CHUNKING ---


        if (type === 'video') {
            $('#mediaModalLabel').text(id ? 'Editar Vídeo' : 'Afegir Vídeo Nou');
            $('#url-tab-item').show();
            // --- ★ MODIFICACIÓN: Límite de vídeo a 50MB y mostrar recomendación ---
            $('#drop-area-text').html('<i class="bi bi-cloud-arrow-up-fill fs-1 text-secondary"></i><p class="m-0">Arrastra i solta un vídeo ací</p><p class="text-muted small">o fes clic (MP4, WebM, OGG | Màx: 50 MB)</p>');
            $('#mediaFile').attr('accept', 'video/mp4,video/webm,video/ogg');
            $('#video-upload-recommendation').show(); // Mostrar recomendación
        } else {
            $('#mediaModalLabel').text(id ? 'Editar Imatge' : 'Afegir Imatge Nova');
            $('#url-tab-item').hide();
            // --- ★ MODIFICACIÓN: Límite de imagen a 3MB y ocultar recomendación ---
            $('#drop-area-text').html('<i class="bi bi-cloud-arrow-up-fill fs-1 text-secondary"></i><p class="m-0">Arrastra i solta una imatge ací</p><p class="text-muted small">o fes clic (JPG, PNG, WebP | Màx: 3 MB)</p>');
            $('#mediaFile').attr('accept', 'image/jpeg,image/png,image/gif,image/webp');
            $('#video-upload-recommendation').hide(); // Ocultar recomendación
            new bootstrap.Tab($('#upload-file-tab')).show();
        }

        // Limpiar previsualizaciones e inputs
        $('#file-info').addClass('d-none').text('');
        $('#mediaUrl').val('');
        $('#url-preview-container').hide(); // <-- Limpiar preview de URL
        $('#url-preview-content').empty();   // <-- Limpiar preview de URL

        if(id) {
             // Modo Editar: Cargar datos
             $.getJSON('gallery.php', { ajax: true, action: 'get', type: type, id: id }, res => {
                if (res.status === 'success') {
                    $('#mediaTitle').val(res.media.title);
                    $('#mediaDescription').val(res.media.description);

                    const filename = res.media.filename || '';
                    const isUrl = filename.startsWith('http');

                    if (type === 'video' && isUrl) {
                        // 1. Poner URL en el campo de texto
                        $('#mediaUrl').val(filename);

                        // 2. Activar la pestaña "Afegir per URL"
                        new bootstrap.Tab($('#add-url-tab')).show();

                        // 3. (CORREGIDO) Mostrar la previsualización en el contenedor correcto
                        showUrlPreview(filename);

                    } else {
                        // 3. Activar la pestaña "Pujar Arxiu"
                        new bootstrap.Tab($('#upload-file-tab')).show();

                        // 4. (CORREGIDO) Mostrar la miniatura del archivo local
                        if (filename) {
                            let previewHtml = '';
                            if (type === 'video') {
                                previewHtml = `<video src="videos/${filename}#t=0.5" preload="metadata" controls muted style="width: 100%; max-height: 200px; border-radius: .25rem;"></video>`;
                            } else {
                                previewHtml = `<img src="${getImagePath(filename)}" alt="Previsualització actual" style="width: 100%; max-height: 200px; object-fit: contain; border-radius: .25rem;">`;
                            }

                            $('#file-info').html(`
                                <p class="mb-2"><strong>Arxiu actual:</strong> ${escapeHtml(filename)}</p>
                                ${previewHtml}
                                <small class="text-muted d-block mt-2">Pots pujar un nou arxiu per a reemplaçar-lo.</small>
                            `).removeClass('d-none');
                        }
                    }
                }
             });
        } else {
            // Modo Nuevo: Asegurar que la pestaña de subida esté activa
            new bootstrap.Tab($('#upload-file-tab')).show();
        }

        mediaModal.show();
    }
    // --- *** FIN CORRECCIÓN 3 *** ---

    $('#addVideoBtn').on('click', () => openMediaModal('video'));
    $('#addImageBtn').on('click', () => openMediaModal('image'));
    $(document).on('click', '.edit-media-btn', function(e) {
        e.preventDefault();
        openMediaModal($(this).data('type'), $(this).data('id'));
    });

    // --- INICIO LÓGICA DE SUBIDA (ROUTER) ---

    // Esta es la función de subida "legacy" (antigua), para URLs o imágenes simples
    function handleLegacyUpload() {
        const type = $('#mediaType').val();
        const action = $('#mediaId').val() ? 'update' : 'upload';

        // --- Lógica de la función original ---
        if (type === 'video' && (action === 'upload' || ($('#mediaId').val() && ($('#mediaFile').val() || $('#mediaUrl').val())))) {
            if ($('#addMediaTabs .nav-link.active').attr('id') === 'upload-file-tab') $('#mediaUrl').val('');
            else $('#mediaFile').val('');
        }

        const formData = new FormData(document.getElementById('mediaForm'));
        formData.append('ajax', true);
        formData.append('action', action);

        $.ajax({
            url: 'gallery.php', type: 'POST', data: formData, processData: false, contentType: false, dataType: 'json',
            success: res => {
                mediaModal.hide();
                showToast(res.message, 'success');

                const idToHighlight = res.saved_media_id;
                const state = type === 'video' ? videoState : imageState;

                fetchMedia(type).done(() => {
                    if (idToHighlight) {
                        let elementToFlash;
                        if (state.viewMode === 'grid') {
                            elementToFlash = $(`.manage-media-btn[data-id="${idToHighlight}"]`).closest('.card');
                        } else {
                            elementToFlash = $(`tr .manage-media-btn[data-id="${idToHighlight}"]`).closest('tr');
                        }

                        if (typeof flashElement === 'function' && elementToFlash && elementToFlash.length > 0) {
                            setTimeout(() => {
                                flashElement(elementToFlash);
                            }, 50);
                        }
                    }
                });
            },
            error: xhr => showToast(xhr.responseJSON?.message || 'Error desconegut', 'danger')
        });
        // --- Fin lógica de la función original ---
    }

    // Nuevo manejador de 'submit' que decide qué hacer
    $('#mediaForm').on('submit', function(e) {
        e.preventDefault();

        const type = $('#mediaType').val();
        const isFileTabActive = $('#upload-file-tab').hasClass('active');

        // --- DECISIÓN ---
        // 1. Si es un VÍDEO, está en la pestaña "Pujar Arxiu" Y se ha seleccionado un archivo: USAR CHUNKING
        if (type === 'video' && currentFile && isFileTabActive) {
            const title = $('#mediaTitle').val();
            const description = $('#mediaDescription').val();

            // --- ★ INICIO DE LA CORRECCIÓN ★ ---
            // Captura el ID del medio que se está editando
            const mediaId = $('#mediaId').val();
            // --- ★ FIN DE LA CORRECCIÓN ★ ---

            if (!title) {
                showToast('El títol és obligatori.', 'danger');
                return;
            }

            // Preparar UI para subida por trozos
            $('#submit-media-btn, #cancel-upload-btn').prop('disabled', true);
            $('#upload-progress-section').removeClass('d-none');
            $('#upload-content, #edit-mode-notice, #mediaTitle, #mediaDescription').find('input, textarea, button').prop('disabled', true);


            // Iniciar subida por trozos
            // --- ★ INICIO DE LA CORRECCIÓN ★ ---
            // Pasa el mediaId a la función de subida
            uploadFileInChunks(currentFile, title, description, type, mediaId); // <-- LÍNEA MODIFICADA
            // --- ★ FIN DE LA CORRECCIÓN ★ ---
        }
        // 2. Si es una IMATGE (usa subida simple) O es un VÍDEO por URL O es solo una EDICIÓN DE TEXTO: USAR LEGACY
        else {
            handleLegacyUpload();
        }
    });

    // --- INICIO FUNCIONES DE SUBIDA POR TROZOS ---

    /**
     * Función principal que gestiona el bucle de subida por trozos.
     */
    // --- ★ INICIO DE LA CORRECCIÓN ★ ---
    async function uploadFileInChunks(file, title, description, type, mediaId) { // <-- AÑADIR mediaId
    // --- ★ FIN DE LA CORRECCIÓN ★ ---
        currentUploadId = 'upload-' + Date.now() + '-' + Math.random().toString(36).substring(2);
        const totalChunks = Math.ceil(file.size / CHUNK_SIZE);

        $('#chunk-counter').text(`(Tros 0 de ${totalChunks})`);
        $('#total-progress-bar').css('width', '0%');
        $('#total-progress-text').text('0%');

        for (let i = 0; i < totalChunks; i++) {
            const start = i * CHUNK_SIZE;
            const end = Math.min(start + CHUNK_SIZE, file.size);
            const chunk = file.slice(start, end);

            $('#chunk-counter').text(`(Tros ${i + 1} de ${totalChunks})`);

            try {
                await uploadChunk(chunk, currentUploadId, i, totalChunks, file.name);
            } catch (error) {
                let errorMsg = 'Error en la pujada. Si us plau, intenta-ho de nou.';
                if (error && error.responseJSON && error.responseJSON.message) {
                    errorMsg = error.responseJSON.message;
                }
                showToast(errorMsg, 'danger');
                resetUploadUI(); // Resetear UI
                return;
            }
        }

        // 3. Ensamblar cuando todos los trozos estén subidos
        try {
            $('#chunk-counter').text('Processant...');
            $('#total-progress-text').text('Processant...');
            // --- ★ INICIO DE LA CORRECCIÓN ★ ---
            const response = await $.post('gallery.php', {
                ajax: true,
                action: 'assemble_chunks',
                upload_id: currentUploadId,
                original_filename: file.name,
                title: title,
                description: description,
                type: type,
                media_id: mediaId || null // <-- ★ LÍNEA AÑADIDA ★
            });
            // --- ★ FIN DE LA CORRECCIÓN ★ ---

            if (response.status === 'success') {
                showToast(response.message, 'success');
                mediaModal.hide();
                fetchMedia(type); // Recargar galería
            } else {
                showToast(response.message || 'Error en ensamblar l\'arxiu.', 'danger');
                resetUploadUI();
            }
        } catch (error) {
            showToast('Error finalitzant la pujada.', 'danger');
            resetUploadUI();
        }
    }

    /**
     * Sube un único trozo (chunk) y actualiza las barras de progreso.
     */
    function uploadChunk(chunk, uploadId, chunkIndex, totalChunks, filename) {
        return new Promise((resolve, reject) => {
            const formData = new FormData();
            formData.append('ajax', true);
            formData.append('action', 'upload_chunk');
            formData.append('upload_id', uploadId);
            formData.append('current_chunk_index', chunkIndex);
            formData.append('total_chunks', totalChunks);
            formData.append('media_chunk', chunk, filename);

            $.ajax({
                url: 'gallery.php',
                type: 'POST',
                data: formData,
                processData: false,
                contentType: false,
                dataType: 'json',

                xhr: function() {
                    const xhr = new window.XMLHttpRequest();
                    xhr.upload.addEventListener('progress', function(e) {
                        if (e.lengthComputable) {
                            // Progreso del trozo actual
                            const chunkProgressPercent = Math.round((e.loaded / e.total) * 100);
                            $('#chunk-progress-bar').css('width', chunkProgressPercent + '%');

                            // Progreso total (calculado)
                            const progressFromChunks = (chunkIndex / totalChunks) * 100;
                            const progressFromCurrentChunk = (e.loaded / e.total) * (100 / totalChunks);

                            const totalProgressPercent = Math.round(progressFromChunks + progressFromCurrentChunk);

                            $('#total-progress-bar').css('width', totalProgressPercent + '%');
                            $('#total-progress-text').text(totalProgressPercent + '%');
                        }
                    }, false);
                    return xhr;
                },

                success: function(response) {
                    if (response.status === 'chunk_uploaded') {
                        resolve(response);
                    } else {
                        reject(response);
                    }
                },
                error: function(xhr, status, error) {
                    reject(xhr);
                }
            });
        });
    }

    /**
     * Resetea la UI de subida en el modal.
     */
    function resetUploadUI() {
        // Habilitar botones y formulario
        $('#submit-media-btn, #cancel-upload-btn').prop('disabled', false);
        $('#upload-content, #edit-mode-notice, #mediaTitle, #mediaDescription').find('input, textarea, button').prop('disabled', false);

        // Ocultar sección de progreso
        $('#upload-progress-section').addClass('d-none');

        // Resetear barras y texto
        $('#total-progress-bar').css('width', '0%');
        $('#total-progress-text').text('0%');
        $('#chunk-progress-bar').css('width', '0%');
        $('#chunk-counter').text('');

        // Limpiar el archivo (importante)
        currentFile = null;
        $('#mediaFile').val('');
        $('#file-info').addClass('d-none').html('');
    }
    // --- FIN FUNCIONES DE SUBIDA POR TROZOS ---


    $(document).on('click', '.view-media-btn', function(e) {
        e.preventDefault();
        const type = $(this).data('type');
        const id = $(this).data('id');
        $('#viewMediaModalLabel').text('Carregant...');
        $('#viewMediaModalBody').html('<div class="text-center p-5"><div class="spinner-border"></div></div>');
        viewMediaModal.show();
        $.getJSON('gallery.php', { ajax: true, action: 'get', type, id })
        .done(response => {
            if (response.status === 'success') {
                const media = response.media;
                $('#viewMediaModalLabel').text(media.title);
                let playerHtml = '';
                if (type === 'video') {
                    const isEmbed = media.filename.startsWith('http');
                    const videoSrc = isEmbed ? media.filename : `videos/${media.filename}`;
                    playerHtml = isEmbed ? `<iframe src="${videoSrc}" class="w-100" style="aspect-ratio: 16/9;" frameborder="0" allowfullscreen></iframe>` : `<video controls oncontextmenu="return false;" controlsList="nodownload" preload="metadata" src="${videoSrc}" class="w-100"></video>`;
                } else {
                    playerHtml = `<div class="ratio ratio-16x9 bg-dark rounded"><img src="${getImagePath(media.filename)}" style="object-fit: contain; width: 100%; height: 100%;" alt="${media.title}"></div>`;
                }

                const uploadDate = new Date(media.upload_date).toLocaleDateString('ca-ES');
                let detailsHtml = `<p class="mt-3 small text-muted"><strong>Data:</strong> ${uploadDate}</p>`;
                if (media.description) {
                    detailsHtml += `<p class="text-muted">${media.description}</p>`;
                }

                $('#viewMediaModalBody').html(playerHtml + detailsHtml);
            }
        }).fail(() => $('#viewMediaModalBody').html('<div class="alert alert-danger">No s\'ha pogut carregar.</div>'));
    });

    $(document).on('click', '.manage-media-btn', function(e) {
        e.preventDefault();

        // --- ★ MODIFICACIÓN: Corregir captura de 'name' ---
        let mediaName = $(this).data('name');
        if (typeof mediaName === 'string') {
            mediaName = mediaName.replace(/^'|'$/g, ''); // Quita comillas simples del inicio/final
        }

        itemToManage = {
            id: $(this).data('id'),
            name: mediaName, // Usar el nombre limpio
            type: $(this).data('type'),
            ownerId: $(this).data('owner-id')
        };
        // --- ★ FIN MODIFICACIÓN ---

        $.getJSON('gallery.php', { ajax: true, action: 'check_media_usage', type: itemToManage.type, id: itemToManage.id })
        .done(response => {
            const modalTitle = $('#manageModalLabel');
            const modalBody = $('#manageModalBody').empty();
            const archiveBtn = $('#archiveBtn').off('click').hide();
            const deleteBtn = $('#deleteBtn').off('click').hide();

            const isOwner = itemToManage.ownerId == CURRENT_USER_ID;
            const isInUse = !response.is_usable_for_action;

            modalTitle.text(`Gestionar: ${itemToManage.name}`);

            if (isInUse) {
                let usageHtml = `<div class="alert alert-warning mb-3"><i class="bi bi-exclamation-triangle-fill me-2"></i><strong>Recurs en Ús</strong><br>Aquest arxiu no es pot eliminar perquè s'està utilitzant en els següents exercicis:</div><ul class="list-group list-group-flush small">`;
                response.usage.forEach(exTitle => { usageHtml += `<li class="list-group-item">${escapeHtml(exTitle)}</li>`; });
                usageHtml += '</ul><hr>';
                modalBody.append(usageHtml);
            }

            if (IS_SUPERADMIN) {
                archiveBtn.text('Arxivar per a mi').show().on('click', handlePersonalArchive);
                if (isInUse) {
                    modalBody.append('<p>Aquest recurs està en ús. Pots arxivar-lo o forçar la seua eliminació.</p><div class="alert alert-danger small"><strong>Atenció:</strong> Forçar l\'eliminació pot causar inconsistències en exercicis existents.</div>');
                    deleteBtn.text('Forçar Eliminació').show().on('click', handleDelete);
                } else {
                    modalBody.append('<p>Pots arxivar aquest recurs per a la teua vista o eliminar-lo permanentment del sistema.</p>');
                    deleteBtn.text('Eliminar Permanentment').show().on('click', handleDelete);
                }
            }
            else {
                archiveBtn.text('Arxivar per a mi').show().on('click', handlePersonalArchive);
                if (isOwner) {
                    if (isInUse) {
                        modalBody.append('<p>Aquest recurs, creat per tu, no es pot eliminar perquè està en ús. Només pots arxivar-lo.</p>');
                        deleteBtn.prop('disabled', true).attr('title', 'No es pot eliminar porque está en ús').show().text('Eliminar');
                    } else {
                        modalBody.append('<p>Com a creador, pots arxivar aquest recurs per a tu o eliminar-lo permanentment.</p>');
                        deleteBtn.prop('disabled', false).attr('title', 'Eliminar permanentment').show().text('Eliminar').on('click', handleDelete);
                    }
                } else {
                    modalBody.append('<p>Pots arxivar aquest recurs per a ocultar-lo de les teves llistes.</p>');
                }
            }
            manageModal.show();
        });
    });

    function handlePersonalArchive() {
        $.post('gallery.php', { ajax: true, action: 'personal_archive', type: itemToManage.type, id: itemToManage.id }, 'json')
        .done(res => { showToast(res.message, 'success'); manageModal.hide(); fetchMedia(itemToManage.type); })
        .fail(xhr => showToast(xhr.responseJSON.message, 'danger'));
    }

    function handleDelete() {
        if (prompt(`Per a confirmar l'eliminació permanent de "${itemToManage.name}", escriu ELIMINAR ací baix:`) === 'ELIMINAR') {

            // --- ★ INICIO DE LA NUEVA LÓGICA DE FLASH ---

            const idToDelete = itemToManage.id;
            const typeToDelete = itemToManage.type;
            const state = (typeToDelete === 'video') ? videoState : imageState;
            const fetchFunction = () => fetchMedia(typeToDelete);

            const $manageModal = (manageModal ? $(manageModal._element) : $('#manageModal')); // Obtener instancia del modal

            // 1. Encontrar el elemento ANTES de borrarlo
            let elementToDelete;
            if (state.viewMode === 'grid') {
                // En grid, el data-id está en el manage button. Buscamos la columna (col-lg-3) padre.
                elementToDelete = $(`.manage-media-btn[data-id="${idToDelete}"]`).closest('.col-lg-3');
            } else {
                // En lista, también está en el botón. Buscamos la fila (tr) padre.
                elementToDelete = $(`tr .manage-media-btn[data-id="${idToDelete}"]`).closest('tr');
            }

            // 2. Ocultar el modal inmediatamente
            if(manageModal) manageModal.hide();

            if (elementToDelete.length > 0) {
                // 3. Aplicar la animación de borrado (definida en style.css)
                elementToDelete.addClass('flash-delete');

                // 4. Esperar a que termine la animación (1.5s)
                setTimeout(() => {
                    // 5. Enviar la petición de borrado SOLO DESPUÉS de la animación
                    $.post('gallery.php', { ajax: true, action: 'delete', type: typeToDelete, id: idToDelete }, 'json')
                    .done(res => {
                        showToast(res.message, 'success');
                        // 6. Recargar la lista (el elemento ya está invisible)
                        fetchFunction();
                    })
                    .fail(xhr => {
                        showToast(xhr.responseJSON?.message || 'Error', 'danger');
                        // Si falla, quitamos la clase para que reaparezca
                        elementToDelete.removeClass('flash-delete');
                    });
                }, 1500); // Debe coincidir con la duración de la animación CSS

            } else {
                // Fallback (si no se encontró el elemento, solo borrar y recargar)
                $.post('gallery.php', { ajax: true, action: 'delete', type: typeToDelete, id: idToDelete }, 'json')
                .done(res => { showToast(res.message, 'success'); fetchFunction(); })
                .fail(xhr => showToast(xhr.responseJSON.message, 'danger'));
            }
            // --- ★ FIN DE LA NUEVA LÓGICA DE FLASH ---

        } else {
            showToast("L'acció d'eliminació ha sigut cancel·lada.", 'info');
        }
    }

    $(document).on('click', '.page-link', function(e) {
        e.preventDefault();
        const type = $(this).data('type');
        const page = $(this).data('page');
        const state = type === 'video' ? videoState : imageState;
        if (page !== state.page) {
            state.page = page;
            fetchMedia(type);
        }
    });

    $(document).on('click', '#videos-panel .sortable-header, #images-panel .sortable-header', function() {
        const type = $(this).closest('.tab-pane').attr('id').includes('videos') ? 'video' : 'image';
        const state = type === 'video' ? videoState : imageState;
        const newSort = $(this).data('sort');

        if (state.sort === newSort) {
            state.order = state.order === 'ASC' ? 'DESC' : 'ASC';
        } else {
            state.sort = newSort;
            // --- MODIFICACIÓN 7 (JS): Actualizar la lógica de ordenación por defecto ---
            state.order = (newSort === 'title' || newSort === 'uploader_name') ? 'ASC' : 'DESC';
        }
        state.page = 1;
        fetchMedia(type);
    });

    $('#filterMyVideos').on('change', function() { videoState.filterMine = $(this).is(':checked'); videoState.page = 1; fetchMedia('video'); });
    $('#filterMyImages').on('change', function() { imageState.filterMine = $(this).is(':checked'); imageState.page = 1; fetchMedia('image'); });

    // --- ★ TAREA 2 (Botones UI) ---
    $('.toggle-view-btn').on('click', function() {
        const type = $(this).data('type');
        const state = type === 'video' ? videoState : imageState;
        const newViewMode = $(this).attr('id').includes('GridView') ? 'grid' : 'list';

        if (state.viewMode === newViewMode) return;

        state.viewMode = newViewMode;
        $(`#${type}s-panel .toggle-view-btn`).removeClass('active');
        $(this).addClass('active');

        // --- ★ INICIO DE LA MODIFICACIÓN ★ ---
        const sortButtonGroup = `#${type}-sort-buttons-group`;
        if (newViewMode === 'list') {
            $(sortButtonGroup).hide(); // Ocultar botones en Lista
        } else {
            $(sortButtonGroup).show(); // Mostrar botones en Rejilla
        }
        // --- ★ FIN DE LA MODIFICACIÓN ★ ---

        const pageKey = (type === 'video') ? 'gallery_video' : 'gallery_image';
        $.post('dashboard.php', {
            ajax: true,
            action: 'save_view_preference',
            page_name: pageKey,
            view_mode: newViewMode
        }, 'json')
        .done(res => {
            if(res.status === 'success') { console.log('Vista guardada: ' + pageKey + ' -> ' + newViewMode); }
        })
        .fail(xhr => {
             console.error('Error guardant la preferència de vista.', xhr.responseText);
        });

        fetchMedia(type);
    });
    // --- ★ FIN TAREA 2 ---


    $('.sort-btn').on('click', function() {
        const type = $(this).data('type');
        const state = type === 'video' ? videoState : imageState;
        const newSort = $(this).data('sort');
        const newOrder = $(this).data('order');

        if (state.sort === newSort) { state.order = state.order === 'ASC' ? 'DESC' : 'ASC'; }
        else { state.sort = newSort; state.order = newOrder; }

        state.page = 1;
        fetchMedia(type);
    });

    // --- INICIO CORRECCIÓN: Listeners de pestañas ESPECÍFICOS para #mediaTabs ---

        // 1. Evento 'show.bs.tab': Se dispara ANTES de que la pestaña cambie visualmente.
        // Usamos el selector '#mediaTabs button...' para que SOLO afecte a las pestañas principales
        // y NO a las pestañas dentro de los modales (que causaban el error).
        $('#mediaTabs button[data-bs-toggle="tab"]').on('show.bs.tab', function(e) {
            const targetId = $(e.target).attr('id');

            if (targetId === 'videos-tab') {
                // Si vamos a la pestaña Vídeos: Mostrar botón de vídeo, ocultar imagen
                $('#addVideoBtn').removeClass('d-none');
                $('#addImageBtn').addClass('d-none');
            } else {
                // Si vamos a la pestaña Imágenes: Mostrar botón de imagen, ocultar vídeo
                $('#addVideoBtn').addClass('d-none');
                $('#addImageBtn').removeClass('d-none');
            }
        });

        // 2. Evento 'shown.bs.tab': Se dispara DESPUÉS de que la pestaña haya cambiado.
        // Lo usamos para cargar los datos (Lazy loading) solo cuando sea necesario.
        $('#mediaTabs button[data-bs-toggle="tab"]').on('shown.bs.tab', function(e) {
            const targetId = $(e.target).attr('id');

            // Solo cargamos imágenes si entramos en su pestaña.
            // Los vídeos ya se cargan al inicio por defecto.
            if (targetId === 'images-tab') {
                fetchMedia('image');
            }
        });

        // --- FIN CORRECCIÓN ---

    const urlParams_gal = new URLSearchParams(window.location.search);
    if (urlParams_gal.get('filter') === 'mine') {
        videoState.filterMine = true;
        imageState.filterMine = true;
    }

    if ($('#filterMyVideos').length > 0) $('#filterMyVideos').prop('checked', videoState.filterMine);
    if ($('#filterMyImages').length > 0) $('#filterMyImages').prop('checked', imageState.filterMine);

    // --- ★ TAREA 2 (Botones UI) ---
    // Aplicar estado inicial de botones
    $('#videos-panel .toggle-view-btn').removeClass('active');
    if (videoState.viewMode === 'grid') {
        $('#videoGridViewBtn').addClass('active');
        $('#video-sort-buttons-group').show();
    } else {
        $('#videoListViewBtn').addClass('active');
        $('#video-sort-buttons-group').hide();
    }

    $('#images-panel .toggle-view-btn').removeClass('active');
    if (imageState.viewMode === 'grid') {
        $('#imageGridViewBtn').addClass('active');
        $('#image-sort-buttons-group').show();
    } else {
        $('#imageListViewBtn').addClass('active');
        $('#image-sort-buttons-group').hide();
    }
    // --- ★ FIN TAREA 2 ---

    const hash = window.location.hash;
    if (hash === '#images-tab') {
        new bootstrap.Tab($('#images-tab')).show();
    } else {
        fetchMedia('video');
    }


    const dropArea = $('#drop-area'), fileInput = $('#mediaFile'), fileInfo = $('#file-info');
    dropArea.on('click', () => fileInput.click());
    fileInput.on('change', () => handleFiles(fileInput[0].files));
    dropArea.on('dragenter dragover', e => { e.preventDefault(); dropArea.addClass('border-primary'); });
    dropArea.on('dragleave drop', e => { e.preventDefault(); dropArea.removeClass('border-primary'); });
    dropArea.on('drop', e => handleFiles(e.originalEvent.dataTransfer.files));

    // --- *** INICIO CORRECCIÓN 4: Reemplazar handleFiles *** ---
    function handleFiles(files) {
        if (files.length > 0) {
            const file = files[0];
            const type = $('#mediaType').val(); // 'video' o 'image'

            // --- INICIO LÓGICA CHUNKING ---
            // Guardar el archivo globalmente
            currentFile = file;
            // --- FIN LÓGICA CHUNKING ---

            // --- ★ MODIFICACIÓN: Límites actualizados ---
            const maxSize = (type === 'video' ? 50 : 3) * 1024 * 1024;
            const fileInput = $('#mediaFile');
            const fileInfo = $('#file-info');

            if (file.size > maxSize) {
                // --- ★ MODIFICACIÓN: Mensajes de error actualizados ---
                showToast(`L'arxiu supera el límit de ${type === 'video' ? 50 : 3} MB.`, 'danger');
                fileInput.val('');
                currentFile = null; // Limpiar el archivo global
                return;
            }

            const allowedVideoTypes = ['video/mp4', 'video/webm', 'video/ogg'];
            const allowedImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];

            if (type === 'video' && !allowedVideoTypes.includes(file.type)) {
                 showToast('Format de vídeo no permès (només MP4, WebM, OGG).', 'danger');
                 fileInput.val('');
                 currentFile = null; // Limpiar el archivo global
                 return;
            }
            if (type === 'image' && !allowedImageTypes.includes(file.type)) {
                 showToast('Format d\'imatge no permès (només JPG, PNG, GIF, WebP).', 'danger');
                 fileInput.val('');
                 currentFile = null; // Limpiar el archivo global
                 return;
            }

            // No es necesario: fileInput[0].files = files; ya que no usaremos el submit nativo

            if (currentPreviewUrl) {
                URL.revokeObjectURL(currentPreviewUrl);
            }

            currentPreviewUrl = URL.createObjectURL(file);
            let previewHtml = '';

            if (type === 'video') {
                previewHtml = `<video src="${currentPreviewUrl}" controls muted style="width: 100%; max-height: 200px; border-radius: .25rem;"></video>`;
            } else {
                previewHtml = `<img src="${currentPreviewUrl}" alt="Previsualització" style="width: 100%; max-height: 200px; object-fit: contain; border-radius: .25rem;">`;
            }

            fileInfo.html(`
                <p class="mb-2"><strong>Arxiu seleccionat:</strong> ${escapeHtml(file.name)}</p>
                ${previewHtml}
            `).removeClass('d-none');
        }
    }
    // --- *** FIN CORRECCIÓN 4 *** ---

    // --- *** INICIO CORRECCIÓN 5: Añadir listener hidden.bs.modal y para la URL *** ---
    $('#mediaModal').on('hidden.bs.modal', function () {
        // Allibera la memòria de la previsualització d'ARXIU
        if (currentPreviewUrl) {
            URL.revokeObjectURL(currentPreviewUrl);
            currentPreviewUrl = null;
        }
        // Neteja el file input
        $('#mediaFile').val('');
        currentFile = null; // Limpiar el archivo global

        // NOU: Neteja la previsualització de URL
        $('#url-preview-container').hide();
        $('#url-preview-content').empty();
        $('#mediaUrl').val(''); // Asegurar que la URL también se limpia

        // NOU: Resetear la UI de subida
        resetUploadUI();
    });

    // Listener para previsualizar la URL al escribir/pegar
    let previewTimeout;
    $('#mediaUrl').on('input', function() {
        clearTimeout(previewTimeout);
        const url = $(this).val();
        // Esperar 500ms después de que el usuario deje de teclear
        previewTimeout = setTimeout(() => {
            showUrlPreview(url);
        }, 500);
    });
    // --- *** FIN CORRECCIÓN 5 *** ---
});

// --- ★ MODIFICACIÓN: Corrección de 'escapeHtml' y 'renderCardButtons' ---
// (Estas funciones deben estar fuera de $(document).ready() si son llamadas por otras)
// ... pero en este caso, están contenidas. Sin embargo, 'escapeHtml' es necesaria
// para el 'data-name' en renderCardButtons. La muevo a un scope más accesible
// o me aseguro de que esté definida.
// En tu 'footer.php' ya tienes una 'escapeHtml' global, así que no es necesario
// duplicarla aquí. Simplemente me aseguro de que 'renderCardButtons' la use.

/**
 * Escapa HTML para prevenir XSS.
 * @param {string} text - El texto a escapar.
 *D * @returns {string} - El texto escapado.
 */
function escapeHtml(text) {
    if (typeof text !== 'string') return '';
    var map = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#039;'
    };
    return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
</script>

</body>
</html>
