# Media System Documentation

## Overview

نظام الميديا في ZADA يوفر طريقة موحدة لرفع وإدارة الملفات (صور، فيديوهات، ملفات صوتية، PDFs، وملفات عامة) وربطها بأي موديل في النظام عبر Polymorphic Relationship.

---

## Database Structure

### جدول `media`

| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary Key |
| `model_id` | UUID | ID الموديل المرتبط (nullable) |
| `model_type` | string | نوع الموديل (e.g., `App\Models\Users\User`) |
| `name` | string | اسم الملف المحفوظ |
| `type` | string | نوع الملف (image, video, audio, pdf, file) |
| `extension` | string | امتداد الملف (jpg, mp4, etc.) |
| `size` | string | حجم الملف بالبايت |
| `path` | string | مسار المجلد (images, videos, audio, files) |
| `disk` | string | Storage disk (default: public) |
| `option` | string | تصنيف الميديا (avatar, logo, gallery, etc.) |
| `is_attached` | boolean | هل مرتبط بموديل |
| `uploaded_by` | UUID | ID المستخدم الذي رفع الملف |
| `width` | string | عرض الصورة/الفيديو (nullable) |
| `height` | string | ارتفاع الصورة/الفيديو (nullable) |
| `duration` | string | مدة الفيديو/الصوت (nullable) |
| `quality` | string | جودة الضغط (nullable) |
| `has_thumbnail` | boolean | هل يوجد thumbnail |
| `thumbnail_name` | string | اسم ملف الـ thumbnail |
| `has_hls` | boolean | هل يوجد HLS streams |
| `hls_name` | string | اسم ملف HLS الرئيسي |
| `hls_240p_name` | string | HLS 240p |
| `hls_360p_name` | string | HLS 360p |
| `hls_480p_name` | string | HLS 480p |
| `hls_720p_name` | string | HLS 720p |
| `hls_1080p_name` | string | HLS 1080p |
| `metadata` | JSON | بيانات إضافية |

### جدول `media_translations`

| Column | Type | Description |
|--------|------|-------------|
| `id` | bigint | Primary Key |
| `media_id` | UUID | Foreign Key |
| `locale` | string | اللغة (ar, en) |
| `alt` | string | النص البديل للصورة |

---

## Model Setup

### استخدام HasMedia Trait

لإضافة دعم الميديا لأي موديل:

```php
<?php

namespace App\Models;

use App\Traits\HasMedia;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasMedia;

    protected array $mediaColumns = [
        'image' => [
            'option' => 'image',
            'type' => 'image',
            'is_single' => true,
            'default' => 'default-product.png',
        ],
        'gallery' => [
            'option' => 'gallery',
            'type' => 'image',
            'is_single' => false,
            'default' => 'default-image.png',
        ],
    ];
}
```

### خصائص mediaColumns

| Property | Type | Description |
|----------|------|-------------|
| `option` | string | اسم فريد للتصنيف (يُستخدم في الـ API) |
| `type` | string | نوع الميديا المتوقع |
| `is_single` | boolean | `true` = ملف واحد فقط، `false` = ملفات متعددة |
| `default` | string | الصورة الافتراضية في حالة عدم وجود ميديا |

### الوصول للميديا في الموديل

```php
$user = User::find($id);

// الحصول على الـ avatar (is_single = true)
$avatar = $user->avatar;
// Returns:
// [
//     'id' => 'uuid',
//     'path' => 'https://domain.com/storage/images/filename.jpg',
//     'type' => 'image',
//     'option' => 'avatar',
//     'model_id' => 'user-uuid',
//     'model_type' => 'App\Models\Users\User',
//     'is_single' => true,
// ]

// الحصول على الـ gallery (is_single = false)
$gallery = $product->gallery;
// Returns: Collection of media items
```

---

## API Endpoints

### 1. Upload File

**Endpoint:** `POST /api/attachments`

**Headers:**
```
Authorization: Bearer {token}
Content-Type: multipart/form-data
```

