using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using LSFE.MobApp.Contracts; using LSFE.MobApp.Entities.Account; using LSFE.MobApp.Entities.Doctor; using LSFE.MobApp.Entities.Location; using LSFE.MobApp.Models; using LSFE.MobApp.Services; using LSFE.Infrastructure.Entities.Maintenance; using Microsoft.Extensions.Logging; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; namespace LSFE.MobApp.ViewModels { public partial class MainPageVM : ObservableObject, INotifyPropertyChanged { private readonly Repository _userRepository; private readonly Repository _institutionRepository; private readonly Repository _doctorRepository; private readonly IDatabaseService _databaseService; private readonly ILogger _logger; private readonly ISyncService _syncService; // Add sync service private readonly IApiClientFactory _apiClientFactory; // Add API client factory private DateTime _currentMonthStart; private string _selectedTask = "REMOTE, TASK 1"; private bool _isLeftPanelVisible = true; private int _currentCycle; private bool _isLoading; private bool _isSyncing; // Add sync status private string _statusMessage = string.Empty; // Add status message public ObservableCollection Institutions { get; set; } = new(); public ObservableCollection AllDoctors { get; set; } = new(); // Add property to track selected doctor [ObservableProperty] private Doctor selectedDoctor; // Add property to control which view is shown [ObservableProperty] private bool isCalendarViewVisible = true; // Property to control doctor view visibility public bool IsDoctorViewVisible => !isCalendarViewVisible; // Command to toggle institution expansion [RelayCommand] private void ToggleInstitution(Institution institution) { if (institution != null) { institution.IsExpanded = !institution.IsExpanded; } } public bool IsSyncing { get => _isSyncing; set => SetProperty(ref _isSyncing, value); } public string StatusMessage { get => _statusMessage; set => SetProperty(ref _statusMessage, value); } // Add this property for header visibility private bool _isHeaderVisible = true; public bool IsHeaderVisible { get => _isHeaderVisible; set { if (_isHeaderVisible != value) { _isHeaderVisible = value; OnPropertyChanged(nameof(IsHeaderVisible)); } } } public string SelectedTask { get => _selectedTask; set => SetProperty(ref _selectedTask, value); } public DateTime CurrentMonthStart { get => _currentMonthStart; set { if (SetProperty(ref _currentMonthStart, value)) { OnPropertyChanged(nameof(CycleText)); OnPropertyChanged(nameof(CurrentMonthText)); // LoadCalendarMonth(); } } } public bool IsLeftPanelVisible { get => _isLeftPanelVisible; set { if (SetProperty(ref _isLeftPanelVisible, value)) { OnPropertyChanged(nameof(LeftPanelWidth)); OnPropertyChanged(nameof(LeftPanelVisibility)); } } } public int CurrentCycle { get => _currentCycle; set { if (SetProperty(ref _currentCycle, value)) { OnPropertyChanged(nameof(CycleText)); } } } public bool IsLoading { get => _isLoading; set => SetProperty(ref _isLoading, value); } public string CycleText => $"Cycle {CurrentCycle}"; public string CurrentMonthText => CurrentMonthStart.ToString("MMMM yyyy"); public GridLength LeftPanelWidth => IsLeftPanelVisible ? new GridLength(250, GridUnitType.Absolute) : new GridLength(0, GridUnitType.Absolute); public bool LeftPanelVisibility => IsLeftPanelVisible; public MainPageVM( Repository userRepository, Repository doctorRepository, Repository institutionRepository, IDatabaseService databaseService, ILogger logger, ISyncService syncService, // Add sync service injection IApiClientFactory apiClientFactory) // Add API client factory injection { _userRepository = userRepository; _doctorRepository = doctorRepository; _institutionRepository = institutionRepository; _databaseService = databaseService; _logger = logger; _syncService = syncService; _apiClientFactory = apiClientFactory; //InitializePlottingNavigationItems(); // Initialize with current month CurrentMonthStart = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1); CurrentCycle = DateTime.Today.Month; // Load data asynchronously _ = Task.Run(async () => await InitializeDataAsync()); } [RelayCommand] private async Task SyncFromServerAsync() { if (IsSyncing) return; try { IsSyncing = true; StatusMessage = "Fetching data from server..."; // Create API client using the exact API entity type var apiClient = _apiClientFactory.CreateClient("institutions"); // Call your Web API endpoint var serverInstitutions = await apiClient.GetAllAsync(); if (serverInstitutions?.Any() == true) { StatusMessage = $"Received {serverInstitutions.Count} institutions from server"; _logger.LogInformation($"Successfully fetched {serverInstitutions.Count} institutions from server"); // Log sample data to verify API response foreach (var institution in serverInstitutions.Take(3)) { _logger.LogInformation($"API Data: ID={institution.InstitutionId}, Name={institution.InstitutionName}, Territory={institution.TerritoryId}, Active={institution.IsActive}"); } // Process the data await ProcessServerDataAsync(serverInstitutions); StatusMessage = "Server data processed successfully!"; } else { StatusMessage = "No data received from server"; _logger.LogWarning("No institutions received from server"); } // Clear status message after delay await Task.Delay(3000); StatusMessage = string.Empty; } catch (HttpRequestException httpEx) { _logger.LogError(httpEx, "Network error while fetching from server"); StatusMessage = "Network error: Check your connection"; } catch (Exception ex) { _logger.LogError(ex, "Error fetching data from server"); StatusMessage = $"Server error: {ex.Message}"; } finally { IsSyncing = false; // Clear error messages after delay if (!string.IsNullOrEmpty(StatusMessage)) { await Task.Delay(5000); StatusMessage = string.Empty; } } } // Option 1: Use InsertAsync directly instead of SaveAsync private async Task ProcessServerDataAsync(List serverInstitutions) { try { _logger.LogInformation($"=== PROCESSING SERVER DATA ==="); _logger.LogInformation($"Received {serverInstitutions.Count} institutions from server"); int newCount = 0; int updatedCount = 0; int errorCount = 0; foreach (var serverInstitution in serverInstitutions) { try { // Find existing local record by InstitutionId var existingLocal = await _institutionRepository.FirstOrDefaultAsync(x => x.InstitutionId == serverInstitution.InstitutionId); if (existingLocal == null) { // Create new local record with DEFAULT GUID initially var newInstitution = new Institution { Id = default(Guid), // Use default GUID for SaveAsync to work InstitutionId = serverInstitution.InstitutionId, InstitutionName = serverInstitution.InstitutionName, TerritoryId = serverInstitution.TerritoryId, IsActive = serverInstitution.IsActive, LastSyncedAt = DateTime.UtcNow, NeedsSync = false, CreatedDate = DateTime.UtcNow }; _logger.LogInformation($"Creating: {newInstitution.InstitutionName} (ServerID: {newInstitution.InstitutionId})"); // Method 1: Use SaveAsync with default GUID var result = await _institutionRepository.SaveAsync(newInstitution); _logger.LogInformation($"Save result: {result}"); if (result > 0) { newCount++; _logger.LogInformation($"✅ Successfully created: {newInstitution.InstitutionName}"); } else { _logger.LogWarning($"❌ SaveAsync failed, trying direct insert..."); // Method 2: Try direct insert as backup //try //{ // newInstitution.Id = Guid.NewGuid(); // Set proper GUID for direct insert // // Use direct SQLite insert // var insertQuery = @" //INSERT INTO Institutions (Id, InstitutionId, InstitutionName, TerritoryId, IsActive, CreatedDate, LastSyncedAt, NeedsSync) //VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; // await _institutionRepository.QueryAsync(insertQuery, // newInstitution.Id.ToString(), // newInstitution.InstitutionId, // newInstitution.InstitutionName, // newInstitution.TerritoryId, // newInstitution.IsActive ? 1 : 0, // newInstitution.CreatedDate.ToString("yyyy-MM-dd HH:mm:ss"), // newInstitution.LastSyncedAt.ToString("yyyy-MM-dd HH:mm:ss"), // newInstitution.NeedsSync ? 1 : 0 // ); // newCount++; // _logger.LogInformation($"✅ Direct insert successful: {newInstitution.InstitutionName}"); //} //catch (Exception insertEx) //{ // _logger.LogError(insertEx, $"❌ Direct insert also failed: {newInstitution.InstitutionName}"); // errorCount++; //} } // Verify the record was saved var verification = await _institutionRepository.FirstOrDefaultAsync(x => x.InstitutionId == serverInstitution.InstitutionId); if (verification != null) { _logger.LogInformation($"✅ Verified in database: {verification.InstitutionName} with GUID: {verification.Id}"); } else { _logger.LogWarning($"❌ Could not verify saved record for {newInstitution.InstitutionName}"); } } else { // Update existing record var oldName = existingLocal.InstitutionName; existingLocal.InstitutionName = serverInstitution.InstitutionName; existingLocal.TerritoryId = serverInstitution.TerritoryId; existingLocal.IsActive = serverInstitution.IsActive; existingLocal.LastSyncedAt = DateTime.UtcNow; existingLocal.NeedsSync = false; var result = await _institutionRepository.UpdateAsync(existingLocal); if (result > 0) { updatedCount++; _logger.LogInformation($"✅ Updated: {oldName} -> {existingLocal.InstitutionName}"); } else { _logger.LogWarning($"❌ Failed to update: {existingLocal.InstitutionName}"); errorCount++; } } } catch (Exception itemEx) { _logger.LogError(itemEx, $"Error processing institution {serverInstitution.InstitutionId}: {serverInstitution.InstitutionName}"); errorCount++; } } _logger.LogInformation($"=== SYNC SUMMARY ==="); _logger.LogInformation($"New: {newCount}, Updated: {updatedCount}, Errors: {errorCount}"); // Final verification - count records in database var finalCount = (await _institutionRepository.GetAllAsync()).Count; _logger.LogInformation($"Total records in database after sync: {finalCount}"); // Refresh the UI data await LoadDataFromDatabaseAsync(); StatusMessage = $"Processed: {newCount} new, {updatedCount} updated, {errorCount} errors. Total DB records: {finalCount}"; } catch (Exception ex) { _logger.LogError(ex, "Error processing server data"); throw; } } private async Task InitializeDataAsync() { try { IsLoading = true; await _databaseService.InitializeAsync(); await _institutionRepository.GetActiveAsync(); await LoadDataFromDatabaseAsync(); } catch (Exception ex) { _logger.LogError(ex, "Error initializing data"); } finally { IsLoading = false; } } private async Task LoadDataFromDatabaseAsync() { try { // Load institutions from database var institutions = await _institutionRepository.GetActiveAsync(); var users = await _doctorRepository.GetActiveAsync(); // Update UI on main thread MainThread.BeginInvokeOnMainThread(() => { // Clear existing collections Institutions.Clear(); AllDoctors.Clear(); // Add all users to AllUsers collection foreach (var user in users) { // Set institution name for display var institution = institutions.FirstOrDefault(i => i.InstitutionId == user.InstitutionId); if (institution != null) { user.InstitutionName = institution.InstitutionName; } AllDoctors.Add(user); } // Group users by institution and add to Institutions collection foreach (var institution in institutions) { var institutionUsers = users.Where(u => u.InstitutionId == institution.InstitutionId).ToList(); institution.Doctors.Clear(); foreach (var user in institutionUsers) { institution.Doctors.Add(user); } Institutions.Add(institution); } _logger.LogInformation($"Loaded {institutions.Count} institutions and {users.Count} users from database"); }); } catch (Exception ex) { _logger.LogError(ex, "Error loading data from database"); throw; } } // Updated RefreshDataAsync with sync functionality [RelayCommand] private async Task RefreshDataAsync() { if (IsSyncing) return; // Prevent multiple sync operations try { IsSyncing = true; StatusMessage = "Syncing with server..."; // First try to sync with server var syncSuccess = await _syncService.SyncPullCoveragePlanTodayAsync(); if (syncSuccess) { StatusMessage = "Sync successful! Loading updated data..."; _logger.LogInformation("Data sync completed successfully"); } else { StatusMessage = "Sync failed, loading local data..."; _logger.LogWarning("Data sync failed, falling back to local data"); } // Load updated local data await LoadDataFromDatabaseAsync(); StatusMessage = syncSuccess ? "Data refreshed successfully!" : "Loaded local data"; // Clear status message after delay await Task.Delay(3000); StatusMessage = string.Empty; } catch (Exception ex) { _logger.LogError(ex, "Error refreshing data"); StatusMessage = $"Error: {ex.Message}"; // Clear error message after delay await Task.Delay(5000); StatusMessage = string.Empty; } finally { IsSyncing = false; } } [RelayCommand] private void ToggleLeftPanel() { IsLeftPanelVisible = !IsLeftPanelVisible; } [RelayCommand] private void PreviousMonth() { CurrentMonthStart = CurrentMonthStart.AddMonths(-1); } [RelayCommand] private void NextMonth() { CurrentMonthStart = CurrentMonthStart.AddMonths(1); } } }