using LSFE.MobApp.Contracts; using LSFE.MobApp.Entities.Doctor; using LSFE.MobApp.Entities.Planning; using LSFE.MobApp.Helpers; using LSFE.MobApp.Models; using LSFE.MobApp.Pages.CoveragePlan; using LSFE.MobApp.Services; using LSFE.MobApp.Services.Coverage; using LSFE.MobApp.ViewModels.Common; using LSFE.MobApp.Views.CoveragePlan; using Microsoft.Extensions.Configuration; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Windows.Input; namespace LSFE.MobApp.ViewModels.Coverage { public class MarketProductVM : BaseDoctorVM { #region Service and Helper private readonly IFileDownloadService _fileDownloadService; #endregion #region Repository private readonly Repository _productTransRepository; private readonly Repository _inventoryTransRepository; private readonly Repository _mrRawPlanRepository; #endregion public ObservableCollection Materials { get; set; } public ObservableCollection Samplings { get; set; } private readonly Doctor _doctor; public MarketProductVM(Doctor doctor, Repository doctorRepository, IFileDownloadService fileDownloadService, Repository productTransRepository, Repository inventoryTransRepository, Repository mrRawPlanRepository, ISyncService syncService, IConfiguration configuration, AddressService addressService) : base(doctorRepository, syncService, addressService, configuration) { _doctor = doctor; _fileDownloadService = fileDownloadService; _productTransRepository = productTransRepository; _inventoryTransRepository = inventoryTransRepository; _mrRawPlanRepository = mrRawPlanRepository; Materials = new ObservableCollection(); Samplings = new ObservableCollection(); CloseCommand = new Command(async () => await Close()); CopyEmailCommand = new Command(async () => await CopyEmail()); CallPhoneCommand = new Command(async () => await CallPhone()); DownloadMaterialCommand = new Command(async (material) => await DownloadMaterial(material)); OpenMaterialCommand = new Command(async (material) => await OpenMaterial(material)); DownloadAllCommand = new Command(async () => await DownloadAllMaterials()); CheckAllMaterialsCommand = new Command(CheckAllMaterials); CheckAllSamplingCommand = new Command(CheckAllSampling); DoctorSignInCommand = new Command(async () => await ProcessDoctorSignIn()); _ = LoadMaterials(); _ = LoadSamplings(); } #region Status Properties - Using Helper (DRY!) public string StatusText => DoctorStatusHelper.GetStatusText(_doctor.Status); public Color StatusBackgroundColor => DoctorStatusHelper.GetStatusBackgroundColor(_doctor.Status); public Color StatusTextColor => DoctorStatusHelper.GetStatusTextColor(_doctor.Status); #endregion /// /// This method is automatically called when status updates are broadcast /// protected override async void OnDoctorStatusUpdated(int doctorId) { // Only update if it's this doctor if (_doctor.DoctorId == doctorId) { var updated = await RefreshDoctorFromRepositoryAsync(doctorId); if (updated != null) { _doctor.Status = updated.Status; // Notify UI to refresh OnPropertyChanged(nameof(StatusText)); OnPropertyChanged(nameof(StatusBackgroundColor)); OnPropertyChanged(nameof(StatusTextColor)); } } } private bool _isLoading; public bool IsLoading { get => _isLoading; set { _isLoading = value; OnPropertyChanged(); } } #region Dr Information public string FullName => $"Dr. {_doctor.FirstName} {_doctor.MiddleInitial} {_doctor.LastName}".Replace(" ", " ").Trim(); public string EmailAddress => _doctor.EmailAddress ?? "N/A"; public string PhoneNo => _doctor.PhoneNo ?? "N/A"; public string LicenseNo => _doctor.LicenseNo ?? "N/A"; public string Specialization => _doctor.SpecializationName ?? "General Practice"; public string InstitutionName => _doctor.InstitutionName ?? "No Institution Assigned"; public string Address => _doctor.Address ?? "No address provided"; public string MaxVisit => $"{_doctor.MaxVisit} doctors"; public string BirthDateFormatted => _doctor.BirthDate.ToString("MMMM dd, yyyy"); public string CreatedDateFormatted => _doctor.CreatedDate.ToString("MMMM dd, yyyy"); public bool HasInstitution => !string.IsNullOrEmpty(_doctor.InstitutionName); public bool HasAddress => !string.IsNullOrEmpty(_doctor.Address); #endregion #region Styles public static string GetIconEmoji(int iconEmojiId) { return iconEmojiId switch { 1 => "💊", 2 => "🍎", 3 => "🍌", 4 => "🍓", 5 => "🍌", 6 => "💉", _ => "🧪" }; } #endregion #region Command public ICommand CloseCommand { get; } public ICommand CopyEmailCommand { get; } public ICommand CallPhoneCommand { get; } public ICommand DownloadMaterialCommand { get; } public ICommand OpenMaterialCommand { get; } public ICommand DownloadAllCommand { get; } public ICommand CheckAllMaterialsCommand { get; } public ICommand CheckAllSamplingCommand { get; } public ICommand DoctorSignInCommand { get; } #endregion #region Method private async Task ProcessDoctorSignIn() { try { IsLoading = true; if (_doctor.Status == 2) { await UtilityVM.ShowAlert( "Warning:", $"Failed to process the signature. The visitation for Dr. {_doctor.FirstName} {_doctor.LastName} has already been completed." ); return; } // Get all checked materials var checkedMaterials = Materials.Where(m => m.IsChecked).ToList(); // Get all checked sampling items var checkedSamplings = Samplings.Where(s => s.IsChecked).ToList(); if (!checkedMaterials.Any() && !checkedSamplings.Any()) { await Application.Current.MainPage.DisplayAlert( "No Selection", "Please select at least one material or sampling item.", "OK"); return; } // Build summary message var vm = new ConfirmSelectionVM( FullName, Specialization, checkedMaterials, checkedSamplings, async () => { var signatureVM = new DoctorSignatureVM( _doctor, _addressService, _syncService, _mrRawPlanRepository, _doctorRepository, _configuration, checkedMaterials, checkedSamplings, OnSignatureCompleted ); var signaturePage = new DoctorSignaturePage(signatureVM); await Application.Current.MainPage.Navigation.PushAsync(signaturePage); }); await Application.Current.MainPage.Navigation.PushModalAsync( new ConfirmSelectionModal(vm) ); } catch (Exception ex) { await Application.Current.MainPage.DisplayAlert("Error", $"Failed to process: {ex.Message}", "OK"); } finally { IsLoading = false; } } private async Task LoadSamplings() { try { Samplings.Clear(); var sampleData = await _inventoryTransRepository.GetAllAsync(); var today = DateTime.Today; var filtered = sampleData.Where(i => i.DoctorId == _doctor.DoctorId && i.AppointmentDate >= today && i.AppointmentDate < today.AddDays(1)); foreach (var item in filtered) { Samplings.Add(new SamplingItem { InventoryTransId = item.InventoryTransId, Description = item.Description, Qty = item.QtyIn, IconEmoji = GetIconEmoji(item.IconEmojiId), IsChecked = false }); } } catch (Exception ex) { Debug.WriteLine($"Failed to load samplings: {ex.Message}"); } } private async Task DownloadMaterial(ProductMaterial material) { if (material == null || material.IsDownloading) return; try { material.IsDownloading = true; await _fileDownloadService.DownloadFileAsync(material.FileUrl, material.ProductLink, CancellationToken.None); material.IsDownloaded = true; material.IsDownloading = false; await Application.Current.MainPage.DisplayAlert("Success", $"{material.ProductName} downloaded successfully!", "OK"); } catch (Exception ex) { material.IsDownloading = false; await Application.Current.MainPage.DisplayAlert("Error", $"Failed to download: {ex.Message}", "OK"); } } private async Task OpenMaterial(ProductMaterial material) { try { if (!material.IsDownloaded) { var download = await Application.Current.MainPage.DisplayAlert( "Not Downloaded", "This material needs to be downloaded first. Download now?", "Yes", "No"); if (download) { await DownloadMaterial(material); } return; } var localPath = _fileDownloadService.GetLocalFilePath(material.ProductLink); if (File.Exists(localPath)) { await Launcher.OpenAsync(new OpenFileRequest { File = new ReadOnlyFile(localPath) }); } else { await Application.Current.MainPage.DisplayAlert("Error", "File not found. Please re-download.", "OK"); material.IsDownloaded = false; } } catch (Exception ex) { await Application.Current.MainPage.DisplayAlert("Error", $"Failed to open file: {ex.Message}", "OK"); } } private async Task DownloadMaterials(ProductMaterial material) { if (material == null || material.IsDownloading) return; try { material.IsDownloading = true; await _fileDownloadService.DownloadFileAsync(material.FileUrl, material.ProductLink, CancellationToken.None); material.IsDownloaded = true; material.IsDownloading = false; } catch (Exception ex) { material.IsDownloading = false; await Application.Current.MainPage.DisplayAlert("Error", $"Failed to download: {ex.Message}", "OK"); } } private async Task DownloadAllMaterials() { try { await LoadMaterials(); var allTransactions = await _productTransRepository.GetAllAsync(); foreach (var product in allTransactions) { var endpoint = _configuration["ApiSettings:Endpoints:GetProductFiles"]; var fileUrl = $"{endpoint}{Uri.EscapeDataString(product.ProductLink)}"; var material = new ProductMaterial { ProductTransId = product.ProductTransId, ProductId = product.ProductId, ProductName = product.ProductName, ProductLink = product.ProductLink, FileUrl = fileUrl, FileExtension = Path.GetExtension(product.ProductLink) }; if (!await _fileDownloadService.IsFileDownloadedAsync(product.ProductLink)) { await DownloadMaterials(material); } } await Application.Current.MainPage.DisplayAlert("Success", "All materials downloaded!", "OK"); } catch (Exception ex) { await Application.Current.MainPage.DisplayAlert("Error", $"Failed to download all: {ex.Message}", "OK"); } } private async Task LoadMaterials() { IsLoading = true; try { await TrySyncAsync(); Materials.Clear(); // Broadcast to ALL ViewModels - ONE LINE! DoctorStatusHelper.BroadcastStatusUpdate(_doctor.DoctorId); var transactions = await _productTransRepository.GetAllAsync(); var displayList = transactions .Where(p => p.DoctorId == _doctor.DoctorId && p.MRRawPlanId < _doctor.MRRawPlanId) .ToList(); // Load materials in parallel var materialTasks = displayList.Select(async product => { var fileExtension = Path.GetExtension(product.ProductLink); var endpoint = _configuration["ApiSettings:Endpoints:GetProductFiles"]; var fileUrl = $"{endpoint}{Uri.EscapeDataString(product.ProductLink)}"; var isDownloaded = await _fileDownloadService.IsFileDownloadedAsync(product.ProductLink); return new ProductMaterial { ProductTransId = product.ProductTransId, ProductId = product.ProductId, ProductName = product.ProductName, ProductLink = product.ProductLink, FileUrl = fileUrl, FileExtension = fileExtension, IsDownloaded = isDownloaded, IsChecked = false }; }); var materials = await Task.WhenAll(materialTasks); foreach (var m in materials) Materials.Add(m); } finally { IsLoading = false; } } private async Task TrySyncAsync() { try { await _syncService.SyncPullProductsAsync(); await _syncService.SyncPullSamplesAsync(); } catch (Exception ex) { Debug.WriteLine($"Sync failed: {ex.Message}"); } } private void CheckAllMaterials() { bool allChecked = Materials.All(m => m.IsChecked); foreach (var material in Materials) { material.IsChecked = !allChecked; } } private void CheckAllSampling() { bool allChecked = Samplings.All(s => s.IsChecked); foreach (var sampling in Samplings) { sampling.IsChecked = !allChecked; } } private async Task Close() { await Application.Current.MainPage.Navigation.PopAsync(); } private async Task CopyEmail() { if (!string.IsNullOrEmpty(_doctor.EmailAddress)) { await Clipboard.SetTextAsync(_doctor.EmailAddress); await Application.Current.MainPage.DisplayAlert("Copied", "Email address copied to clipboard", "OK"); } } private async Task CallPhone() { if (!string.IsNullOrEmpty(_doctor.PhoneNo)) { try { if (PhoneDialer.Default.IsSupported) { PhoneDialer.Default.Open(_doctor.PhoneNo); } else { await Clipboard.SetTextAsync(_doctor.PhoneNo); await Application.Current.MainPage.DisplayAlert("Phone Number", "Phone number copied to clipboard", "OK"); } } catch (Exception ex) { await Application.Current.MainPage.DisplayAlert("Error", $"Unable to dial: {ex.Message}", "OK"); } } } private void OnSignatureCompleted(byte[] signatureBytes, string signatureFilePath) { // Handle the completed signature here // You can save to database, upload to server, etc. // Example: Save the transaction record // var transaction = new DoctorTransaction // { // DoctorId = _doctor.DoctorId, // SignatureFilePath = signatureFilePath, // TransactionDate = DateTime.Now, // Materials = checkedMaterials, // Samplings = checkedSamplings // }; // await _repository.SaveAsync(transaction); } #endregion } }