using LSFE.MobApp.Contracts;
using LSFE.MobApp.Contracts.Doctor;
using LSFE.MobApp.Entities;
using LSFE.MobApp.Entities.Account;
using LSFE.MobApp.Entities.Doctor;
using LSFE.MobApp.Entities.Location;
using LSFE.MobApp.Entities.Planning;
using LSFE.MobApp.Helpers;
using LSFE.MobApp.Pages.Admin;
using LSFE.MobApp.Pages.Attendance;
using LSFE.MobApp.Pages.Authentication;
using LSFE.MobApp.Pages.CoveragePlan;
using LSFE.MobApp.Pages.Utilities;
using LSFE.MobApp.Services;
using LSFE.MobApp.Services.Coverage;
using LSFE.MobApp.Services.Doctor;
using LSFE.MobApp.ViewModels;
using LSFE.MobApp.ViewModels.Admin;
using LSFE.MobApp.ViewModels.Authentication;
using LSFE.MobApp.ViewModels.Coverage;
using LSFE.MobApp.ViewModels.Utility;
using LSFE.MobApp.Views.CoveragePlan;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Reflection;

namespace LSFE.MobApp
{
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                    fonts.AddFont("Font-Awesome-7-Free-Solid-900.otf", "FontAwesome");
                    fonts.AddFont("materialdesignicons-webfont.ttf", "MaterialIconsRegular");
                });
            // Register logging
            builder.Services.AddLogging();
#if DEBUG
            builder.Logging.AddDebug();
