# 🔧 DI & State Management Refactoring Plan

## 🚨 Critical Issues Found

### 1. ViewModel-to-ViewModel Dependencies
**Current:** ViewModels directly depend on other ViewModels
**Problem:** Tight coupling, difficult testing, cascading rebuilds
**Solution:** Use shared repositories or event bus

### 2. Eager Instantiation
**Current:** All 25 providers created at app startup
**Problem:** Slow startup, memory waste
**Solution:** Lazy loading with ProxyProvider

### 3. Error Handling Anti-Pattern
**Current:** `_errorMessage = error; notifyListeners(); _errorMessage = null; notifyListeners();`
**Problem:** Errors flash and disappear instantly
**Solution:** Proper error state with timed clearing

### 4. Large ViewModels (338-525 lines)
**Current:** Monolithic ViewModels with mixed responsibilities
**Problem:** Violates SRP, hard to maintain
**Solution:** Split into smaller, focused controllers

---

## 📋 Refactoring Strategy

### Phase 1: Remove ViewModel Dependencies (Week 1)

#### Option A: Shared Repository Pattern (Recommended)
```dart
// ✅ BEFORE: TendersViewModel depends on HomeViewModel
class TendersViewModel {
  final HomeViewModel homeViewModel;  // ❌ BAD
  
  void updateFeedback() {
    homeViewModel.init();  // Coupled!
  }
}

// ✅ AFTER: Both share TendersRepository
class TendersViewModel {
  final TendersRepository _repository;  // ✅ GOOD
  
  Future<void> updateFeedback() async {
    await _repository.refreshTenders();
    // Repository notifies all listeners
  }
}

class HomeViewModel {
  final TendersRepository _repository;  // ✅ Same repository
  
  void init() {
    _repository.addListener(_onTendersChanged);
  }
}
```

#### Option B: Event Bus Pattern
```dart
// Create event bus
class AppEventBus {
  final _controller = StreamController<AppEvent>.broadcast();
  
  Stream<AppEvent> get events => _controller.stream;
  void fire(AppEvent event) => _controller.add(event);
}

// Events
abstract class AppEvent {}
class TenderUpdatedEvent extends AppEvent {
  final String tenderId;
}

// ViewModels listen to events
class HomeViewModel {
  void init() {
    eventBus.events
        .whereType<TenderUpdatedEvent>()
        .listen(_onTenderUpdated);
  }
}
```

---

### Phase 2: Implement Lazy Loading (Week 1)

```dart
// ✅ AFTER: Lazy loading with ProxyProvider
List<SingleChildWidget> get viewModels {
  return [
    // Only AuthViewModel and ThemeProvider are eager
    ChangeNotifierProvider(
      create: (context) => AuthViewModel(
        authRepository: context.read(),
      ),
    ),
    
    // Others are lazy - created on first access
    ChangeNotifierProxyProvider<AuthViewModel, HomeViewModel>(
      create: (_) => HomeViewModel(homeRepository: _),
      update: (_, auth, previous) => previous ?? HomeViewModel(...),
      lazy: true,  // ✅ Not created until needed
    ),
  ];
}

// Remove init() from constructor
class HomeViewModel {
  HomeViewModel({required HomeRepository homeRepository})
    : _homeRepository = homeRepository;
    // ✅ Don't call init() here
}
```

---

### Phase 3: Fix Error Handling (Week 1)

```dart
// ✅ Create proper error state
class ErrorState {
  final String message;
  final DateTime timestamp;
  final Duration displayDuration;
  
  bool get shouldDisplay =>
      DateTime.now().difference(timestamp) < displayDuration;
}

// In ViewModel
ErrorState? _errorState;
Timer? _errorTimer;

void _setError(String message) {
  _errorState = ErrorState(
    message: message,
    timestamp: DateTime.now(),
    displayDuration: Duration(seconds: 3),
  );
  
  _errorTimer?.cancel();
  _errorTimer = Timer(Duration(seconds: 3), () {
    _errorState = null;
    notifyListeners();
  });
  
  notifyListeners();
}
```

---

### Phase 4: Introduce State Classes (Week 2)

