using CMSMicroservice.Protobuf.Protos.OtpToken;
using CMSMicroservice.Protobuf.Protos.Package;
using CMSMicroservice.Protobuf.Protos.Role;
using CMSMicroservice.Protobuf.Protos.Products;
using CMSMicroservice.Protobuf.Protos.User;
using CMSMicroservice.Protobuf.Protos.UserAddress;
using CMSMicroservice.Protobuf.Protos.UserOrder;
using CMSMicroservice.Protobuf.Protos.UserRole;
using CMSMicroservice.Protobuf.Protos.Category;
using CMSMicroservice.Protobuf.Protos.Commission;
using CMSMicroservice.Protobuf.Protos.ClubMembership;
using CMSMicroservice.Protobuf.Protos.Configuration;
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using CMSMicroservice.Protobuf.Protos.Health;
using CMSMicroservice.Protobuf.Protos.AppVersion;

// Discount Shop Proto Clients
using CMSMicroservice.Protobuf.Protos.DiscountProduct;
using CMSMicroservice.Protobuf.Protos.DiscountCategory;
using CMSMicroservice.Protobuf.Protos.DiscountOrder;
using CMSMicroservice.Protobuf.Protos.Tag;
using CMSMicroservice.Protobuf.Protos.ProductTag;
using CMSMicroservice.Protobuf.Protos.ManualPayment;
using CMSMicroservice.Protobuf.Protos;
using CMSMicroservice.Protobuf.Protos.Inventory;
using CMSMicroservice.Protobuf.Protos.BlogPost;
using CMSMicroservice.Protobuf.Protos.BlogCategory;
using CMSMicroservice.Protobuf.Protos.BlogPostImage;
using CMSMicroservice.Protobuf.Protos.SitePage;
using CMSMicroservice.Protobuf.Protos.SitePageSettings;
using CMSMicroservice.Protobuf.Protos.UserContract;
using CMSMicroservice.Protobuf.Protos.UserWallet;
using CMSMicroservice.Protobuf.Protos.UserWalletHistory;
using CMSMicroservice.Protobuf.Protos.PaymentTransaction;
using CMSMicroservice.Protobuf.Protos.Transactions;
using CMSMicroservice.Protobuf.Protos.UserPackagePurchase;
using CMSMicroservice.Protobuf.Protos.ClubMembershipCycle;
using CMSMicroservice.Protobuf.Protos.ClubMembershipCycleHistory;
using BackOffice.Common.Utilities;
using BackOffice.Services.DiscountProduct;
using BackOffice.Services.DiscountCategory;
using BackOffice.Services.DiscountOrder;
using BackOffice.Services.PublicMessage;
using BackOffice.Services.Tag;
using BackOffice.Services.Blog;
using BackOffice.Services.Content;
using Blazored.LocalStorage;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Grpc.Net.Client;
using Grpc.Net.Client.Web;
using Microsoft.AspNetCore.Components.Authorization;
using MudBlazor.Services;
using System.Text.Json;
using System.Text.Json.Serialization;


namespace Microsoft.Extensions.DependencyInjection;

public static class ConfigureServices
{
    public static IServiceCollection AddCommonServices(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddBlazoredLocalStorageAsSingleton(config =>
        {
            config.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
            config.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
            config.JsonSerializerOptions.IgnoreReadOnlyProperties = true;
            config.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
            config.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            config.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
            config.JsonSerializerOptions.WriteIndented = false;
        });
        services.AddAuthorizationCore();
        services.AddScoped<AuthenticationStateProvider, ApiAuthenticationStateProvider>();
        services.AddSingleton<ITokenProvider, AppTokenProvider>();
        services.AddSingleton<ITokenHandler, TokenHandler>();
        services.AddMudServices();
        services.AddGrpcServices(configuration);
        
        // Persian DateTime Service
        services.AddSingleton<BackOffice.Services.IPersianDateTimeService, BackOffice.Services.PersianDateTimeService>();
        
        // Application Services
        services.AddScoped<BackOffice.Services.Authorization.IAuthorizationService, BackOffice.Services.Authorization.AuthorizationService>();
        services.AddScoped<BackOffice.Services.AppVersion.IAppVersionService, BackOffice.Services.AppVersion.AppVersionService>();
        
        // Discount Shop Services
        services.AddScoped<IDiscountProductService, DiscountProductService>();
        services.AddScoped<IDiscountCategoryService, DiscountCategoryService>();
        services.AddScoped<IDiscountOrderService, DiscountOrderService>();
        services.AddScoped<IPublicMessageService, PublicMessageService>();
        services.AddScoped<ITagService, TagService>();
        
        // Blog & Content Management Services
        services.AddScoped<IBlogPostService, BlogPostService>();
        services.AddScoped<IBlogCategoryService, BlogCategoryService>();
        services.AddScoped<IBlogPostImageService, BlogPostImageService>();
        services.AddScoped<ISitePageService, SitePageService>();
        services.AddScoped<ISitePageSettingsService, SitePageSettingsService>();
        
        return services;
    }



