<?php
// modules/estudiante/accion_ficha_est.php
// Maneja la subida de la ficha PPP firmada por el estudiante
// ★ Versión con validación segura: magic bytes, MIME real, PDF header, tamaño ★
require_once __DIR__ . '/../../config/app.php';
require_once __DIR__ . '/../../config/onedrive.php';
require_once __DIR__ . '/../../includes/OneDriveStorage.php';
require_once __DIR__ . '/../../includes/auth.php';
require_once __DIR__ . '/../../includes/functions.php';
require_once __DIR__ . '/../../includes/flash.php';
requireRol(ROL_ESTUDIANTE);

$user      = currentUser();
$db        = getDB();
$idUsuario = $user['id_usuario'] ?? $user['id'] ?? 0;
$idPrac    = (int)($_POST['id_practica'] ?? 0);
$accion    = $_POST['accion'] ?? '';
$ip        = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';

if (!$idPrac || $accion !== 'upload_est') {
    header('Location: perfil.php');
    exit;
}

// ── Verificar que la práctica pertenece a este estudiante ──────────────────
$stEst = $db->prepare("
    SELECT e.id_estudiante, pr.ficha_estado
    FROM   practicas pr
    JOIN   estudiantes e ON e.id_estudiante = pr.id_estudiante
    WHERE  pr.id_practica = :idp
      AND  e.id_usuario   = :idu
    LIMIT 1
");
$stEst->execute([':idp' => $idPrac, ':idu' => $idUsuario]);
$row = $stEst->fetch();

if (!$row) {
    error_log("[SIGVP][UPLOAD] Acceso denegado — IP:{$ip} | practica:{$idPrac} | usuario:{$idUsuario}");
    flashSet('err', '❌ No tienes permiso para esta acción.');
    header('Location: perfil.php');
    exit;
}

if (!in_array($row['ficha_estado'], ['enviada', 'firmada_est'])) {
    flashSet('err', '❌ La ficha no está disponible para subir. El coordinador debe habilitarla primero.');
    header('Location: perfil.php');
    exit;
}

// ── ¿Se recibió el archivo? ────────────────────────────────────────────────
if (!isset($_FILES['ficha_firmada'])) {
    flashSet('err', '❌ No se recibió ningún archivo.');
    header('Location: perfil.php');
    exit;
}

$file = $_FILES['ficha_firmada'];

// ── 1. Error de subida ─────────────────────────────────────────────────────
if ($file['error'] !== UPLOAD_ERR_OK) {
    $errCodes = [
        UPLOAD_ERR_INI_SIZE   => 'El archivo supera el límite del servidor.',
        UPLOAD_ERR_FORM_SIZE  => 'El archivo supera el límite del formulario.',
        UPLOAD_ERR_PARTIAL    => 'La subida fue interrumpida. Intenta de nuevo.',
        UPLOAD_ERR_NO_FILE    => 'No seleccionaste ningún archivo.',
        UPLOAD_ERR_NO_TMP_DIR => 'Error interno del servidor.',
        UPLOAD_ERR_CANT_WRITE => 'Error al escribir en disco.',
    ];
    flashSet('err', '❌ ' . ($errCodes[$file['error']] ?? 'Error desconocido al subir.'));
    header('Location: perfil.php');
    exit;
}

// ── 2. Verificar que es un archivo realmente subido (anti path traversal) ──
if (!is_uploaded_file($file['tmp_name'])) {
    error_log("[SIGVP][UPLOAD][ALERTA] is_uploaded_file falló — IP:{$ip}");
    flashSet('err', '❌ Archivo inválido.');
    header('Location: perfil.php');
    exit;
}

// ── 3. Límite de tamaño: máx 10 MB ────────────────────────────────────────
if ($file['size'] > 10 * 1024 * 1024) {
    flashSet('err', '❌ El archivo es demasiado grande. Máximo: 10 MB.');
    header('Location: perfil.php');
    exit;
}

// ── 4. Extensión permitida ─────────────────────────────────────────────────
$tiposPermitidos = [
    'pdf'  => 'application/pdf',
    'jpg'  => 'image/jpeg',
    'jpeg' => 'image/jpeg',
    'png'  => 'image/png',
];

$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!array_key_exists($ext, $tiposPermitidos)) {
    flashSet('err', '❌ Formato no permitido. Solo se aceptan: PDF, JPG, PNG.');
    header('Location: perfil.php');
    exit;
}

// ── 5. ★ MAGIC BYTES con finfo — detecta archivos camuflados ★ ────────────
$finfo    = new finfo(FILEINFO_MIME_TYPE);
$mimeReal = $finfo->file($file['tmp_name']);

if ($mimeReal !== $tiposPermitidos[$ext]) {
    error_log("[SIGVP][UPLOAD][BLOQUEADO] Camuflado — MIME:{$mimeReal} | ext:.{$ext} | IP:{$ip} | usr:{$idUsuario}");
    flashSet('err', '❌ Archivo rechazado: el contenido no coincide con la extensión. Sube un archivo real.');
    header('Location: perfil.php');
    exit;
}

// ── 6. PDF: verificar cabecera mágica %PDF ────────────────────────────────
if ($ext === 'pdf') {
    $h   = fopen($file['tmp_name'], 'rb');
    $hdr = fread($h, 5);
    fclose($h);
    if (strpos($hdr, '%PDF') !== 0) {
        error_log("[SIGVP][UPLOAD][BLOQUEADO] PDF falso — hdr:" . bin2hex($hdr) . " IP:{$ip}");
        flashSet('err', '❌ El archivo PDF no es válido.');
        header('Location: perfil.php');
        exit;
    }
}

// ── 7. Imágenes: verificar con getimagesize ────────────────────────────────
if (in_array($mimeReal, ['image/jpeg', 'image/png'])) {
    if (@getimagesize($file['tmp_name']) === false) {
        error_log("[SIGVP][UPLOAD][BLOQUEADO] Imagen inválida — IP:{$ip}");
        flashSet('err', '❌ El archivo de imagen no es válido.');
        header('Location: perfil.php');
        exit;
    }
}

// ── 8. Nombre 100% seguro: aleatorio, nunca el nombre del cliente ──────────
$nombreSeguro = 'ficha_est_' . $idPrac . '_' . bin2hex(random_bytes(8)) . '.' . $ext;

// ── 9. Subir a OneDrive ────────────────────────────────────────────────────
try {
    $rutaOD = OneDriveStorage::upload($file['tmp_name'], 'fichas_ppp', $nombreSeguro, (string)$idPrac, (int)($user['id_instituto'] ?? 0));

    $db->prepare("
        UPDATE practicas
        SET    ficha_estado  = 'firmada_est',
               ficha_doc_est = :doc
        WHERE  id_practica   = :id
    ")->execute([
        ':doc' => $rutaOD,
        ':id'  => $idPrac,
    ]);

    error_log("[SIGVP][UPLOAD][OK] Ficha OneDrive — practica:{$idPrac} | archivo:{$nombreSeguro} | IP:{$ip}");
    flashSet('ok', '✅ ¡Ficha firmada enviada correctamente! El coordinador la revisará.');
} catch(Exception $e) {
    flashSet('err', '❌ Error al subir a OneDrive: '.$e->getMessage());
}

header('Location: perfil.php');
exit;