**Request Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `file` | file | ✅ | الملف المراد رفعه |
| `media_type` | string | ✅ | نوع الملف: `image`, `video`, `audio`, `pdf`, `file` |
| `model` | string | ✅ | مسار الموديل (e.g., `Users/User`, `Products/Product`) |
| `model_id` | string | ❌ | UUID الموديل (nullable للرفع المؤقت) |
| `option` | string | ✅ | تصنيف الميديا (e.g., `avatar`, `logo`, `gallery`) |
| `is_single` | boolean | ✅ | `true` لملف واحد، `false` لملفات متعددة |
| `id` | string | ❌ | UUID ميديا موجودة للتحديث |
| `deleted_media` | string | ❌ | UUID ميديا لحذفها |
| `width` | number | ❌ | عرض الصورة المطلوب (للصور فقط) |
| `height` | number | ❌ | ارتفاع الصورة المطلوب (للصور فقط) |
| `quality` | number | ❌ | جودة الضغط 1-100 (default: 85) |
| `generate_thumbnail` | boolean | ❌ | توليد thumbnail (للفيديو و PDF) |
| `generate_slh` | boolean | ❌ | توليد HLS streams (للفيديو) |

**Success Response (200):**
```json
{
    "status": "success",
    "message": "File uploaded successfully",
    "data": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "path": "https://domain.com/storage/images/1703001234_abc123.jpg",
        "type": "image",
        "option": "avatar",
        "model_id": "user-uuid",
        "model_type": "App\\Models\\Users\\User",
        "is_single": true
    }
}
```

### 2. Delete File

**Endpoint:** `DELETE /api/attachments/{id}`

**Headers:**
```
Authorization: Bearer {token}
```

**Success Response (200):**
```json
{
    "status": "success",
    "message": "Deleted successfully",
    "data": null
}
```

### 3. Get Available Models

**Endpoint:** `GET /api/attachments`

**Headers:**
```
Authorization: Bearer {token}
```

**Response:** قائمة الموديلات المتاحة للرفع

---

## Usage Scenarios

### 1. رفع صورة جديدة (Avatar)

```javascript
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('media_type', 'image');
formData.append('model', 'Users/User');
formData.append('model_id', 'user-uuid-here');
formData.append('option', 'avatar');
formData.append('is_single', true);
formData.append('quality', 85);

const response = await fetch('/api/attachments', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`
    },
    body: formData
});
```

### 2. تحديث صورة موجودة

```javascript
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('media_type', 'image');
formData.append('model', 'Users/User');
formData.append('model_id', 'user-uuid-here');
formData.append('option', 'avatar');
formData.append('is_single', true);
formData.append('id', 'existing-media-uuid'); // ID الميديا الموجودة

const response = await fetch('/api/attachments', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`
    },
    body: formData
});
```

### 3. رفع ملف مع حذف القديم

```javascript
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('media_type', 'image');
formData.append('model', 'Companies/Company');
formData.append('model_id', 'company-uuid');
formData.append('option', 'logo');
formData.append('is_single', true);
formData.append('deleted_media', 'old-media-uuid'); // حذف الميديا القديمة

const response = await fetch('/api/attachments', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`
    },
    body: formData
});
```

### 4. رفع ملفات متعددة (Gallery)

```javascript
// رفع كل ملف على حدة
for (const file of fileInput.files) {
    const formData = new FormData();
    formData.append('file', file);
    formData.append('media_type', 'image');
    formData.append('model', 'Products/Product');
    formData.append('model_id', 'product-uuid');
    formData.append('option', 'gallery');
    formData.append('is_single', false); // متعددة

    await fetch('/api/attachments', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${token}`
        },
        body: formData
    });
}
```

### 5. رفع فيديو مع Thumbnail و HLS

```javascript
const formData = new FormData();
formData.append('file', videoFile);
formData.append('media_type', 'video');
formData.append('model', 'Courses/Lesson');
formData.append('model_id', 'lesson-uuid');
formData.append('option', 'video');
formData.append('is_single', true);
formData.append('generate_thumbnail', true);
formData.append('generate_slh', true); // HLS streaming

const response = await fetch('/api/attachments', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`
    },
    body: formData
});
```

### 6. رفع ملف مؤقت (Unattached)

```javascript
// رفع بدون model_id - للاستخدام في forms قبل حفظ الموديل
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('media_type', 'image');
formData.append('model', 'Products/Product');
// model_id غير موجود
formData.append('option', 'image');
formData.append('is_single', true);

