# Media Attachment Flow - Complete Explanation

## 📊 Media Table Structure

```sql
media table:
- id (UUID)
- model_type (nullable) ← Which model? (App\Models\Profile, App\Models\Product, etc.)
- model_id (nullable)   ← Which record? (profile id, product id, etc.)
- name                  ← File name on disk
- path                  ← Storage path
- option (nullable)     ← Which field? (logo, commercial_register_file, etc.)
- is_attached (boolean) ← Is it linked to a model?
- uploaded_by          ← Who uploaded it
```

## 🔄 Two-Stage Process

### Stage 1: Upload File (Unattached)

```bash
POST /api/media
file: logo.jpg
model: Profile  ← This is just metadata, NOT attachment yet!
media_type: image
```

**What happens:**
```php
// MediaController creates media record
Media::create([
    'id'          => 'abc123-uuid',
    'name'        => 'logo_abc123.jpg',
    'path'        => 'images',
    'type'        => 'image',
    'model_type'  => 'App\Models\Profile',  // ← Just a hint!
    'model_id'    => null,                   // ← NOT attached yet!
    'option'      => null,                   // ← No option yet!
    'is_attached' => false,                  // ← NOT attached!
    'uploaded_by' => auth()->id(),
]);
```

**Media table after upload:**
```
┌──────────┬─────────────────────┬──────────┬────────┬──────────────┐
│ id       │ model_type          │ model_id │ option │ is_attached  │
├──────────┼─────────────────────┼──────────┼────────┼──────────────┤
│ abc123   │ App\Models\Profile  │ NULL     │ NULL   │ false        │
└──────────┴─────────────────────┴──────────┴────────┴──────────────┘
```

**Status:** ✅ File uploaded, ❌ NOT attached to any model

---

### Stage 2: Attach to Model

Now you have two options:

#### Option A: Direct Attachment to Profile

```php
// Update profile directly
$profile->update([
    'logo' => 'abc123-uuid',  // ← Media ID in request
    'commercial_register_number' => 'CR-123',
]);
```

**What happens:**
```php
// UploadMediaObserver::saved() is triggered
// 1. Sees 'logo' in request
// 2. Finds media with id 'abc123-uuid'
// 3. Deletes old logo media (if exists)
// 4. Attaches new media to profile

Media::find('abc123-uuid')->update([
    'model_id'    => $profile->id,        // ← NOW attached to profile!
    'model_type'  => 'App\Models\Profile',
    'option'      => 'logo',              // ← Knows it's the logo!
    'is_attached' => true,                // ← NOW attached!
]);
```

**Media table after attachment:**
```
┌──────────┬─────────────────────┬──────────┬────────┬──────────────┐
│ id       │ model_type          │ model_id │ option │ is_attached  │
├──────────┼─────────────────────┼──────────┼────────┼──────────────┤
│ abc123   │ App\Models\Profile  │ 5        │ logo   │ true         │
└──────────┴─────────────────────┴──────────┴────────┴──────────────┘
```

**Status:** ✅ File uploaded, ✅ Attached to Profile (id=5)

---

#### Option B: Via ProfileUpdateRequest (Our Case)

**Step 1: Vendor uploads logo**
```bash
POST /api/media
file: logo.jpg
```

**Media table:**
```
┌──────────┬─────────────────────┬──────────┬────────┬──────────────┐
│ id       │ model_type          │ model_id │ option │ is_attached  │
├──────────┼─────────────────────┼──────────┼────────┼──────────────┤
│ abc123   │ App\Models\Profile  │ NULL     │ NULL   │ false        │
└──────────┴─────────────────────┴──────────┴────────┴──────────────┘
```

**Step 2: Vendor creates ProfileUpdateRequest**
```php
ProfileUpdateRequest::create([
    'vendor_id' => 10,
    'profile_data' => [
        'logo' => 'abc123-uuid',  // ← Stored in JSON, NOT attached yet!
        'translations' => [...]
    ],
    'status' => 'pending',
]);
```

**Media table (unchanged):**
```
┌──────────┬─────────────────────┬──────────┬────────┬──────────────┐
│ id       │ model_type          │ model_id │ option │ is_attached  │
├──────────┼─────────────────────┼──────────┼────────┼──────────────┤
│ abc123   │ App\Models\Profile  │ NULL     │ NULL   │ false        │
└──────────┴─────────────────────┴──────────┴────────┴──────────────┘
```

**Status:** ✅ File uploaded, ❌ NOT attached (waiting for approval)

**Step 3: Admin approves request**
```php
public function approve(): void
{
    // Get the profile
    $profile = $this->vendor->profile;  // Profile id = 5
    
    // Merge media ID into request
    request()->merge(['logo' => $this->profile_data['logo']]);
    
    // Update profile
    $profile->update([
        'commercial_register_number' => 'CR-123'
    ]);
    
    // UploadMediaObserver is triggered!
    // Attaches media to profile
}
```

**Media table after approval:**
```
┌──────────┬─────────────────────┬──────────┬────────┬──────────────┐
│ id       │ model_type          │ model_id │ option │ is_attached  │
├──────────┼─────────────────────┼──────────┼────────┼──────────────┤
│ abc123   │ App\Models\Profile  │ 5        │ logo   │ true         │
└──────────┴─────────────────────┴──────────┴────────┴──────────────┘
```