    public static IServiceCollection AddGrpcServices(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddSingleton<CallInvoker>(sp =>
        {
            var config = sp.GetRequiredService<IConfiguration>();
            var baseUri = config["GwUrl"];

            if (string.IsNullOrWhiteSpace(baseUri))
            {
                throw new InvalidOperationException("Configuration value 'GwUrl' is missing or empty.");
            }

            var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()))
            {
                Timeout = TimeSpan.FromMinutes(10)
            };

            return CreateAuthenticatedChannel(baseUri, httpClient, sp);
        });

        services.AddTransient(sp => new OtpTokenContract.OtpTokenContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new PackageContract.PackageContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new ProductsContract.ProductsContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new RoleContract.RoleContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new UserContract.UserContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new UserAddressContract.UserAddressContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new UserOrderContract.UserOrderContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new UserRoleContract.UserRoleContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new CategoryContract.CategoryContractClient(sp.GetRequiredService<CallInvoker>()));
        
        // CMS Services (Commission, Network, Club, Configuration, Health)
        services.AddTransient(sp => new CommissionContract.CommissionContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new NetworkMembershipContract.NetworkMembershipContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new ClubMembershipContract.ClubMembershipContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new ConfigurationContract.ConfigurationContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new HealthContract.HealthContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new AppVersionContract.AppVersionContractClient(sp.GetRequiredService<CallInvoker>()));

        // Discount Shop Services
        services.AddTransient(sp => new DiscountProductContract.DiscountProductContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new DiscountCategoryContract.DiscountCategoryContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new DiscountOrderContract.DiscountOrderContractClient(sp.GetRequiredService<CallInvoker>()));

        // Public Message Service
        services.AddTransient(sp => new PublicMessageContract.PublicMessageContractClient(sp.GetRequiredService<CallInvoker>()));

        // Tag Management Services
        services.AddTransient(sp => new TagContract.TagContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new ProductTagContract.ProductTagContractClient(sp.GetRequiredService<CallInvoker>()));

        // Manual Payment Service
        services.AddTransient(sp => new ManualPaymentContract.ManualPaymentContractClient(sp.GetRequiredService<CallInvoker>()));

        // Inventory Management Service
        services.AddTransient(sp => new InventoryContract.InventoryContractClient(sp.GetRequiredService<CallInvoker>()));

        // Blog & Content Management Services
        services.AddTransient(sp => new BlogPostContract.BlogPostContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new BlogCategoryContract.BlogCategoryContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new BlogPostImageContract.BlogPostImageContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new SitePageContract.SitePageContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new SitePageSettingsContract.SitePageSettingsContractClient(sp.GetRequiredService<CallInvoker>()));

        // User Contracts Service
        services.AddTransient(sp => new UserContractContract.UserContractContractClient(sp.GetRequiredService<CallInvoker>()));

        // User Wallet Services
        services.AddTransient(sp => new UserWalletContract.UserWalletContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new UserWalletHistoryContract.UserWalletHistoryContractClient(sp.GetRequiredService<CallInvoker>()));

        // Admin Report Services
        services.AddTransient(sp => new PaymentTransactionContract.PaymentTransactionContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new TransactionsContract.TransactionsContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new UserPackagePurchaseContract.UserPackagePurchaseContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new ClubMembershipCycleContract.ClubMembershipCycleContractClient(sp.GetRequiredService<CallInvoker>()));
        services.AddTransient(sp => new ClubMembershipCycleHistoryContract.ClubMembershipCycleHistoryContractClient(sp.GetRequiredService<CallInvoker>()));

        return services;
    }

    private static CallInvoker CreateAuthenticatedChannel(string address, HttpClient httpClient, IServiceProvider serviceProvider)
    {
        var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
        {
            var provider = serviceProvider.GetRequiredService<ITokenProvider>();
            var token = await provider.GetTokenAsync();
            if (!string.IsNullOrEmpty(token))
            {
                metadata.Add("Authorization", $"Bearer {token}");
            }
            await Task.CompletedTask;
        });

        // تشخیص HTTP یا HTTPS
        var isHttps = address.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
    
        var channelCredentials = isHttps
            ? ChannelCredentials.Create(new SslCredentials(), credentials)
            : ChannelCredentials.Create(ChannelCredentials.Insecure, credentials);

        var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
        {
            UnsafeUseInsecureChannelCallCredentials = !isHttps,  // فقط برای HTTP
            Credentials = channelCredentials,
            HttpClient = httpClient,
            MaxReceiveMessageSize = 1000 * 1024 * 1024,
            MaxSendMessageSize = 1000 * 1024 * 1024
        });
        return channel.Intercept(new ErrorHandlerInterceptor());
    }
}