```dart
// ✅ Create immutable state classes with Freezed
@freezed
class TendersState with _$TendersState {
  const factory TendersState({
    @Default(LoadingStatus.idle) LoadingStatus status,
    @Default([]) List<Tender> tenders,
    @Default([]) List<FeedbackData> feedbacks,
    @Default(PaginationState()) PaginationState pagination,
    ErrorState? error,
  }) = _TendersState;
}

enum LoadingStatus { idle, loading, loadingMore, success, failure }

@freezed
class PaginationState with _$PaginationState {
  const factory PaginationState({
    @Default(1) int currentPage,
    @Default(true) bool hasMore,
    @Default(10) int pageSize,
  }) = _PaginationState;
}

// ViewModel becomes much simpler
class TendersViewModel extends StateNotifier<TendersState> {
  TendersViewModel() : super(TendersState());
  
  Future<void> loadTenders() async {
    state = state.copyWith(status: LoadingStatus.loading);
    
    final result = await _repository.getTenders();
    
    state = result.when(
      ok: (data) => state.copyWith(
        status: LoadingStatus.success,
        tenders: data.tenders,
      ),
      error: (error) => state.copyWith(
        status: LoadingStatus.failure,
        error: ErrorState(message: error.toString()),
      ),
    );
  }
}
```

---

### Phase 5: Split Large ViewModels (Week 2-3)

#### Before: 525 lines in TendersViewModel
```dart
class TendersViewModel {
  // Tenders logic (150 lines)
  // Feedback logic (150 lines)
  // Filtering logic (100 lines)
  // Sorting logic (75 lines)
  // Pagination logic (50 lines)
}
```

#### After: Multiple focused files
```
view_models/tenders/
  ├── tenders_view_model.dart          (50 lines - orchestrator)
  ├── controllers/
  │   ├── tenders_loader.dart          (60 lines)
  │   ├── feedback_controller.dart     (70 lines)
  │   └── pagination_controller.dart   (50 lines)
  ├── filters/
  │   ├── tenders_filter_state.dart    (30 lines)
  │   └── tenders_sort_state.dart      (30 lines)
  └── state/
      └── tenders_state.dart           (40 lines)
```

---

## 🎯 Better State Management Options

### Option 1: Riverpod (Recommended)
**Pros:**
- ✅ Compile-time safety
- ✅ No BuildContext needed
- ✅ Built-in testing support
- ✅ Auto-dispose
- ✅ Family/AutoDispose modifiers
- ✅ No ViewModel dependencies

**Cons:**
- 🟡 Learning curve
- 🟡 Migration effort

### Option 2: Bloc/Cubit
**Pros:**
- ✅ Clear state transitions
- ✅ Built-in testing
- ✅ Event-driven
- ✅ Time-travel debugging

**Cons:**
- 🟡 More boilerplate
- 🟡 Steeper learning curve

### Option 3: Stick with Provider + Refactor
**Pros:**
- ✅ No migration needed
- ✅ Team knows it

**Cons:**
- 🔴 Still requires manual state management
- 🔴 No compile-time safety
- 🔴 Prone to same issues

---

## 📊 Migration Effort Estimate

| Phase | Effort | Priority | Files Affected |
|-------|--------|----------|----------------|
| 1. Remove VM deps | 3 days | 🔴 Critical | 9 ViewModels, dependencies.dart |
| 2. Lazy loading | 1 day | 🟡 High | dependencies.dart, main.dart |
| 3. Fix error handling | 2 days | 🟡 High | 9 ViewModels |
| 4. State classes | 5 days | 🟢 Medium | 9 ViewModels + new state files |
| 5. Split ViewModels | 7 days | 🟢 Medium | 9 ViewModels → 30+ files |

**Total: ~18 days (3.5 weeks) for complete refactor**

---

## 🔍 Quick Wins (Can Do Today)

### 1. Fix Error Handling (2 hours)
```dart
// Create lib/core/state/error_state.dart
// Update all ViewModels to use it
```

### 2. Remove init() from Constructors (1 hour)
```dart
// Move all init() calls to explicit method calls
```

### 3. Add State Classes (1 day)
```dart
// Start with AuthState, HomeState
```

---

## 📚 Resources

- [Riverpod Migration Guide](https://riverpod.dev/docs/from_provider)
- [Flutter State Management Comparison](https://docs.flutter.dev/data-and-backend/state-mgmt/options)
- [Provider Best Practices](https://pub.dev/packages/provider#best-practices)


