using Hangfire;
using Hangfire.SqlServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Polly;
using Polly.Extensions.Http;
using Stripe;
using RealCV.Application.Interfaces;
using RealCV.Infrastructure.Configuration;
using RealCV.Infrastructure.Data;
using RealCV.Infrastructure.ExternalApis;
using RealCV.Infrastructure.Jobs;
using RealCV.Infrastructure.Services;

namespace RealCV.Infrastructure;

public static class DependencyInjection
{
    public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
    {
        // Configure DbContext with SQL Server
        // AddDbContextFactory enables thread-safe parallel operations
        services.AddDbContextFactory<ApplicationDbContext>(options =>
            options.UseSqlServer(
                configuration.GetConnectionString("DefaultConnection"),
                sqlOptions =>
                {
                    sqlOptions.EnableRetryOnFailure(
                        maxRetryCount: 3,
                        maxRetryDelay: TimeSpan.FromSeconds(30),
                        errorNumbersToAdd: null);
                }));

        // Also register DbContext for scoped injection (non-parallel scenarios)
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                configuration.GetConnectionString("DefaultConnection"),
                sqlOptions =>
                {
                    sqlOptions.EnableRetryOnFailure(
                        maxRetryCount: 3,
                        maxRetryDelay: TimeSpan.FromSeconds(30),
                        errorNumbersToAdd: null);
                }));

        // Configure Hangfire with SQL Server storage
        services.AddHangfire(config => config
            .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
            .UseSimpleAssemblyNameTypeSerializer()
            .UseRecommendedSerializerSettings()
            .UseSqlServerStorage(
                configuration.GetConnectionString("HangfireConnection"),
                new SqlServerStorageOptions
                {
                    CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                    SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                    QueuePollInterval = TimeSpan.Zero,
                    UseRecommendedIsolationLevel = true,
                    DisableGlobalLocks = true
                }));

        services.AddHangfireServer();

        // Configure options
        services.Configure<CompaniesHouseSettings>(
            configuration.GetSection(CompaniesHouseSettings.SectionName));

        services.Configure<AnthropicSettings>(
            configuration.GetSection(AnthropicSettings.SectionName));

        services.Configure<AzureBlobSettings>(
            configuration.GetSection(AzureBlobSettings.SectionName));

        services.Configure<LocalStorageSettings>(
            configuration.GetSection(LocalStorageSettings.SectionName));

        services.Configure<StripeSettings>(
            configuration.GetSection(StripeSettings.SectionName));

        // Configure Stripe API key
        var stripeSettings = configuration.GetSection(StripeSettings.SectionName).Get<StripeSettings>();
        if (!string.IsNullOrEmpty(stripeSettings?.SecretKey))
        {
            StripeConfiguration.ApiKey = stripeSettings.SecretKey;
        }

        // Configure HttpClient for CompaniesHouseClient with retry policy
        services.AddHttpClient<CompaniesHouseClient>((serviceProvider, client) =>
            {
                var settings = configuration
                    .GetSection(CompaniesHouseSettings.SectionName)
                    .Get<CompaniesHouseSettings>();

                if (settings is not null)
                {
                    client.BaseAddress = new Uri(settings.BaseUrl);
                }
            })
            .AddPolicyHandler(GetRetryPolicy());

        // Register services
        services.AddScoped<ICVParserService, CVParserService>();
        services.AddScoped<ICompanyNameMatcherService, AICompanyNameMatcherService>();
        services.AddScoped<ICompanyVerifierService, CompanyVerifierService>();
        services.AddScoped<IEducationVerifierService, EducationVerifierService>();
        services.AddScoped<ITimelineAnalyserService, TimelineAnalyserService>();
        services.AddScoped<ICVCheckService, CVCheckService>();
        services.AddScoped<IUserContextService, UserContextService>();
        services.AddScoped<IAuditService, AuditService>();
        services.AddScoped<IStripeService, StripeService>();
        services.AddScoped<ISubscriptionService, Services.SubscriptionService>();

        // Register file storage - use local storage if configured, otherwise Azure
        var useLocalStorage = configuration.GetValue<bool>("UseLocalStorage");
        if (useLocalStorage)
        {
            services.AddScoped<IFileStorageService, LocalFileStorageService>();
        }
        else
        {
            services.AddScoped<IFileStorageService, FileStorageService>();
        }

        // Register Hangfire jobs
        services.AddTransient<ProcessCVCheckJob>();
        services.AddTransient<ResetMonthlyUsageJob>();

        return services;
    }

    private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: retryAttempt =>
                    TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (outcome, timespan, retryAttempt, context) =>
                {
                    // Logging could be added here via ILogger if injected
                });
    }
}
