<?php

require_once __DIR__ . '/FileValidator.php';
require_once __DIR__ . '/SecurityLogger.php';

class SecureFileManager {
    private $uploadPath;
    private $allowedUsers;
    private $maxStorageSize;
    
    public function __construct($uploadPath = null) {
        $this->uploadPath = $uploadPath ?: __DIR__ . '/../../uploads/documents/';
        $this->maxStorageSize = 100 * 1024 * 1024; // 100MB total
        $this->allowedUsers = ['Administrador', 'Regente'];
        
        $this->initializeStorage();
    }
    
    /**
     * Inicializar almacenamiento seguro
     */
    private function initializeStorage() {
        // Crear directorio si no existe
        if (!is_dir($this->uploadPath)) {
            mkdir($this->uploadPath, 0750, true);
        }
        
        // Crear .htaccess para proteger directorio
        $this->createSecurityFiles();
        
        // Verificar permisos
        if (!is_writable($this->uploadPath)) {
            throw new Exception('Directorio de uploads no tiene permisos de escritura');
        }
    }
    
    /**
     * Crear archivos de seguridad en directorio de uploads
     */
    private function createSecurityFiles() {
        // .htaccess para denegar acceso directo
        $htaccessPath = $this->uploadPath . '.htaccess';
        if (!file_exists($htaccessPath)) {
            $htaccessContent = <<<EOT
# Denegar acceso directo a archivos
<RequireAll>
    Require all denied
</RequireAll>

# Prevenir ejecución de scripts
<FilesMatch "\.(php|phtml|php3|php4|php5|pl|py|jsp|asp|sh|cgi)$">
    <RequireAll>
        Require all denied
    </RequireAll>
</FilesMatch>

# Solo permitir PDFs
<FilesMatch "\.pdf$">
    ForceType application/pdf
    Header set Content-Type "application/pdf"
</FilesMatch>

# Prevenir listado de directorio
Options -Indexes

# Headers de seguridad
<IfModule mod_headers.c>
    Header always set X-Content-Type-Options nosniff
    Header always set X-Frame-Options DENY
</IfModule>
EOT;
            file_put_contents($htaccessPath, $htaccessContent);
        }
        
        // index.php para prevenir listado de directorio
        $indexPath = $this->uploadPath . 'index.php';
        if (!file_exists($indexPath)) {
            file_put_contents($indexPath, "<?php\nhttp_response_code(403);\nexit('Access denied');\n?>");
        }
    }
    
    /**
     * Subir archivo de forma segura
     */
    public function uploadFile($file, $projectId, $documentType, $userId, $userRole) {
        try {
            // Verificar permisos de usuario
            if (!in_array($userRole, $this->allowedUsers)) {
                SecurityLogger::logSecurityViolation(
                    'unauthorized_file_upload',
                    "Usuario con rol '$userRole' intentó subir archivo",
                    $userId,
                    'high'
                );
                throw new Exception('No tiene permisos para subir archivos');
            }
            
            // Validar archivo
            $validation = FileValidator::validateUpload($file);
            if (!$validation['valid']) {
                SecurityLogger::logSecurityViolation(
                    'invalid_file_upload',
                    "Archivo inválido: " . $validation['error'],
                    $userId,
                    'medium'
                );
                throw new Exception($validation['error']);
            }
            
            // Verificar espacio disponible
            if (!$this->checkStorageSpace($file['size'])) {
                throw new Exception('No hay suficiente espacio de almacenamiento');
            }
            
            // Generar nombre seguro
            $secureFileName = FileValidator::generateSecureFileName(
                $file['name'],
                $projectId . '_' . $documentType . '_'
            );
            
            $destinationPath = $this->uploadPath . $secureFileName;
            
            // Mover archivo
            if (!move_uploaded_file($file['tmp_name'], $destinationPath)) {
                throw new Exception('Error al guardar el archivo');
            }
            
            // Establecer permisos seguros
            chmod($destinationPath, 0640);
            
            // Log de éxito
            SecurityLogger::logFileAccess(
                'upload',
                $secureFileName,
                $userId,
                $userRole,
                true,
                [
                    'project_id' => $projectId,
                    'document_type' => $documentType,
                    'original_name' => $file['name'],
                    'file_size' => $file['size']
                ]
            );
            
            return [
                'success' => true,
                'file_name' => $secureFileName,
                'relative_path' => 'uploads/documents/' . $secureFileName,
                'original_name' => $file['name'],
                'size' => $file['size']
            ];
            
        } catch (Exception $e) {
            SecurityLogger::logFileAccess(
                'upload',
                $file['name'] ?? 'unknown',
                $userId,
                $userRole,
                false,
                ['error' => $e->getMessage()]
            );
            
            throw $e;
        }
    }
    
