using LSFE.Infrastructure.Dto.Maintenance;
using LSFE.Infrastructure.Dto.Planning;
using LSFE.Infrastructure.Entities.Maintenance;
using LSFE.MobApp.Contracts;
using LSFE.MobApp.Entities.Account;
using LSFE.MobApp.Entities.Location;
using LSFE.MobApp.Mapper;
using LSFE.MobApp.Models;
using LSFE.MobApp.Services.Authentication;
using LSFE.MobApp.Services.Coverage;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LSFE.MobApp.Services
{
    public class SyncService : ISyncService
    {
        #region Api Client Factory
        private readonly IApiClientFactory _apiClientFactory;
        private readonly IApiClient<Entities.Doctor.Doctor> _doctorApiClient;
        private readonly IApiClient<ProductTransactionDto> _productTransApiClient;
        private readonly IApiClient<InventoryTransaction> _inventoryTransApiClient;
        private readonly IApiClient<MRRawPlansDto> _mrRawPlanApiClient;
        private readonly IApiClient<LoginResponse> _userApiClient;
        #endregion

        #region Local Repository
        private readonly IRepository<Institution> _institutionRepository;
        private readonly IRepository<Entities.Account.Attendance> _attendanceRepository;
        private readonly IRepository<Entities.Doctor.Doctor> _doctorRepository;
        private readonly IRepository<Entities.Planning.ProductTransaction> _productTransRepository;
        private readonly IRepository<Entities.Planning.InventoryTransaction> _inventoryTransRepository;
        private readonly IRepository<Entities.Planning.MRRawPlan> _mrRawPlanRepository;
        private readonly IRepository<Entities.Account.User> _userRepository;
        #endregion

        #region Helper
        private readonly IFileDownloadService _fileDownloadService;
        #endregion
        private readonly IConfiguration _configuration;
        private readonly AddressService _addressService;
        public SyncService(
            IApiClientFactory apiClientFactory,
            IRepository<Institution> institutionRepository,
            IRepository<Entities.Doctor.Doctor> doctor,
            IRepository<Entities.Account.Attendance> attendanceRepository,
            IRepository<Entities.Planning.ProductTransaction> productTransRepository,
            IRepository<Entities.Planning.InventoryTransaction> inventoryTransRepository,
            IRepository<Entities.Planning.MRRawPlan> mrRawPlanRepository,
            IRepository<Entities.Account.User> userRepository,
            IConfiguration configuration,
            IFileDownloadService fileDownloadService,
            AddressService addressService)
        {
            #region Api Constructor
            _configuration = configuration;
            _apiClientFactory = apiClientFactory;
            _doctorApiClient = apiClientFactory.CreateClient<Entities.Doctor.Doctor>("DoctorMgmt/GetDoctorCoverageToday");
            _productTransApiClient = apiClientFactory.CreateClient<ProductTransactionDto>("Plan/GetDoctorProductToday");
            _inventoryTransApiClient = apiClientFactory.CreateClient<InventoryTransaction>("Plan/GetDoctorSampleToday");
            _mrRawPlanApiClient = apiClientFactory.CreateClient<MRRawPlansDto>("Plan/GetMRRawPlans");
            _userApiClient = apiClientFactory.CreateClient<LoginResponse>(_configuration["ApiSettings:Login"] ?? "AnonAccount/Login/");
            #endregion

            #region Local Constructor
            _institutionRepository = institutionRepository;
            _doctorRepository = doctor;
            _productTransRepository = productTransRepository;
            _inventoryTransRepository = inventoryTransRepository;
            _attendanceRepository = attendanceRepository;
            _mrRawPlanRepository = mrRawPlanRepository;
            _userRepository = userRepository;
            #endregion

            #region Helper
            _fileDownloadService = fileDownloadService;
            _addressService = addressService;
            #endregion
        }
        #region Sync Pull
        public async Task<bool> SyncPullUsersAsync(User users)
        {
            try
            {
                var result = await _userRepository.SaveAsync(users);

                if (result > 0)
                {
                   return true;
                }

                return false;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Sync error: {ex.Message}");
                return false;
            }
        }
        public async Task<bool> SyncPullMRRawPlansAsync()
        {
            try
            {
                var cred =await UserCred.GetUserCred();

                if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
                {
                    var userResponse = await _userApiClient.CreateAsync(cred);
                    cred.Token = userResponse.Token;
                }

                var serverPlans = await _mrRawPlanApiClient.GetWithQueryAsync(new Dictionary<string, string>
                {
                    { "UserId", cred.UserId },
                    { "UserName", cred.UserName },
                    { "Token", cred.Token }
                });

                if (serverPlans == null || !serverPlans.Any())
                {
                    Debug.WriteLine("No data received from server");
                    return false;
                }

                // Get existing local data
                var localPlans = await _mrRawPlanRepository.GetAllAsync();

                int newCount = 0;
                int updatedCount = 0;

                foreach (var serverPlan in serverPlans)
                {
                    try
                    {
                        // Find existing local record by InstitutionId
                        var existingLocal = await _mrRawPlanRepository.
                            FirstOrDefaultAsync(x => x.MRRawPlanId == serverPlan.MRRawPlanId);

                        if (existingLocal == null)
                        {
                            // Create new local record
                            var newPlan = ApiEntityMapper.MapMRRawPlanFromApi(serverPlan);

                            var result = await _mrRawPlanRepository.SaveAsync(newPlan);

                            if (result > 0)
                            {
                                newCount++;
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to save: {newPlan.MRRawPlanId}");
                            }
                        }
                        else
                        {
                            // Update existing record
                            ApiEntityMapper.UpdateMRRawPlanFromApi(existingLocal, serverPlan);

                            var result = await _mrRawPlanRepository.UpdateAsync(existingLocal);

                            if (result > 0)
                            {
                                updatedCount++;
                                Debug.WriteLine($"✅ Updated: {existingLocal.MRRawPlanId}");
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to update: {existingLocal.MRRawPlanId}");
                            }
                        }
                    }
                    catch (Exception itemEx)
                    {
                        Debug.WriteLine($"Error processing mrRawPlan {serverPlan.MRRawPlanId}: {itemEx.Message}");
                    }
                }

                Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Sync error: {ex.Message}");
                return false;
            }
        }
        public async Task<bool> SyncPullProductsAsync()
        {
            try
            {
                var cred = await UserCred.GetUserCred();

                if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
                {
                    var userResponse = await _userApiClient.CreateAsync(cred);
                    cred.Token = userResponse.Token;
                }

                var serverProducts = await _productTransApiClient.GetWithQueryAsync(new Dictionary<string, string>
                {
                      { "UserId", cred.UserId },
                    { "UserName", cred.UserName },
                    { "Token", cred.Token }
                });

                if (serverProducts == null || !serverProducts.Any())
                {
                    Debug.WriteLine("No data received from server");
                    return false;
                }

                // Get existing local data
                var localProducts = await _productTransRepository.GetAllAsync();

                int newCount = 0;
                int updatedCount = 0;

                foreach (var serverProduct in serverProducts)
                {
                    try
                    {
                        // Find existing local record by InstitutionId
                        var existingLocal = await _productTransRepository.
                            FirstOrDefaultAsync(x => x.ProductTransId == serverProduct.ProductTransId);

                        if (existingLocal == null)
                        {
                            // Create new local record
                            var newProduct = ApiEntityMapper.MapProductTransFromApi(serverProduct);

                            var result = await _productTransRepository.SaveAsync(newProduct);

                            if (result > 0)
                            {
                                newCount++;
                                Debug.WriteLine($"✅ Successfully created: {newProduct.ProductName}");
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to save: {newProduct.ProductName}");
                            }
                        }
                        else
                        {
                            // Update existing record
                            ApiEntityMapper.UpdateProductTransFromApi(existingLocal, serverProduct);

                            var result = await _productTransRepository.UpdateAsync(existingLocal);

                            if (result > 0)
                            {
                                updatedCount++;
                                Debug.WriteLine($"✅ Updated: {existingLocal.ProductName}");
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to update: {existingLocal.ProductName}");
                            }
                        }
                    }
                    catch (Exception itemEx)
                    {
                        Debug.WriteLine($"Error processing institution {serverProduct.ProductId}: {itemEx.Message}");
                    }
                }

                Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Sync error: {ex.Message}");
                return false;
            }
        }
        public async Task<bool> SyncPullSamplesAsync()
        {
            try
            {
                var cred = await UserCred.GetUserCred();
                if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
                {
                    var userResponse = await _userApiClient.CreateAsync(cred);
                    cred.Token = userResponse.Token;
                }

                var serverInventoryTrans = await _inventoryTransApiClient.GetWithQueryAsync(new Dictionary<string, string>
                {
                      { "UserId", cred.UserId },
                    { "UserName", cred.UserName },
                    { "Token", cred.Token }
                });

                if (serverInventoryTrans == null || !serverInventoryTrans.Any())
                {
                    Debug.WriteLine("No data received from server");
                    return false;
                }

                // Get existing local data
                var localInventoryTrans = await _inventoryTransRepository.GetAllAsync();

                int newCount = 0;
                int updatedCount = 0;

                foreach (var serverInventoryTran in serverInventoryTrans)
                {
                    try
                    {
                        // Find existing local record by InventoryTransId
                        var existingLocal = await _inventoryTransRepository.
                            FirstOrDefaultAsync(x => x.InventoryTransId == serverInventoryTran.InventoryTransId);

                        if (existingLocal == null)
                        {
                            // Create new local record
                            var newProduct = ApiEntityMapper.MapInventoryTransFromApi(serverInventoryTran);

                            var result = await _inventoryTransRepository.SaveAsync(newProduct);

                            if (result > 0)
                            {
                                newCount++;
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to save: {newProduct.Description}");
                            }
                        }
                        else
                        {
                            // Update existing record
                            ApiEntityMapper.UpdateInventoryTransFromApi(existingLocal, serverInventoryTran);

                            var result = await _inventoryTransRepository.UpdateAsync(existingLocal);

                            if (result > 0)
                            {
                                updatedCount++;
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to update: {existingLocal.Description}");
                            }
                        }
                    }
                    catch (Exception itemEx)
                    {
                        Debug.WriteLine($"Error processing inventory {serverInventoryTran.InventoryTransId}: {itemEx.Message}");
                    }
                }

                //Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Sync error: {ex.Message}");
                return false;
            }
        }
        public async Task<bool> SyncPullInstitutionsAsync()
        {
            try
            {
                // Use the exact API entity name
                var apiClient = _apiClientFactory.CreateClient<Institutions>("Maintenance/institutions");
                var serverInstitutions = await apiClient.GetAllAsync();

                if (serverInstitutions == null || !serverInstitutions.Any())
                {
                    Debug.WriteLine("No data received from server");
                    return false;
                }

                Debug.WriteLine($"Received {serverInstitutions.Count} institutions from API");

                // Get existing local data
                var localInstitutions = await _institutionRepository.GetAllAsync();
                Debug.WriteLine($"Found {localInstitutions.Count} existing local institutions");

                int newCount = 0;
                int updatedCount = 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
                            var newInstitution = ApiEntityMapper.MapInstutionFromApi(serverInstitution);

                            Debug.WriteLine($"Creating new institution: {newInstitution.InstitutionName} (ServerID: {newInstitution.InstitutionId})");

                            var result = await _institutionRepository.SaveAsync(newInstitution);

                            if (result > 0)
                            {
                                newCount++;
                                Debug.WriteLine($"✅ Successfully created: {newInstitution.InstitutionName}");
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to save: {newInstitution.InstitutionName}");
                            }
                        }
                        else
                        {
                            // Update existing record
                            ApiEntityMapper.UpdateInstutionFromApi(existingLocal, serverInstitution);

                            var result = await _institutionRepository.UpdateAsync(existingLocal);

                            if (result > 0)
                            {
                                updatedCount++;
                                Debug.WriteLine($"✅ Updated: {existingLocal.InstitutionName}");
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to update: {existingLocal.InstitutionName}");
                            }
                        }
                    }
                    catch (Exception itemEx)
                    {
                        Debug.WriteLine($"Error processing institution {serverInstitution.InstitutionId}: {itemEx.Message}");
                    }
                }

                Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Sync error: {ex.Message}");
                return false;
            }
        }
        public async Task<bool> SyncPullDoctorsAsync()
        {
            try
            {

                var cred= await UserCred.GetUserCred();
                if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
                {
                    var userResponse = await _userApiClient.CreateAsync(cred);
                    cred.Token = userResponse.Token;
                    await SecureStorage.SetAsync("auth_token", userResponse.Token ?? "N/A");
                }

                var serverDoctors = await _doctorApiClient.GetWithQueryAsync(new Dictionary<string, string>
                {
                    { "UserId", cred.UserId },
                    { "UserName", cred.UserName },
                    { "Token", cred.Token }
                });

                if (serverDoctors == null || !serverDoctors.Any())
                {
                    Debug.WriteLine("No data received from server");
                    return false;
                }

                // Get existing local data and update new
                 await _doctorRepository.TruncateAsync();
                var localDoctor = await _doctorRepository.GetAllAsync();

                int newCount = 0;
                int updatedCount = 0;

                foreach (var serverDoc in serverDoctors)
                {
                    try
                    {
                        // Find existing local record by InstitutionId
                        var existingLocal = await _doctorRepository.
                            FirstOrDefaultAsync(x => x.DoctorId == serverDoc.DoctorId);

                        if (existingLocal == null)
                        {
                            // Create new local record
                            var newDoctor = ApiEntityMapper.MapDoctorFromApi(serverDoc);

                            Debug.WriteLine($"Creating new institution: {newDoctor.FirstName} (ServerID: {newDoctor.DoctorId})");

                            var result = await _doctorRepository.SaveAsync(newDoctor);

                            if (result > 0)
                            {
                                newCount++;
                                Debug.WriteLine($"✅ Successfully created: {newDoctor.FirstName}");
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to save: {newDoctor.FirstName}");
                            }
                        }
                        else
                        {
                            // Update existing record
                            ApiEntityMapper.UpdateDoctorFromApi(existingLocal, serverDoc);

                            var result = await _doctorRepository.UpdateAsync(existingLocal);

                            if (result > 0)
                            {
                                updatedCount++;
                                Debug.WriteLine($"✅ Updated: {existingLocal.FirstName}");
                            }
                            else
                            {
                                Debug.WriteLine($"❌ Failed to update: {existingLocal.FirstName}");
                            }
                        }
                    }
                    catch (Exception itemEx)
                    {
                        Debug.WriteLine($"Error processing institution {serverDoc.InstitutionId}: {itemEx.Message}");
                    }
                }

                Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Sync error: {ex.Message}");
                return false;
            }
        }
        public async Task<bool> SyncPullCoveragePlanTodayAsync()
        {
            await SyncPullDoctorsAsync();
            await SyncPullProductsAsync();
            await SyncPullSamplesAsync();
            await SyncPullMRRawPlansAsync();
            await DownloadAllMaterials();
            return await SyncPullInstitutionsAsync();
        }
        #endregion
        #region Sync Post
        public async Task<bool> SyncPostAttendanceAsync()
        {
            try
            {
                var today = DateTime.Today;
                var localAttendance = await _attendanceRepository.GetAllAsync();

                var _todayAttendance = localAttendance
                    .Where(a => a.TimeIn.HasValue && a.TimeIn.Value.Date == today)
                    .OrderByDescending(a => a.TimeIn)
                    .FirstOrDefault();

                // Add null check here
                if (_todayAttendance == null)
                {
                    Debug.WriteLine("No attendance record found for today");
                    return false;
                }
                var cred = await UserCred.GetUserCred();

                if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
                {
                    var userResponse = await _userApiClient.CreateAsync(cred);
                    cred.Token = userResponse.Token;
                }
                var apiClient = _apiClientFactory.
                    CreateClient<Entities.Account.Attendance>(_configuration["ApiSettings:Endpoints:PostAttendance"]);
                var serverAttendance = await apiClient.CreateAttendanceAsync(_todayAttendance);

                if (!serverAttendance.Success)
                {
                    Debug.WriteLine("No data received from server");
                    return false;
                }

                Debug.WriteLine($"Sync completed");
                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Sync error: {ex.Message}");
                return false;
            }
        }
        public async Task<bool> SyncPostCoverageTodayAsync(Guid mrRawPlanId)
        {
            try
            {
                // 1. Fetch the local record
                var localActualCoveragePlan = await _mrRawPlanRepository
                    .FirstOrDefaultAsync(a => a.MRRawPlanId == mrRawPlanId);

                // Validate the record exists
                if (localActualCoveragePlan == null)
                {
                    Debug.WriteLine($"[SYNC] No coverage plan found for ID: {mrRawPlanId}");
                    return false;
                }

                // Check if already synced (avoid redundant API calls)
                if (localActualCoveragePlan.IsSync)
                {
                    Debug.WriteLine($"[SYNC] Coverage plan {mrRawPlanId} already synced. Skipping.");
                    return true; // Already synced is a success
                }

                // 4. Validate required data before sending to API
                if (!ValidateCoveragePlan(localActualCoveragePlan))
                {
                    Debug.WriteLine($"[SYNC] Coverage plan {mrRawPlanId} validation failed");
                    return false;
                }
                if (localActualCoveragePlan.ForAddressTranslation)
                {
                    var (address, isHumanReadable) = await _addressService.
                        GetAddressAsync(double.Parse(localActualCoveragePlan.Latitude),
                        double.Parse(localActualCoveragePlan.Longitude));

                    localActualCoveragePlan.Location = address;
                }
                // 5. Create API client
                var apiClient = _apiClientFactory
                    .CreateClient<Entities.Planning.MRRawPlan>(
                        _configuration["ApiSettings:Endpoints:PostActualCoveragePlan"]);

                Debug.WriteLine($"[SYNC] Sending coverage plan {mrRawPlanId} to server...");

                // Send to server
                var serverResponse = await apiClient.CreateCoveragePlanAsync(localActualCoveragePlan);

                // Validate server response
                if (serverResponse == null)
                {
                    Debug.WriteLine($"[SYNC] Null response from server for {mrRawPlanId}");
                    return false;
                }

                if (!serverResponse.Success)
                {
                    Debug.WriteLine($"[SYNC] Server returned failure for {mrRawPlanId}. " +
                                  $"Message: {serverResponse.Message ?? "No message"}");
                    return false;
                }

                localActualCoveragePlan.IsSync = true;
                localActualCoveragePlan.LastSyncDate = DateTime.Now; 

                int updateResult = await _mrRawPlanRepository.UpdateAsync(localActualCoveragePlan);

                if (updateResult <= 0)
                {
                    Debug.WriteLine($"[SYNC] Failed to update local IsSync flag for {mrRawPlanId}");
                    // Note: Data is on server but local flag update failed
                    // Consider how to handle this edge case
                    return false;
                }

                Debug.WriteLine($"[SYNC] ✓ Coverage plan {mrRawPlanId} synced successfully");
                return true;
            }
            catch (HttpRequestException httpEx)
            {
                // Network-specific errors
                Debug.WriteLine($"[SYNC] Network error for {mrRawPlanId}: {httpEx.Message}");
                return false;
            }
            catch (TimeoutException timeoutEx)
            {
                // Timeout errors
                Debug.WriteLine($"[SYNC] Timeout error for {mrRawPlanId}: {timeoutEx.Message}");
                return false;
            }
            catch (Exception ex)
            {
                // General errors
                Debug.WriteLine($"[SYNC] Unexpected error for {mrRawPlanId}: {ex.Message}");
                Debug.WriteLine($"[SYNC] Stack trace: {ex.StackTrace}");
                return false;
            }
        }

        private bool ValidateCoveragePlan(Entities.Planning.MRRawPlan plan)
        {
            if (plan == null)
                return false;

            // Check required fields
            if (plan.MRRawPlanId == Guid.Empty)
            {
                Debug.WriteLine("[SYNC] Invalid MRRawPlanId");
                return false;
            }

            return true;
        }

        #endregion
        #region Download Materials & Samplings
        private async Task DownloadAllMaterials()
        {
            try
            {
                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 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");
            }
        }
        #endregion
    }
}
