# توثيق وحدة الوسائط (Media Module Documentation)

هذا المستند يقدم شرحاً تفصيلياً وشاملاً لوحدة `Media` في مشروع `newtouch-server`. تُعنى هذه الوحدة بإدارة كافة الملفات والوسائط (صور، مستندات، فيديو) في النظام، وتوفر آلية آمنة ومرنة لرفع الملفات، تخزينها، معالجتها، وربطها بالكيانات المختلفة.

---

## 1. المفهوم العام وفلسفة العمل (Concept & Philosophy)

تتبنى وحدة الوسائط منهجية **"الرفع المرحلي" (Staging Process)** لضمان نظافة البيانات وأمان النظام.

### المشكلة التقليدية:
في الأنظمة التقليدية، عندما يرفع المستخدم ملفاً في فورم (Form) لإنشاء "فرصة جديدة"، يتم رفع الملف فوراً وربطه بكيان غير موجود بعد (لأن الفرصة لم تُحفظ)، أو يتم تأجيل الرفع حتى حفظ الفرصة مما يسبب بطء في تجربة المستخدم. وإذا ألغى المستخدم العملية، تبقى "ملفات يتيمة" (Orphan Files) تستهلك مساحة التخزين.

### الحل في Media Module:
1.  **المرحلة المؤقتة (Staging):** عند اختيار ملف، يتم رفعه فوراً إلى منطقة مؤقتة (Staging Area). لا يرتبط بأي كيان حقيقي بعد، بل بكيان وهمي يسمى `TemporaryMediaSubject`.
2.  **سياق الرفع (Upload Context):** يتم توليد `context_uuid` في الواجهة الأمامية (Frontend) وإرساله مع كل ملف. هذا الرمز يربط مجموعة ملفات بعملية واحدة.
3.  **المطالبة (Claiming):** عند حفظ الفرصة النهائية، يرسل النظام الـ `context_uuid`. تقوم وحدة الوسائط بالبحث عن كل الملفات المؤقتة التي تحمل هذا الرمز، وتقوم بـ "نقل ملكيتها" من الكيان الوهمي إلى الفرصة الحقيقية التي تم إنشاؤها للتو.
4.  **التنظيف التلقائي:** أي ملفات بقيت في الـ Staging لفترة معينة (مثلاً 24 ساعة) دون أن يتم "المطالبة بها" (Claiming) تعتبر مهملة ويتم حذفها تلقائياً بواسطة وظيفة جدولة (Cron Job).

---

## 2. هيكلية قاعدة البيانات (Database Schema)

يعتمد الموديول على جدول واحد رئيسي `media` (مبني على مكتبة Spatie Media Library مع تخصيصات).

### جدول `media`

| اسم الحقل (Field) | النوع (Type) | الوصف التفصيلي (Description) |
| :--- | :--- | :--- |
| `id` | `BigInteger` | المفتاح الأساسي التسلسلي للجدول (Auto-increment). |
| `model_type` | `string` | **علاقة متعددة الأشكال (Polymorphic):** اسم الكلاس للكيان المالك للملف (مثلاً `Modules\Opportunity\Models\Opportunity`). في حالة التخزين المؤقت يكون `Modules\Media\Models\TemporaryMediaSubject`. |
| `model_id` | `BigInteger` | **علاقة متعددة الأشكال:** معرف الكيان المالك (ID). |
| `uuid` | `UUID` | معرف فريد عالمياً للملف. يستخدم في الـ APIs للتعامل مع الملف بدلاً من الـ ID لزيادة الأمان. |
| `collection_name` | `string` | اسم المجموعة التي ينتمي إليها الملف (مثلاً: `profile_picture`, `contract_documents`, `staged_files`). يساعد في تنظيم ملفات الكيان الواحد. |
| `name` | `string` | الاسم البشري للملف (قابل للتعديل ولا يؤثر على اسم الملف الفعلي). |
| `file_name` | `string` | الاسم الفعلي للملف على القرص (الخادم أو S3). يتم تنظيفه وتوليده تلقائياً لضمان الأمان وعدم التكرار. |
| `mime_type` | `string` | نوع الملف (مثلاً: `image/jpeg`, `application/pdf`). |
| `disk` | `string` | اسم القرص الذي تم التخزين عليه (كما هو معرف في `config/filesystems.php`). مثلاً `public`, `s3`, `local`. |
| `conversions_disk` | `string` | القرص الذي تخزن عليه النسخ المصغرة أو المعالجة (Thumbnails/Conversions). غالباً يكون نفس الـ `disk`. |
| `size` | `UnsignedBigInt` | حجم الملف بالبايت. |
| `manipulations` | `JSON` | يخزن أي تعديلات تمت على الصور (قص، تدوير) ليتم إعادة تطبيقها إذا لزم الأمر. |
| `custom_properties` | `JSON` | **حقل الجوكر:** يخزن أي بيانات إضافية. هنا يتم تخزين بيانات الـ Staging (`context_uuid`, `staged_by_user_uuid`). |
| `generated_conversions` | `JSON` | مصفوفة تخزن أسماء النسخ المصغرة التي تم توليدها وتأكيد وجودها. |
| `responsive_images` | `JSON` | تخزن بيانات الصور المتجاوبة (Responsive Images) لدعم شاشات العرض المختلفة. |
| `order_column` | `Integer` | لترتيب الملفات داخل نفس المجموعة (Collection). |
| `created_at` | `DateTime` | تاريخ الرفع. |
| `updated_at` | `DateTime` | تاريخ آخر تعديل. |

