<?php

namespace App\Core;

/**
 * File Upload Validator - التحقق من أمان الملفات المرفوعة
 */
class FileUploadValidator
{
    private const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
    private const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    private const ALLOWED_DOCUMENT_TYPES = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
    
    // Magic bytes للتحقق من نوع الملف الفعلي
    private const MAGIC_BYTES = [
        'image/jpeg' => ["\xFF\xD8\xFF"],
        'image/png' => ["\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"],
        'image/gif' => ["GIF87a", "GIF89a"],
        'image/webp' => ["RIFF"],
        'application/pdf' => ["%PDF"],
    ];

    /**
     * التحقق من الملف المرفوع
     * 
     * @param array $file $_FILES array element
     * @param array $allowedTypes أنواع الملفات المسموحة
     * @param int $maxSize الحد الأقصى للحجم بالبايت
     * @return array ['valid' => bool, 'errors' => array]
     */
    public static function validate(array $file, array $allowedTypes = [], int $maxSize = self::MAX_FILE_SIZE): array
    {
        $errors = [];

        // التحقق من وجود الملف
        if (!isset($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) {
            $errors[] = 'الملف غير صالح أو لم يتم رفعه بشكل صحيح';
            return ['valid' => false, 'errors' => $errors];
        }

        // التحقق من أخطاء الرفع
        if ($file['error'] !== UPLOAD_ERR_OK) {
            $errors[] = self::getUploadErrorMessage($file['error']);
            return ['valid' => false, 'errors' => $errors];
        }

        // التحقق من الحجم
        if ($file['size'] > $maxSize) {
            $errors[] = 'حجم الملف كبير جداً. الحد الأقصى: ' . self::formatBytes($maxSize);
        }

        if ($file['size'] === 0) {
            $errors[] = 'الملف فارغ';
        }

        // التحقق من نوع الملف (MIME type)
        $mimeType = $file['type'] ?? mime_content_type($file['tmp_name']);
        if (empty($allowedTypes)) {
            $allowedTypes = array_merge(self::ALLOWED_IMAGE_TYPES, self::ALLOWED_DOCUMENT_TYPES);
        }

        if (!in_array($mimeType, $allowedTypes, true)) {
            $errors[] = 'نوع الملف غير مسموح: ' . $mimeType;
        }

        // التحقق من محتوى الملف الفعلي (Magic Bytes)
        $actualType = self::detectFileType($file['tmp_name']);
        if ($actualType && !in_array($actualType, $allowedTypes, true)) {
            $errors[] = 'نوع الملف الفعلي لا يطابق المعلن عنه';
        }

        // التحقق من امتداد الملف
        $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        $allowedExtensions = self::getExtensionsForTypes($allowedTypes);
        if (!in_array($extension, $allowedExtensions, true)) {
            $errors[] = 'امتداد الملف غير مسموح: .' . $extension;
        }

        // فحص إضافي للصور
        if (strpos($mimeType, 'image/') === 0) {
            $imageValidation = self::validateImage($file['tmp_name']);
            if (!$imageValidation['valid']) {
                $errors = array_merge($errors, $imageValidation['errors']);
            }
        }

        return [
            'valid' => empty($errors),
            'errors' => $errors,
            'detected_type' => $actualType ?? $mimeType
        ];
    }

    /**
     * التحقق من صحة الصورة
     */
    private static function validateImage(string $filePath): array
    {
        $errors = [];

        // محاولة فتح الصورة
        $imageInfo = @getimagesize($filePath);
        if ($imageInfo === false) {
            $errors[] = 'الملف ليس صورة صالحة';
            return ['valid' => false, 'errors' => $errors];
        }

        // التحقق من الأبعاد (منع الصور الكبيرة جداً)
        $maxWidth = 5000;
        $maxHeight = 5000;
        if ($imageInfo[0] > $maxWidth || $imageInfo[1] > $maxHeight) {
            $errors[] = "أبعاد الصورة كبيرة جداً. الحد الأقصى: {$maxWidth}x{$maxHeight}";
        }

        // التحقق من أن الصورة ليست فارغة
        if ($imageInfo[0] === 0 || $imageInfo[1] === 0) {
            $errors[] = 'الصورة فارغة أو غير صالحة';
        }

        return [
            'valid' => empty($errors),
            'errors' => $errors,
            'width' => $imageInfo[0] ?? null,
            'height' => $imageInfo[1] ?? null
        ];
    }

    /**
     * اكتشاف نوع الملف من محتواه (Magic Bytes)
     */
    private static function detectFileType(string $filePath): ?string
    {
        if (!file_exists($filePath) || !is_readable($filePath)) {
            return null;
        }

        $handle = @fopen($filePath, 'rb');
        if (!$handle) {
            return null;
        }

        $bytes = fread($handle, 12);
        fclose($handle);

        if ($bytes === false || strlen($bytes) < 4) {
            return null;
        }

        foreach (self::MAGIC_BYTES as $mimeType => $signatures) {
            foreach ($signatures as $signature) {
                if (strpos($bytes, $signature) === 0) {
                    // فحص خاص لـ WebP
                    if ($mimeType === 'image/webp' && strpos($bytes, 'WEBP') === false) {
                        continue;
                    }
                    return $mimeType;
                }
            }
        }

        return null;
    }

    /**
     * جلب امتدادات الملفات لأنواع MIME معينة
     */
    private static function getExtensionsForTypes(array $types): array
    {
        $map = [
            'image/jpeg' => ['jpg', 'jpeg'],
            'image/png' => ['png'],
            'image/gif' => ['gif'],
            'image/webp' => ['webp'],
            'application/pdf' => ['pdf'],
            'application/msword' => ['doc'],
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => ['docx'],
        ];

        $extensions = [];
        foreach ($types as $type) {
            if (isset($map[$type])) {
                $extensions = array_merge($extensions, $map[$type]);
            }
        }

        return array_unique($extensions);
    }

    /**
     * رسالة خطأ رفع الملف
     */
    private static function getUploadErrorMessage(int $errorCode): string
    {
        $messages = [
            UPLOAD_ERR_INI_SIZE => 'الملف يتجاوز الحد المسموح في إعدادات PHP',
            UPLOAD_ERR_FORM_SIZE => 'الملف يتجاوز الحد المسموح في النموذج',
            UPLOAD_ERR_PARTIAL => 'تم رفع الملف جزئياً فقط',
            UPLOAD_ERR_NO_FILE => 'لم يتم رفع أي ملف',
            UPLOAD_ERR_NO_TMP_DIR => 'مجلد الملفات المؤقتة مفقود',
            UPLOAD_ERR_CANT_WRITE => 'فشل كتابة الملف على القرص',
            UPLOAD_ERR_EXTENSION => 'تم إيقاف الرفع بواسطة إضافة PHP',
        ];

        return $messages[$errorCode] ?? 'خطأ غير معروف في رفع الملف';
    }

    /**
     * تنسيق حجم الملف
     */
    private static function formatBytes(int $bytes, int $precision = 2): string
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        $bytes /= pow(1024, $pow);
        return round($bytes, $precision) . ' ' . $units[$pow];
    }

    /**
     * إنشاء اسم ملف آمن
     */
    public static function generateSafeFileName(string $originalName, int $userId = 0): string
    {
        $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
        $name = pathinfo($originalName, PATHINFO_FILENAME);
        
        // تنظيف الاسم
        $name = preg_replace('/[^a-z0-9_-]/i', '_', $name);
        $name = preg_replace('/_+/', '_', $name);
        $name = trim($name, '_');
        
        // إذا كان الاسم فارغاً، استخدم timestamp
        if (empty($name)) {
            $name = 'file_' . time();
        }
        
        // إضافة معرف المستخدم و timestamp
        $safeName = ($userId > 0 ? $userId . '_' : '') . $name . '_' . time() . '.' . $extension;
        
        return $safeName;
    }

    /**
     * التحقق من أن الملف ليس PHP script
     */
    public static function isNotPhpFile(string $filePath): bool
    {
        $content = @file_get_contents($filePath, false, null, 0, 1024);
        if ($content === false) {
            return false;
        }

        // البحث عن علامات PHP
        $phpSignatures = ['<?php', '<?=', '<? ', '<script language="php">'];
        foreach ($phpSignatures as $signature) {
            if (stripos($content, $signature) !== false) {
                return false;
            }
        }

        return true;
    }
}