const response = await fetch('/api/attachments', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`
    },
    body: formData
});

// احفظ الـ media ID واستخدمه لاحقاً لربطه بالموديل
const mediaId = response.data.id;
```

---

## File Storage Structure

```
storage/app/public/
├── images/
│   ├── 1703001234_abc123.jpg
│   ├── 1703001235_def456.png
│   └── thumbnails/
│       └── thumb_1703001234.jpg
├── videos/
│   ├── 1703001236_video123.mp4
│   ├── thumbnails/
│   │   └── thumb_1703001236.jpg
│   └── hls/
│       ├── video123_240p.m3u8
│       ├── video123_360p.m3u8
│       ├── video123_480p.m3u8
│       ├── video123_720p.m3u8
│       └── video123_1080p.m3u8
├── audio/
│   └── 1703001237_audio123.mp3
└── files/
    ├── 1703001238_document.pdf
    └── thumbnails/
        └── thumb_1703001238.jpg
```

---

## Models with HasMedia

### User
```php
protected array $mediaColumns = [
    'avatar' => [
        'option' => 'avatar',
        'type' => 'image',
        'is_single' => true,
        'default' => 'default-avatar.png',
    ],
];
```

### Company
```php
protected array $mediaColumns = [
    'logo' => [
        'option' => 'logo',
        'type' => 'image',
        'is_single' => true,
        'default' => 'default-logo-dark.png',
    ],
];
```

### Announcement
```php
protected array $mediaColumns = [
    'media' => [
        'option' => 'media',
        'type' => 'image',
        'is_single' => true,
        'default' => 'default-announcement.png',
    ],
];
```

### SupportTicket
```php
protected array $mediaColumns = [
    'image' => [
        'option' => 'image',
        'type' => 'image',
        'is_single' => true,
        'default' => 'default-ticket.png',
    ],
];
```

### SupportMessage
```php
protected array $mediaColumns = [
    'attachment' => [
        'option' => 'attachment',
        'type' => 'file',
        'is_single' => true,
        'default' => null,
    ],
];
```

---

## Validation Rules

### Media Types المدعومة
- `image` - صور (jpg, png, gif, webp, etc.)
- `video` - فيديوهات (mp4, mov, avi, etc.)
- `audio` - ملفات صوتية (mp3, wav, etc.)
- `pdf` - ملفات PDF
- `file` - أي نوع ملفات آخر

### Option Validation
الـ `option` يجب أن يكون موجود في `mediaOptions()` الخاصة بالموديل:

```php
// في الموديل
public static function mediaOptions(): array
{
    // يتم توليدها تلقائياً من mediaColumns
    return ['avatar']; // للـ User
    return ['logo']; // للـ Company
    return ['image', 'gallery']; // للـ Product
}
```

---

## Error Handling

### الأخطاء الشائعة

| Error | Cause | Solution |
|-------|-------|----------|
| `No file provided` | لم يتم إرسال ملف | تأكد من إرسال الملف في `file` field |
| `Invalid model` | مسار الموديل غير صحيح | تأكد من المسار مثل `Users/User` |
| `The selected option is invalid` | الـ option غير موجود في الموديل | تأكد من وجود الـ option في `mediaColumns` |
| `The selected model_id does not exist` | الـ model_id غير موجود | تأكد من صحة UUID الموديل |

---

## Media Model Methods

```php
$media = Media::find($id);

// الحصول على URL الكامل
$url = $media->url;
// https://domain.com/storage/images/filename.jpg

// الحصول على URL الـ Thumbnail
$thumbnailUrl = $media->thumbnail_url;
// https://domain.com/storage/images/thumbnails/thumb_filename.jpg
```

---

## Cleanup Behavior

عند حذف ميديا (سواء يدوياً أو عند تحديث ملف موجود):

1. ✅ يتم حذف الملف الرئيسي
2. ✅ يتم حذف الـ Thumbnail (إن وجد)
3. ✅ يتم حذف جميع ملفات HLS (إن وجدت)
4. ✅ يتم حذف السجل من قاعدة البيانات

---

## Best Practices

1. **استخدم `is_single: true`** للملفات الفردية مثل avatar و logo
2. **استخدم `is_single: false`** للـ galleries والملفات المتعددة
3. **حدد `quality`** للصور لتقليل الحجم (85 قيمة جيدة)
4. **استخدم `generate_thumbnail`** للفيديوهات والـ PDFs
5. **استخدم `generate_slh`** للفيديوهات الطويلة لدعم Adaptive Streaming
6. **لا ترسل `model_id`** للرفع المؤقت في الـ forms

---

## Configuration

### Storage Disk
الـ disk الافتراضي هو `public`. تأكد من عمل symbolic link:

```bash
php artisan storage:link
```

### إضافة موديل جديد للـ ModelsService
لإضافة موديل جديد يدعم رفع الملفات، أضفه في `ModelsService::paths()`.