---

## 3. العمليات والأكواد (Processes & Actions)

### أ. عملية الرفع المرحلي `StageMediaAction`
هنا تبدأ رحلة الملف. يتم استدعاء هذا الأكشن عند استخدام الـ Endpoint الخاص بالرفع.

**خطوات التنفيذ:**
1.  استلام الملفات ومُعرف المستخدم (`user_uuid`) وسياق الرفع (`context_uuid`).
2.  تحديد قرص التخزين المؤقت (`stagedFilesDisk`).
3.  استدعاء `mediaRepository->storeStagedFile` لكل ملف:
    *   يتم إنشاء كائن وهمي `TemporaryMediaSubject` (بـ ID ثابت = 1).
    *   يتم إضافة الملف لهذا الكائن.
    *   **الأهم:** يتم حقن البيانات التالية في `custom_properties`:
        *   `staged_by_user_uuid`: من رفع الملف؟
        *   `context_uuid`: مفتاح الربط المستقبلي.
        *   `intended_collection_name`: أين يجب أن يذهب الملف لاحقاً؟
        *   `model_type_alias`: نوع الكيان المستقبلي.
4.  يتم إطلاق حدث `MediaStoredEvent`.

### ب. عملية المطالبة بالملف `claimStagedFile` (داخل الـ Repository)
هذه الدالة تستخدمها الموديولات الأخرى (مثل Opportunity Module) عند حفظ بياناتها.

**المنطق:**
1.  تبحث عن الملف في جدول `media` بشرط:
    *   `uuid` الملف مطابق.
    *   المجموعة هي `staged_files`.
    *   المالك هو `TemporaryMediaSubject`.
    *   `context_uuid` و `staged_by_user_uuid` مطابقين لما تم إرساله (للحماية الأمنية).
2.  تقوم بعملية "نقل" (`move`) للملف:
    *   تغيير `model_type` و `model_id` ليشيرا إلى الكيان الجديد (الفرصة الحقيقية).
    *   تغيير `collection_name` إلى المجموعة النهائية (مثلاً `contract_documents`).
    *   نقل الملف فيزيائياً من مجلد الـ Temp إلى المجلد الدائم (إذا اختلف القرص أو المسار).
3.  يتم مسح خصائص الـ Staging من `custom_properties` لأنها لم تعد لازمة.

### ج. Domain Entity: `MediaEntity`
هو كائن يمثل الملف في طبقة التطبيق (Application Layer). ميزته أنه `Immutable` (غير قابل للتغيير) ويحتوي على دوال مساعدة ذكية:
*   `getUrl()`: يعيد الرابط العام.
*   `getTemporaryUrl()`: يولد رابط موقع (Signed URL) للملفات الخاصة، صالح لمدة محددة.
*   `getCustomProperty('key')`: لاسترجاع الخصائص المخصصة بسهولة.

---

## 4. واجهة برمجة التطبيقات (API Reference)