#endif
            // Add HttpClient
            builder.Services.AddHttpClient("ApiClient")
            .ConfigurePrimaryHttpMessageHandler(() =>
            {
#if DEBUG
                return new HttpClientHandler
                {
                    ServerCertificateCustomValidationCallback =
                        (message, cert, chain, errors) => true
                };
#else
           return new HttpClientHandler
                {
                    ServerCertificateCustomValidationCallback =
                        (message, cert, chain, errors) => true
                };
#endif
            });
            builder.Services.AddHttpClient();
            // Add API and sync services
            builder.Services.AddSingleton<AddressService>();
            builder.Services.AddSingleton<IDeviceInfoService, DeviceInfoService>();
            builder.Services.AddSingleton<IApiClientFactory, ApiClientFactory>();
            builder.Services.AddSingleton<ISyncService, SyncService>();
            builder.Services.AddSingleton<ISyncService>(provider =>
            {
                var apiClientFactory = provider.GetService<IApiClientFactory>();
                var addressService = provider.GetService<AddressService>();
                
                var institutionRepo = provider.GetService<Repository<Institution>>();
                var doctorRepo = provider.GetService<Repository<Entities.Doctor.Doctor>>();
                var attendanceRepo = provider.GetService<Repository<Attendance>>();
                var productRepo = provider.GetService<Repository<Entities.Planning.ProductTransaction>>(); 
                var inventoryTransactionRepo = provider.GetService<Repository<Entities.Planning.InventoryTransaction>>();
                var mrRawPlanRepo = provider.GetService<Repository<Entities.Planning.MRRawPlan>>();
                var userRepo = provider.GetService<Repository<Entities.Account.User>>();

                var configuration = provider.GetService<IConfiguration>();
                var fileDownloadService = provider.GetService<IFileDownloadService>();

                // Create wrappers
                var institutionWrapper = new RepositoryWrapper<Institution>(institutionRepo);
                var doctorWrapper = new RepositoryWrapper<Entities.Doctor.Doctor>(doctorRepo);
                var attendanceWrapper = new RepositoryWrapper<Attendance>(attendanceRepo);
                var productWrapper = new RepositoryWrapper<Entities.Planning.ProductTransaction>(productRepo);
                var inventoryWrapper = new RepositoryWrapper<Entities.Planning.InventoryTransaction>(inventoryTransactionRepo);
                var mrRawPlanWrapper = new RepositoryWrapper<Entities.Planning.MRRawPlan>(mrRawPlanRepo);
                var userWrapper = new RepositoryWrapper<Entities.Account.User>(userRepo);

                return new SyncService(apiClientFactory, institutionWrapper, doctorWrapper,
                    attendanceWrapper, productWrapper, inventoryWrapper, mrRawPlanWrapper, userWrapper,
                    configuration,fileDownloadService, addressService);
            });


            // Load appsettings.json from EmbeddedResource
            var assembly = Assembly.GetExecutingAssembly();
            using var stream = assembly.GetManifestResourceStream("LSFE.MobApp.appsettings.json");

            var configuration = new ConfigurationBuilder()
                .AddJsonStream(stream)
                .Build();

            #region Register IConfiguration for DI
            builder.Configuration.AddConfiguration(configuration);
            builder.Services.AddSingleton<IConfiguration>(configuration);

            // Register database service
            builder.Services.AddSingleton<IDatabaseService, DatabaseService>();
            // Register file download service
            builder.Services.AddSingleton<IFileDownloadService, FileDownloadService>();
            #endregion

            #region Register IRepository<T> wrappers dependencies
            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<User>>>();
                return new Repository<User>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Attendance>>>();
                return new Repository<Attendance>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<AddressCache>>>();
                return new Repository<AddressCache>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Institution>>>();
                return new Repository<Institution>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<DSMScope>>>();
                return new Repository<DSMScope>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Inventory>>>();
                return new Repository<Inventory>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<MobileInfo>>>();
                return new Repository<MobileInfo>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Doctor>>>();
                return new Repository<Doctor>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Rating>>>();
                return new Repository<Rating>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Area>>>();
                return new Repository<Area>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<District>>>();
                return new Repository<District>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Division>>>();
                return new Repository<Division>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Territory>>>();
                return new Repository<Territory>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Cycle>>>();
                return new Repository<Cycle>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<MRPlanDetail>>>();
                return new Repository<MRPlanDetail>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<MRPlanHeader>>>();
                return new Repository<MRPlanHeader>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<MRRawPlan>>>();
                return new Repository<MRRawPlan>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Product>>>();
                return new Repository<Product>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<ProductTransaction>>>();
                return new Repository<ProductTransaction>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<InventoryTransaction>>>();
                return new Repository<InventoryTransaction>(databaseService.Database, logger);
            });

            builder.Services.AddTransient(provider =>
            {
                var databaseService = provider.GetRequiredService<IDatabaseService>();
                var logger = provider.GetRequiredService<ILogger<Repository<Specialization>>>();
                return new Repository<Specialization>(databaseService.Database, logger);
            });

            builder.Services.AddTransient<IRepository<User>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<User>>();
                return new RepositoryWrapper<User>(repo);
            });

            builder.Services.AddTransient<IRepository<Attendance>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Attendance>>();
                return new RepositoryWrapper<Attendance>(repo);
            });
            builder.Services.AddTransient<IRepository<AddressCache>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<AddressCache>>();
                return new RepositoryWrapper<AddressCache>(repo);
            });
            
            builder.Services.AddTransient<IRepository<Institution>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Institution>>();
                return new RepositoryWrapper<Institution>(repo);
            });

            builder.Services.AddTransient<IRepository<DSMScope>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<DSMScope>>();
                return new RepositoryWrapper<DSMScope>(repo);
            });

            builder.Services.AddTransient<IRepository<Inventory>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Inventory>>();
                return new RepositoryWrapper<Inventory>(repo);
            });

            builder.Services.AddTransient<IRepository<MobileInfo>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<MobileInfo>>();
                return new RepositoryWrapper<MobileInfo>(repo);
            });

            builder.Services.AddTransient<IRepository<Doctor>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Doctor>>();
                return new RepositoryWrapper<Doctor>(repo);
            });

            builder.Services.AddTransient<IRepository<Rating>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Rating>>();
                return new RepositoryWrapper<Rating>(repo);
            });

            builder.Services.AddTransient<IRepository<Area>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Area>>();
                return new RepositoryWrapper<Area>(repo);
            });

            builder.Services.AddTransient<IRepository<District>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<District>>();
                return new RepositoryWrapper<District>(repo);
            });

            builder.Services.AddTransient<IRepository<Division>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Division>>();
                return new RepositoryWrapper<Division>(repo);
            });

            builder.Services.AddTransient<IRepository<Territory>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Territory>>();
                return new RepositoryWrapper<Territory>(repo);
            });

            builder.Services.AddTransient<IRepository<Cycle>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Cycle>>();
                return new RepositoryWrapper<Cycle>(repo);
            });

            builder.Services.AddTransient<IRepository<MRPlanDetail>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<MRPlanDetail>>();
                return new RepositoryWrapper<MRPlanDetail>(repo);
            });

            builder.Services.AddTransient<IRepository<MRPlanHeader>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<MRPlanHeader>>();
                return new RepositoryWrapper<MRPlanHeader>(repo);
            });

            builder.Services.AddTransient<IRepository<MRRawPlan>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<MRRawPlan>>();
                return new RepositoryWrapper<MRRawPlan>(repo);
            });

            builder.Services.AddTransient<IRepository<Product>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Product>>();
                return new RepositoryWrapper<Product>(repo);
            });
            builder.Services.AddTransient<IRepository<ProductTransaction>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<ProductTransaction>>();
                return new RepositoryWrapper<ProductTransaction>(repo);
            });
            builder.Services.AddTransient<IRepository<InventoryTransaction>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<InventoryTransaction>>();
                return new RepositoryWrapper<InventoryTransaction>(repo);
            });
            
            builder.Services.AddTransient<IRepository<Specialization>>(provider =>
            {
                var repo = provider.GetRequiredService<Repository<Specialization>>();
                return new RepositoryWrapper<Specialization>(repo);
            });
            #endregion

            #region Register Services
            builder.Services.AddTransient<IDoctorService, DoctorService>();
            builder.Services.AddTransient<IQueryService, QueryService>();
            builder.Services.AddSingleton<Services.INavigationService, NavigationService>();
            #endregion

            #region VM and Pages
            builder.Services.AddTransient<DeviceRegistrationVM>();
            builder.Services.AddTransient<DeviceRegistrationPage>();

            builder.Services.AddTransient<LoginVM>();
            builder.Services.AddTransient<LoginPage>();

            builder.Services.AddTransient<ForgotPasswordVM>();
            builder.Services.AddTransient<ForgotPasswordPage>();

            builder.Services.AddTransient<AttendanceVM>();
            builder.Services.AddTransient<AttendancePage>();

            builder.Services.AddTransient<DoctorsVM>();
            builder.Services.AddTransient<DoctorVM>();//binding for UI
            builder.Services.AddTransient<DoctorsPage>();
         
            builder.Services.AddTransient<ActualCoverageVM>();
            builder.Services.AddTransient<ActualCoveragePage>();

            builder.Services.AddTransient<MainPageVM>();
            builder.Services.AddTransient<MainPage>();

            builder.Services.AddTransient<AddEditDoctorVM>();
            builder.Services.AddTransient<AddEditDoctorPage>();

            builder.Services.AddTransient<ViewDoctorVM>();
            builder.Services.AddTransient<ViewDoctorPage>();

            builder.Services.AddTransient<MarketProductVM>();
            builder.Services.AddTransient<MarketProductPage>();

            builder.Services.AddTransient<DoctorSignatureVM>();
            builder.Services.AddTransient<DoctorSignaturePage>();

            builder.Services.AddTransient<ConfirmSelectionVM>();
            builder.Services.AddTransient<ConfirmSelectionModal>();

            builder.Services.AddTransient<ReasonModalVM>();
            builder.Services.AddTransient<ReasonModal>();

            // Register ViewModel
            builder.Services.AddTransient<UtilitiesVM>();
            builder.Services.AddTransient<UtilitiesPage>();
            #endregion

            return builder.Build();
        }
    }
}