**Status:** ✅ File uploaded, ✅ Attached to Profile (id=5)

---

## 🎯 Key Points

### 1. Upload ≠ Attachment
```
Upload:     Creates media record with is_attached = false
Attachment: Links media to specific model with is_attached = true
```

### 2. Media Can Be Uploaded But Not Attached
```
Scenario: Vendor uploads logo → Creates request → Admin rejects
Result:   Media exists but is_attached = false (orphaned)
```

### 3. Attachment Happens on Model Save
```php
// This triggers UploadMediaObserver
$profile->update(['logo' => 'media-id']);

// Observer checks:
// 1. Does Profile have mediaColumns['logo']? ✓
// 2. Is 'logo' in request? ✓
// 3. Attach media to profile ✓
```

### 4. Option Field Identifies the Purpose
```
option = 'logo'                      → Profile logo
option = 'commercial_register_file'  → Commercial register
option = 'main'                      → Product main image
option = 'gallery'                   → Product gallery images
```

---

## 📋 Complete Example

### Scenario: Vendor Updates Profile Logo

**Step 1: Upload**
```bash
POST /api/media
file: new-logo.jpg
```

Response: `{"id": "xyz789"}`

**Media table:**
```sql
INSERT INTO media (id, model_type, model_id, option, is_attached)
VALUES ('xyz789', 'App\Models\Profile', NULL, NULL, false);
```

**Step 2: Create Request**
```bash
POST /api/dashboard/vendor/profile-update-requests
{
  "logo": "xyz789",
  "ar": {"name": "...", "description": "..."},
  "en": {"name": "...", "description": "..."}
}
```

**profile_update_requests table:**
```sql
INSERT INTO profile_update_requests (vendor_id, profile_data, status)
VALUES (10, '{"logo": "xyz789", "translations": {...}}', 'pending');
```

**Media table (unchanged):**
```sql
-- Still not attached
id='xyz789', model_id=NULL, option=NULL, is_attached=false
```

**Step 3: Admin Approves**
```bash
POST /api/dashboard/admin/profile-update-requests/1/approve
```

**Code execution:**
```php
// ProfileUpdateRequest::approve()
$profile = $this->vendor->profile;  // id = 5

// Merge media into request
request()->merge(['logo' => 'xyz789']);

// Update profile
$profile->update(['commercial_register_number' => 'CR-123']);

// UploadMediaObserver::saved() is triggered
// Deletes old logo (if exists)
Media::where('model_id', 5)
     ->where('option', 'logo')
     ->delete();

// Attaches new logo
Media::find('xyz789')->update([
    'model_id'    => 5,
    'model_type'  => 'App\Models\Profile',
    'option'      => 'logo',
    'is_attached' => true,
]);
```

**Media table after approval:**
```sql
-- NOW attached to profile!
id='xyz789', model_id=5, option='logo', is_attached=true
```

**Step 4: Access Logo**
```php
$profile->logo;
// Returns:
// [
//   'id' => 'xyz789',
//   'path' => 'https://domain.com/storage/images/new-logo.jpg',
//   'type' => 'image',
//   'option' => 'logo'
// ]
```

---

## 🔍 How to Check Attachment Status

### Check if media is attached
```php
$media = Media::find('abc123');

if ($media->is_attached) {
    echo "Attached to: {$media->model_type} (ID: {$media->model_id})";
    echo "Purpose: {$media->option}";
} else {
    echo "Not attached yet (orphaned)";
}
```

### Check profile's media
```php
$profile->media()->where('option', 'logo')->first();
// Returns media record if attached, null if not
```

### Get all profile media
```php
$profile->media;  // All media attached to this profile
$profile->logo;   // Specific logo media
$profile->commercial_register_file;  // Specific file media
```

---

## 🎨 Visual Summary

```
┌─────────────────────────────────────────────────────────────┐
│                    MEDIA LIFECYCLE                          │
└─────────────────────────────────────────────────────────────┘

1. UPLOAD
   ┌──────────┐
   │  Upload  │ → Media created with is_attached = false
   │  File    │    model_id = NULL, option = NULL
   └──────────┘

2. STORE IN REQUEST
   ┌──────────────────┐
   │ ProfileUpdate    │ → Media ID stored in JSON
   │ Request Created  │    Still not attached!
   └──────────────────┘

3. APPROVAL
   ┌──────────────────┐
   │ Admin Approves   │ → request()->merge(['logo' => 'id'])
   └──────────────────┘
           ↓
   ┌──────────────────┐
   │ Profile Updated  │ → UploadMediaObserver triggered
   └──────────────────┘
           ↓
   ┌──────────────────┐
   │ Media Attached   │ → is_attached = true
   │                  │    model_id = profile->id
   │                  │    option = 'logo'
   └──────────────────┘

4. ACCESS
   ┌──────────────────┐
   │ $profile->logo   │ → Returns media with full URL
   └──────────────────┘
```

---

## ✅ Summary

**Question:** When does logo get attached to Profile?

**Answer:** 
- ❌ NOT when you upload the file
- ❌ NOT when vendor creates ProfileUpdateRequest
- ✅ YES when admin approves and `$profile->update()` is called

**The magic:** `request()->merge(['logo' => $mediaId])` + `$profile->update()` = UploadMediaObserver attaches media automatically!