    /**
     * Servir archivo de forma segura
     */
    public function serveFile($fileName, $userId, $userRole, $projectId = null) {
        try {
            // Verificar permisos de usuario
            if (!in_array($userRole, $this->allowedUsers)) {
                SecurityLogger::logSecurityViolation(
                    'unauthorized_file_access',
                    "Usuario con rol '$userRole' intentó acceder a archivo '$fileName'",
                    $userId,
                    'high'
                );
                throw new Exception('No tiene permisos para acceder a archivos');
            }
            
            // Sanitizar nombre de archivo
            $fileName = basename($fileName);
            $filePath = $this->uploadPath . $fileName;
            
            // Verificar que el archivo existe
            if (!file_exists($filePath)) {
                SecurityLogger::logFileAccess(
                    'download',
                    $fileName,
                    $userId,
                    $userRole,
                    false,
                    ['error' => 'Archivo no encontrado']
                );
                throw new Exception('Archivo no encontrado');
            }
            
            // Verificar que es un archivo válido (no directorio)
            if (!is_file($filePath)) {
                SecurityLogger::logSecurityViolation(
                    'suspicious_file_access',
                    "Intento de acceso a no-archivo: '$fileName'",
                    $userId,
                    'high'
                );
                throw new Exception('Recurso inválido');
            }
            
            // Verificar tipo de archivo
            $mimeType = mime_content_type($filePath);
            if (!in_array($mimeType, ['application/pdf', 'application/x-pdf'])) {
                SecurityLogger::logSecurityViolation(
                    'invalid_file_type_access',
                    "Intento de acceso a archivo con tipo MIME inválido: '$mimeType'",
                    $userId,
                    'medium'
                );
                throw new Exception('Tipo de archivo no permitido');
            }
            
            // Log de acceso exitoso
            SecurityLogger::logFileAccess(
                'download',
                $fileName,
                $userId,
                $userRole,
                true,
                [
                    'project_id' => $projectId,
                    'file_size' => filesize($filePath)
                ]
            );
            
            // Configurar headers seguros para descarga
            $this->setSecureDownloadHeaders($fileName, filesize($filePath));
            
            // Enviar archivo
            readfile($filePath);
            exit;
            
        } catch (Exception $e) {
            throw $e;
        }
    }
    
    /**
     * Eliminar archivo de forma segura
     */
    public function deleteFile($fileName, $userId, $userRole) {
        try {
            // Verificar permisos
            if (!in_array($userRole, $this->allowedUsers)) {
                SecurityLogger::logSecurityViolation(
                    'unauthorized_file_deletion',
                    "Usuario con rol '$userRole' intentó eliminar archivo '$fileName'",
                    $userId,
                    'high'
                );
                throw new Exception('No tiene permisos para eliminar archivos');
            }
            
            $fileName = basename($fileName);
            $filePath = $this->uploadPath . $fileName;
            
            if (!file_exists($filePath)) {
                return true; // Ya no existe
            }
            
            if (unlink($filePath)) {
                SecurityLogger::logFileAccess(
                    'delete',
                    $fileName,
                    $userId,
                    $userRole,
                    true
                );
                return true;
            } else {
                SecurityLogger::logFileAccess(
                    'delete',
                    $fileName,
                    $userId,
                    $userRole,
                    false,
                    ['error' => 'Error al eliminar archivo']
                );
                throw new Exception('Error al eliminar el archivo');
            }
            
        } catch (Exception $e) {
            throw $e;
        }
    }
    
    /**
     * Verificar espacio de almacenamiento disponible
     */
    private function checkStorageSpace($newFileSize) {
        $currentSize = $this->getDirectorySize($this->uploadPath);
        return ($currentSize + $newFileSize) <= $this->maxStorageSize;
    }
    
    /**
     * Obtener tamaño total del directorio
     */
    private function getDirectorySize($directory) {
        $size = 0;
        $files = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS)
        );
        
        foreach ($files as $file) {
            $size += $file->getSize();
        }
        
        return $size;
    }
    
    /**
     * Configurar headers seguros para descarga
     */
    private function setSecureDownloadHeaders($fileName, $fileSize) {
        // Limpiar cualquier output previo
        if (ob_get_level()) {
            ob_clean();
        }
        
        // Headers de seguridad
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="' . addslashes($fileName) . '"');
        header('Content-Length: ' . $fileSize);
        header('Cache-Control: no-cache, no-store, must-revalidate');
        header('Pragma: no-cache');
        header('Expires: 0');
        header('X-Content-Type-Options: nosniff');
        header('X-Frame-Options: DENY');
        header('X-XSS-Protection: 1; mode=block');
        
        // Prevenir caching en proxies
        header('Surrogate-Control: no-store');
    }
    
    /**
     * Obtener información de archivo de forma segura
     */
    public function getFileInfo($fileName, $userId, $userRole) {
        try {
            if (!in_array($userRole, $this->allowedUsers)) {
                throw new Exception('No tiene permisos para ver información de archivos');
            }
            
            $fileName = basename($fileName);
            $filePath = $this->uploadPath . $fileName;
            
            if (!file_exists($filePath)) {
                throw new Exception('Archivo no encontrado');
            }
            
            return [
                'name' => $fileName,
                'size' => filesize($filePath),
                'modified' => filemtime($filePath),
                'mime_type' => mime_content_type($filePath)
            ];
            
        } catch (Exception $e) {
            throw $e;
        }
    }
    
    /**
     * Limpiar archivos huérfanos (sin referencia en BD)
     */
    public function cleanOrphanedFiles($validFileNames = []) {
        try {
            $files = glob($this->uploadPath . '*.pdf');
            $cleaned = 0;
            
            foreach ($files as $filePath) {
                $fileName = basename($filePath);
                
                // Si no está en la lista de archivos válidos, eliminarlo
                if (!in_array($fileName, $validFileNames)) {
                    if (unlink($filePath)) {
                        $cleaned++;
                        SecurityLogger::logFileAccess(
                            'cleanup',
                            $fileName,
                            null,
                            'system',
                            true,
                            ['reason' => 'orphaned_file']
                        );
                    }
                }
            }
            
            return $cleaned;
            
        } catch (Exception $e) {
            SecurityLogger::logSystemError('Error cleaning orphaned files: ' . $e->getMessage());
            throw $e;
        }
    }
}