<?php

class FileValidator {
    // Configuración de archivos permitidos
    private static $allowedMimeTypes = [
        'application/pdf',
        'application/x-pdf'
    ];
    
    private static $allowedExtensions = ['pdf'];
    private static $maxFileSize = 5 * 1024 * 1024; // 5MB
    private static $minFileSize = 1024; // 1KB mínimo
    
    /**
     * Validar archivo subido
     * @param array $file Array de $_FILES
     * @return array ['valid' => bool, 'error' => string|null, 'details' => array]
     */
    public static function validateUpload($file) {
        $result = [
            'valid' => false,
            'error' => null,
            'details' => []
        ];
        
        try {
            // Verificar que el archivo existe
            if (!isset($file) || !is_array($file)) {
                $result['error'] = 'No se proporcionó información del archivo';
                return $result;
            }
            
            // Verificar errores de subida
            $uploadError = self::checkUploadErrors($file);
            if ($uploadError) {
                $result['error'] = $uploadError;
                return $result;
            }
            
            // Verificar tamaño del archivo
            $sizeError = self::validateFileSize($file['size']);
            if ($sizeError) {
                $result['error'] = $sizeError;
                return $result;
            }
            
            // Verificar tipo MIME
            $mimeError = self::validateMimeType($file['tmp_name']);
            if ($mimeError) {
                $result['error'] = $mimeError;
                return $result;
            }
            
            // Verificar extensión
            $extensionError = self::validateExtension($file['name']);
            if ($extensionError) {
                $result['error'] = $extensionError;
                return $result;
            }
            
            // Verificar contenido del archivo (magic bytes)
            $contentError = self::validateFileContent($file['tmp_name']);
            if ($contentError) {
                $result['error'] = $contentError;
                return $result;
            }
            
            // Verificar nombre del archivo
            $nameError = self::validateFileName($file['name']);
            if ($nameError) {
                $result['error'] = $nameError;
                return $result;
            }
            
            $result['valid'] = true;
            $result['details'] = [
                'original_name' => $file['name'],
                'size' => $file['size'],
                'mime_type' => self::getMimeType($file['tmp_name']),
                'extension' => strtolower(pathinfo($file['name'], PATHINFO_EXTENSION))
            ];
            
        } catch (Exception $e) {
            $result['error'] = 'Error interno al validar archivo: ' . $e->getMessage();
        }
        
        return $result;
    }
    
    /**
     * Verificar errores de subida de PHP
     */
    private static function checkUploadErrors($file) {
        if (!isset($file['error'])) {
            return 'Error: información de subida incompleta';
        }
        
        switch ($file['error']) {
            case UPLOAD_ERR_OK:
                return null;
            case UPLOAD_ERR_INI_SIZE:
                return 'El archivo excede el tamaño máximo permitido por el servidor';
            case UPLOAD_ERR_FORM_SIZE:
                return 'El archivo excede el tamaño máximo permitido por el formulario';
            case UPLOAD_ERR_PARTIAL:
                return 'El archivo se subió parcialmente';
            case UPLOAD_ERR_NO_FILE:
                return 'No se seleccionó ningún archivo';
            case UPLOAD_ERR_NO_TMP_DIR:
                return 'Falta el directorio temporal en el servidor';
            case UPLOAD_ERR_CANT_WRITE:
                return 'Error al escribir el archivo en el servidor';
            case UPLOAD_ERR_EXTENSION:
                return 'Una extensión de PHP detuvo la subida del archivo';
            default:
                return 'Error desconocido al subir el archivo (código: ' . $file['error'] . ')';
        }
    }
    
    /**
     * Validar tamaño del archivo
     */
    private static function validateFileSize($size) {
        if ($size < self::$minFileSize) {
            return 'El archivo es demasiado pequeño (mínimo: ' . self::formatBytes(self::$minFileSize) . ')';
        }
        
        if ($size > self::$maxFileSize) {
            return 'El archivo es demasiado grande (máximo: ' . self::formatBytes(self::$maxFileSize) . ')';
        }
        
        return null;
    }
    
