# 🔄 Migration Guide: ParentId → NetworkParentId

## 📋 Overview

در سیستم قدیمی، کاربران با استفاده از `User.ParentId` به هم متصل می‌شدند (Parent-Child relationship).  
سیستم جدید **Network-Club-Commission** از یک **Binary Tree** استفاده می‌کند که نیاز به:
- `User.NetworkParentId` (شناسه پدر در شبکه باینری)
- `User.LegPosition` (Left یا Right)

برای اجرای صحیح Worker و محاسبات، **باید** تمام کاربران قدیمی Migrate شوند.

---

## ⚠️ Critical Issues

### مشکل 1: Binary Tree Constraint
- هر Parent فقط می‌تواند **2 فرزند** داشته باشد (Left & Right)
- اگر کاربری در سیستم قدیمی بیشتر از 2 فرزند دارد، Migration فقط **2 فرزند اول** را می‌گیرد

### مشکل 2: Orphaned Nodes
- اگر `ParentId` اشاره به یک کاربر نامعتبر (حذف شده) باشد، آن User **Orphaned** است
- Orphaned nodes در Binary Tree نادیده گرفته می‌شوند

---

## 🚀 Migration Methods

### روش 1: Automatic (Seeder - توصیه می‌شود)

Migration به صورت خودکار در `Program.cs` در حالت **Development** اجرا می‌شود:

```csharp
// در Program.cs
var migrationSeeder = new NetworkParentIdMigrationSeeder(dbContext, logger);
await migrationSeeder.SeedAsync();
```

**مزایا:**
- ✅ Idempotent (می‌توان چندین بار اجرا کرد، فقط یکبار تاثیر می‌گذارد)
- ✅ Validation اتوماتیک
- ✅ Logging کامل

**کجا اجرا می‌شود؟**
- فقط در **Development** environment
- هر بار که پروژه Run شود

---

### روش 2: Manual (Command)

اگر نیاز به اجرای دستی دارید:

```csharp
// درخواست از طریق MediatR
var result = await _mediator.Send(new MigrateNetworkParentIdCommand());

if (result.Success)
{
    Console.WriteLine($"Migrated: {result.MigratedCount}");
    Console.WriteLine($"Skipped: {result.SkippedCount}");
}
else
{
    Console.WriteLine($"Error: {result.Message}");
}
```

---

### روش 3: SQL Script

برای Production یا اجرای مستقیم روی Database:

```bash
# فایل: CMSMicroservice.Infrastructure/Migrations/Scripts/20250601_MigrateParentIdToNetworkParentId.sql
```

**نکته مهم:**  
قبل از اجرا، **حتماً** بررسی کنید که آیا کاربری بیش از 2 فرزند دارد:

```sql
SELECT 
    ParentId, 
    COUNT(*) as ChildCount,
    STRING_AGG(CAST(Id AS VARCHAR), ', ') as ChildIds
FROM Users
WHERE ParentId IS NOT NULL
GROUP BY ParentId
HAVING COUNT(*) > 2;
```

---

## 📊 Validation After Migration

### 1. بررسی تعداد کاربران Migrate شده

```csharp
var stats = await _context.Users
    .GroupBy(u => 1)
    .Select(g => new
    {
        TotalUsers = g.Count(),
        UsersWithNetworkParent = g.Count(u => u.NetworkParentId != null),
        LeftChildren = g.Count(u => u.LegPosition == NetworkLeg.Left),
        RightChildren = g.Count(u => u.LegPosition == NetworkLeg.Right)
    })
    .FirstOrDefaultAsync();
```

### 2. بررسی Orphaned Nodes

```sql
SELECT Id, NetworkParentId
FROM Users
WHERE NetworkParentId IS NOT NULL
  AND NetworkParentId NOT IN (SELECT Id FROM Users);
```

### 3. بررسی Binary Tree Violation

```sql
SELECT NetworkParentId, COUNT(*) as ChildCount
FROM Users
WHERE NetworkParentId IS NOT NULL
GROUP BY NetworkParentId
HAVING COUNT(*) > 2;
```

---

## ⚙️ Algorithm Details

### مراحل Migration:

1. **Find Users**: یافتن کاربران با `ParentId != NULL` و `NetworkParentId == NULL`
2. **Group by Parent**: گروه‌بندی بر اساس ParentId
3. **Check Constraint**: اگر Parent بیش از 2 فرزند دارد، فقط 2 تا اول را بگیر
4. **Assign Values**:
   ```csharp
   child.NetworkParentId = parentId;
   child.LegPosition = (i == 0) ? NetworkLeg.Left : NetworkLeg.Right;
   ```
5. **Save & Validate**: ذخیره و اعتبارسنجی Binary Tree

---

## 🐛 Troubleshooting

### مشکل: Parent has more than 2 children

**راه حل:**  
تصمیم دستی بگیرید که کدام 2 فرزند را نگه دارید:

```sql
-- بررسی کنید که کدام Parent مشکل دارد
SELECT ParentId, COUNT(*) as ChildCount
FROM Users
WHERE ParentId = 123
GROUP BY ParentId;

-- لیست فرزندان را ببینید
SELECT Id, FullName, CreatedAt
FROM Users
WHERE ParentId = 123
ORDER BY CreatedAt;

-- دستی NetworkParentId را برای 2 فرزند انتخابی Set کنید
UPDATE Users
SET NetworkParentId = 123, LegPosition = 0 -- Left
WHERE Id = 456;

UPDATE Users
SET NetworkParentId = 123, LegPosition = 1 -- Right
WHERE Id = 789;
```

---

### مشکل: Orphaned Nodes (Parent doesn't exist)

**راه حل:**  
ParentId را NULL کنید یا به یک Parent معتبر متصل کنید:

```sql
-- گزینه 1: NULL کردن (Root شدن)
UPDATE Users
SET ParentId = NULL, NetworkParentId = NULL
WHERE ParentId = 999; -- 999 وجود ندارد

-- گزینه 2: اتصال به Parent دیگر
UPDATE Users
SET ParentId = 1, NetworkParentId = 1
WHERE ParentId = 999;
```

---

## ✅ Checklist Before Production

- [ ] Migration در Development اجرا شده؟
- [ ] Validation Errors بررسی شد؟
- [ ] Orphaned Nodes رفع شدند؟
- [ ] Binary Tree Violations رفع شدند؟
- [ ] Backup از Database گرفته شده؟
- [ ] Migration Script برای Production آماده است؟
- [ ] Testing کامل انجام شده؟

---

## 🔗 Related Files

- **Seeder**: `CMSMicroservice.Infrastructure/Data/Seeding/NetworkParentIdMigrationSeeder.cs`
- **Command**: `CMSMicroservice.Application/UserCQ/Commands/MigrateNetworkParentId/`
- **SQL Script**: `CMSMicroservice.Infrastructure/Migrations/Scripts/20250601_MigrateParentIdToNetworkParentId.sql`
- **Entity**: `CMSMicroservice.Domain/Entities/User.cs` (خطوط 16, 45, 49)

---

## 📞 Support

اگر مشکل خاصی با Migration پیدا کردید:
1. Log های Seeder را بررسی کنید
2. ValidationErrors را چک کنید
3. SQL Script را به صورت دستی اجرا کنید
