<?php

namespace App\Core\Services\Security;

use Exception;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

/**
 * Secure File Upload Service
 * Handles secure file uploads with validation, scanning, and safe storage
 */
class SecureFileUploadService
{
    /**
     * Allowed MIME types for file uploads
     */
    private const ALLOWED_MIME_TYPES = [
        'image/jpeg',
        'image/jpg',
        'image/png',
        'image/gif',
        'image/webp',
        'application/pdf',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx
        'application/vnd.ms-excel',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
    ];

    /**
     * Allowed file extensions
     */
    private const ALLOWED_EXTENSIONS = [
        'jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf',
        'doc', 'docx', 'xls', 'xlsx',
    ];

    /**
     * Blocked file extensions (security risk)
     */
    private const BLOCKED_EXTENSIONS = [
        'php', 'php3', 'php4', 'php5', 'phtml', 'phps',
        'exe', 'bat', 'cmd', 'com', 'scr', 'vbs', 'js',
        'html', 'htm', 'svg', 'sh', 'bash', 'py', 'rb',
        'pl', 'cgi', 'asp', 'aspx', 'jsp',
    ];

    /**
     * Maximum file size in bytes (5MB)
     */
    private const MAX_FILE_SIZE = 5242880; // 5 * 1024 * 1024

    /**
     * Upload a file securely
     *
     * @return array ['success' => bool, 'path' => string|null, 'message' => string]
     */
    public function upload(UploadedFile $file, string $directory = 'uploads', ?string $disk = 'public'): array
    {
        try {
            // Validate file
            $validation = $this->validateFile($file);
            if (! $validation['valid']) {
                return [
                    'success' => false,
                    'path' => null,
                    'message' => $validation['message'],
                ];
            }

            // Generate secure filename
            $filename = $this->generateSecureFilename($file);

            // Store file
            $path = $file->storeAs($directory, $filename, $disk);

            // Verify file was stored correctly
            if (! Storage::disk($disk)->exists($path)) {
                return [
                    'success' => false,
                    'path' => null,
                    'message' => 'File upload failed. Please try again.',
                ];
            }

            return [
                'success' => true,
                'path' => $path,
                'message' => 'File uploaded successfully',
            ];

        } catch (Exception $e) {
            \Log::error('File upload error: '.$e->getMessage());

            return [
                'success' => false,
                'path' => null,
                'message' => 'An error occurred during file upload.',
            ];
        }
    }

    /**
     * Validate uploaded file
     *
     * @return array ['valid' => bool, 'message' => string]
     */
    public function validateFile(UploadedFile $file): array
    {
        // Check file size
        if ($file->getSize() > self::MAX_FILE_SIZE) {
            return [
                'valid' => false,
                'message' => 'File size exceeds maximum allowed size of 5MB.',
            ];
        }

        // Check if file is empty
        if ($file->getSize() === 0) {
            return [
                'valid' => false,
                'message' => 'File is empty.',
            ];
        }

        // Get file extension
        $extension = strtolower($file->getClientOriginalExtension());

        // Check blocked extensions
        if (in_array($extension, self::BLOCKED_EXTENSIONS)) {
            return [
                'valid' => false,
                'message' => 'File type is not allowed for security reasons.',
            ];
        }

        // Check allowed extensions
        if (! in_array($extension, self::ALLOWED_EXTENSIONS)) {
            return [
                'valid' => false,
                'message' => 'File type is not allowed. Allowed types: '.implode(', ', self::ALLOWED_EXTENSIONS),
            ];
        }

        // Verify MIME type
        $mimeType = $file->getMimeType();
        if (! in_array($mimeType, self::ALLOWED_MIME_TYPES)) {
            return [
                'valid' => false,
                'message' => 'File MIME type is not allowed.',
            ];
        }

        // Additional security: Check file content for suspicious patterns
        $contentCheck = $this->scanFileContent($file);
        if (! $contentCheck['safe']) {
            return [
                'valid' => false,
                'message' => $contentCheck['message'] ?? 'File contains potentially malicious content.',
            ];
        }

        return [
            'valid' => true,
            'message' => 'File is valid.',
        ];
    }

    /**
     * Scan file content for malicious patterns
     *
     * @return array ['safe' => bool, 'message' => string|null]
     */
    private function scanFileContent(UploadedFile $file): array
    {
        // For images, verify they are actual images
        if (str_starts_with($file->getMimeType(), 'image/')) {
            $imageInfo = @getimagesize($file->getRealPath());
            if ($imageInfo === false) {
                return [
                    'safe' => false,
                    'message' => 'File is not a valid image.',
                ];
            }
        }

        // Check for PHP tags in file content (even if extension is different)
        $content = file_get_contents($file->getRealPath(), false, null, 0, 8192); // Read first 8KB
        $suspiciousPatterns = [
            '<?php',
            '<?=',
            '<script',
            'eval(',
            'base64_decode',
            'exec(',
            'system(',
            'shell_exec',
            'passthru',
        ];

        foreach ($suspiciousPatterns as $pattern) {
            if (stripos($content, $pattern) !== false) {
                return [
                    'safe' => false,
                    'message' => 'File contains potentially malicious code.',
                ];
            }
        }

        return [
            'safe' => true,
            'message' => null,
        ];
    }

    /**
     * Generate secure filename
     */
    private function generateSecureFilename(UploadedFile $file): string
    {
        $extension = strtolower($file->getClientOriginalExtension());

        // Generate random hash filename to prevent directory traversal and guessing
        return Str::random(40).'.'.$extension;
    }

    /**
     * Delete uploaded file
     */
    public function delete(string $path, string $disk = 'public'): bool
    {
        try {
            if (Storage::disk($disk)->exists($path)) {
                return Storage::disk($disk)->delete($path);
            }

            return true;
        } catch (Exception $e) {
            \Log::error('File delete error: '.$e->getMessage());

            return false;
        }
    }
}