    /**
     * Validar tipo MIME del archivo
     */
    private static function validateMimeType($tmpName) {
        $mimeType = self::getMimeType($tmpName);
        
        if (!in_array($mimeType, self::$allowedMimeTypes)) {
            return 'Tipo de archivo no permitido. Solo se aceptan archivos PDF';
        }
        
        return null;
    }
    
    /**
     * Obtener tipo MIME del archivo
     */
    private static function getMimeType($tmpName) {
        if (function_exists('finfo_open')) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mimeType = finfo_file($finfo, $tmpName);
            finfo_close($finfo);
            return $mimeType;
        } elseif (function_exists('mime_content_type')) {
            return mime_content_type($tmpName);
        } else {
            // Fallback básico
            return 'application/octet-stream';
        }
    }
    
    /**
     * Validar extensión del archivo
     */
    private static function validateExtension($fileName) {
        $extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
        
        if (!in_array($extension, self::$allowedExtensions)) {
            return 'Extensión de archivo no permitida. Solo se aceptan archivos: ' . implode(', ', self::$allowedExtensions);
        }
        
        return null;
    }
    
    /**
     * Validar contenido del archivo (magic bytes para PDF)
     */
    private static function validateFileContent($tmpName) {
        $handle = fopen($tmpName, 'rb');
        if (!$handle) {
            return 'No se pudo leer el archivo';
        }
        
        $header = fread($handle, 8);
        fclose($handle);
        
        // Verificar magic bytes de PDF
        if (substr($header, 0, 4) !== '%PDF') {
            return 'El archivo no es un PDF válido';
        }
        
        return null;
    }
    
    /**
     * Validar nombre del archivo
     */
    private static function validateFileName($fileName) {
        // Verificar longitud
        if (strlen($fileName) > 255) {
            return 'El nombre del archivo es demasiado largo';
        }
        
        // Verificar caracteres peligrosos
        $dangerousChars = ['<', '>', ':', '"', '|', '?', '*', '\\', '/'];
        foreach ($dangerousChars as $char) {
            if (strpos($fileName, $char) !== false) {
                return 'El nombre del archivo contiene caracteres no permitidos';
            }
        }
        
        // Verificar que no sea solo espacios o puntos
        if (trim($fileName, ' .') === '') {
            return 'Nombre de archivo inválido';
        }
        
        return null;
    }
    
    /**
     * Generar nombre seguro para archivo
     */
    public static function generateSecureFileName($originalName, $prefix = '', $suffix = '') {
        $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
        $baseName = pathinfo($originalName, PATHINFO_FILENAME);
        
        // Limpiar nombre base
        $baseName = preg_replace('/[^a-zA-Z0-9_-]/', '_', $baseName);
        $baseName = substr($baseName, 0, 50); // Limitar longitud
        
        // Generar timestamp y hash único
        $timestamp = time();
        $uniqueId = substr(md5(uniqid(rand(), true)), 0, 8);
        
        return $prefix . $baseName . '_' . $timestamp . '_' . $uniqueId . $suffix . '.' . $extension;
    }
    
    /**
     * Formatear bytes a formato legible
     */
    private static function formatBytes($bytes, $precision = 2) {
        $units = array('B', 'KB', 'MB', 'GB', 'TB');
        
        for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
            $bytes /= 1024;
        }
        
        return round($bytes, $precision) . ' ' . $units[$i];
    }
    
    /**
     * Verificar si un directorio es seguro para almacenar archivos
     */
    public static function validateUploadDirectory($path) {
        // Verificar que el directorio existe
        if (!is_dir($path)) {
            return 'El directorio de subida no existe';
        }
        
        // Verificar permisos de escritura
        if (!is_writable($path)) {
            return 'No hay permisos de escritura en el directorio';
        }
        
        // Verificar que no esté en directorio web público directo
        $realPath = realpath($path);
        $documentRoot = $_SERVER['DOCUMENT_ROOT'] ?? '';
        
        if ($documentRoot && strpos($realPath, $documentRoot) === 0) {
            // Está en document root, verificar que tenga protección
            $htaccessPath = $path . '/.htaccess';
            if (!file_exists($htaccessPath)) {
                return 'Directorio de subida no está protegido adecuadamente';
            }
        }
        
        return null;
    }
}