import 'package:flutter/material.dart'; import '../models/stop.dart'; import '../models/delivery_history.dart'; import '../services/database.dart'; class DeliveryProvider extends ChangeNotifier { final DatabaseService _db = DatabaseService(); List _stops = []; Set _todayDeliveredIds = {}; bool _isLoading = false; String? _error; int? _currentRouteId; int _navigateIndex = 0; bool _navigateMode = false; List get stops => _stops; bool get isLoading => _isLoading; String? get error => _error; bool get navigateMode => _navigateMode; Set get todayDeliveredIds => _todayDeliveredIds; int get navigateIndex => _navigateIndex; /// Stops filtered to show delivered status based on TODAY's history. List get stopsWithDeliveryStatus { return _stops.map((s) { final isDeliveredToday = _todayDeliveredIds.contains(s.id); return s.copyWith(delivered: isDeliveredToday); }).toList(); } int get deliveredCount => _todayDeliveredIds.length; int get totalStops => _stops.length; double get progressPercent => _stops.isEmpty ? 0.0 : _todayDeliveredIds.length / _stops.length; /// Newspaper counts breakdown Map get newspaperCounts { final counts = {}; for (final stop in _stops) { for (final np in stop.newspapers) { counts[np] = (counts[np] ?? 0) + 1; } } return counts; } int get totalNewspapers { int total = 0; for (final stop in _stops) { total += stop.newspapers.length; } return total; } /// Current stop in navigation mode Stop? get currentNavigateStop { if (!_navigateMode || _stops.isEmpty) return null; if (_navigateIndex >= _stops.length) return null; return stopsWithDeliveryStatus[_navigateIndex]; } /// Get the next undelivered stop index for navigation int get nextUndeliveredIndex { final orderedStops = stopsWithDeliveryStatus; for (var i = 0; i < orderedStops.length; i++) { if (!orderedStops[i].delivered) return i; } return -1; // all delivered } // ── Load ──────────────────────────────────────────────── Future loadStops(int routeId) async { _currentRouteId = routeId; _isLoading = true; _error = null; notifyListeners(); try { _stops = await _db.getStops(routeId); _todayDeliveredIds = await _db.getTodayDeliveredStopIds(routeId); _isLoading = false; _error = null; } catch (e) { _error = 'Failed to load stops: $e'; _isLoading = false; } notifyListeners(); } // ── Toggle Delivery ───────────────────────────────────── Future toggleDelivered(int stopId) async { if (_currentRouteId == null) return; final isDelivered = _todayDeliveredIds.contains(stopId); if (isDelivered) { await _db.removeDeliveryRecord(_currentRouteId!, stopId); _todayDeliveredIds.remove(stopId); } else { await _db.recordDelivery(_currentRouteId!, stopId); _todayDeliveredIds.add(stopId); } notifyListeners(); } // ── Reset Today ───────────────────────────────────────── Future resetToday() async { if (_currentRouteId == null) return; await _db.resetToday(_currentRouteId!); _todayDeliveredIds.clear(); notifyListeners(); } // ── Navigation Mode ───────────────────────────────────── void startNavigation() { _navigateMode = true; _navigateIndex = nextUndeliveredIndex >= 0 ? nextUndeliveredIndex : 0; notifyListeners(); } void stopNavigation() { _navigateMode = false; notifyListeners(); } void nextStop() { if (_navigateIndex < _stops.length - 1) { _navigateIndex++; notifyListeners(); } } void previousStop() { if (_navigateIndex > 0) { _navigateIndex--; notifyListeners(); } } void goToStop(int index) { if (index >= 0 && index < _stops.length) { _navigateIndex = index; notifyListeners(); } } // ── Statistics ────────────────────────────────────────── Future> getStats({int days = 30}) async { if (_currentRouteId == null) return []; return _db.getDeliveryStats(_currentRouteId!, days: days); } Future get totalDeliveries async { if (_currentRouteId == null) return 0; return _db.getTotalDeliveries(_currentRouteId!); } Future get weeklyDeliveries async { if (_currentRouteId == null) return 0; return _db.getWeeklyDeliveryCount(_currentRouteId!); } // ── Notes ─────────────────────────────────────────────── Future saveNotes(int stopId, String notes) async { await _db.updateStopNotes(stopId, notes); final idx = _stops.indexWhere((s) => s.id == stopId); if (idx >= 0) { _stops[idx] = _stops[idx].copyWith(notes: notes); notifyListeners(); } } // ── Delete Stop ───────────────────────────────────────── Future deleteStop(int stopId) async { await _db.deleteStop(stopId); _stops.removeWhere((s) => s.id == stopId); _todayDeliveredIds.remove(stopId); notifyListeners(); } // ── Reorder ───────────────────────────────────────────── Future reorderStops(List newOrder) async { await _db.reorderStops(newOrder); _stops = newOrder; notifyListeners(); } // ── Update Coordinates ────────────────────────────────── Future updateCoords(int stopId, double lat, double lng) async { await _db.updateStopCoords(stopId, lat, lng); final idx = _stops.indexWhere((s) => s.id == stopId); if (idx >= 0) { _stops[idx] = _stops[idx].copyWith(lat: lat, lng: lng); notifyListeners(); } } }