### 1. رفع ملفات (مرحلي)
**Endpoint:** `POST /api/v1/media/stage`

يقوم برفع ملف أو أكثر إلى المنطقة المؤقتة.

**Request (Multipart/Check-data):**
*   `files[]`: الملفات المراد رفعها.
*   `context_uuid`: معرف جلسة الرفع (يولده الفرونت إند).
*   `intended_collection_name`: اسم المجموعة المستهدفة (مثلاً `documents`).
*   `model_type_alias`: اختصار لنوع الكيان (مثلاً `opportunity`).

**Response Example:**
```json
{
    "data": [
        {
            "uuid": "550e8400-e29b-41d4-a716-446655440000",
            "name": "contract.pdf",
            "url": "http://api.domain.com/storage/temp/contract.pdf",
            "mime_type": "application/pdf",
            "size": 102400,
            "collection_name": "staged_files"
        }
    ],
    "message": "Files batch staging process completed."
}
```

### 2. توليد رابط مؤقت
**Endpoint:** `GET /api/v1/media/{mediaUuid}/temporary-url`

للوصول للملفات المحمية (Private Files) التي لا يمكن الوصول لها برابط مباشر.

**Request Query Params:**
*   `expires_in_minutes`: مدة صلاحية الرابط (اختياري، الافتراضي 5 دقائق).

**Response:**
```json
{
    "data": {
        "url": "https://s3.amazonaws.com/bucket/file.pdf?signature=xyz..."
    }
}
```

### 3. حذف ملف
**Endpoint:** `DELETE /api/v1/media/{mediaUuid}`

يحذف الملف وسجله من قاعدة البيانات. يجب أن يكون المستخدم هو المالك أو لديه صلاحية.

### 4. تحديث خصائص
**Endpoint:** `PUT /api/v1/media/{mediaUuid}`

يستخدم لتعديل اسم الملف (Display Name) أو الخصائص المخصصة.

**Request Body:**
```json
{
    "name": "العقد النهائي الموقع",
    "custom_properties": {
        "is_reviewed": true
    }
}
```

---

## 5. أمثلة بيانات حقيقية (Real Data Examples)

### مثال 1: ملف في مرحلة الـ Staging
هكذا يبدو السجل في قاعدة البيانات فور الرفع.

```json
{
  "id": 1050,
  "model_type": "Modules\\Media\\Infrastructure\\Persistence\\Eloquent\\Models\\TemporaryMediaSubject",
  "model_id": 1,
  "uuid": "a1b2c3d4-...",
  "collection_name": "staged_files",
  "name": "ID_Card.png",
  "file_name": "id-card-12345.png",
  "disk": "local_temp",
  "custom_properties": {
    "staged_by_user_uuid": "user-uuid-888",
    "context_uuid": "ctx-uuid-999",
    "intended_collection_name": "identity_proof",
    "original_client_name": "ID_Card.png",
    "staged_at": "2024-10-01T10:00:00"
  }
}
```

### مثال 2: ملف بعد المطالبة (Claimed)
هكذا يتحول السجل بعد حفظ الفرصة وربط الملف بها.

```json
{
  "id": 1050,
  "model_type": "Modules\\Opportunity\\Models\\Opportunity",
  "model_id": 500,  // <-- تغير المالك إلى الفرصة رقم 500
  "uuid": "a1b2c3d4-...",
  "collection_name": "identity_proof", // <-- تغيرت المجموعة
  "name": "ID_Card.png",
  "disk": "s3_private", // <-- (اختياري) قد ينقل لقرص آخر
  "custom_properties": {
    "claimed_at": "2024-10-01T10:05:00",
    "original_client_name_at_staging": "ID_Card.png"
    // تم حذف context_uuid لأنه أدى وظيفته
  }
}
```

---

## الخلاصة

وحدة الوسائط في `Newtouch Server` توفر بنية تحتية صلبة وآمنة لإدارة الملفات. استخدام نمط الـ **Staging** يحل مشكلة معقدة في إدارة الحالة (State Management) بين الواجهة الأمامية والخلفية، ويمنع تراكم الملفات غير المستخدمة، مما يضمن أداءً عالياً ونظاماً نظيفاً على المدى الطويل.
