using JSMR.Application.Circles.Queries.GetCreators;
using JSMR.Application.Circles.Queries.GetTags;
using JSMR.Application.Circles.Queries.Search;
using JSMR.Application.Common.Caching;
using JSMR.Application.Creators.Ports;
using JSMR.Application.Creators.Queries.Search.Ports;
using JSMR.Application.Enums;
using JSMR.Application.Integrations.Chobit.Ports;
using JSMR.Application.Integrations.DLSite.Ports;
using JSMR.Application.Jobs;
using JSMR.Application.Scanning.Ports;
using JSMR.Application.Tags.Ports;
using JSMR.Application.Tags.Queries.Search.Ports;
using JSMR.Application.Users;
using JSMR.Application.VoiceWorks.Ports;
using JSMR.Application.VoiceWorks.Queries.Search;
using JSMR.Infrastructure.Caching;
using JSMR.Infrastructure.Caching.Adapters;
using JSMR.Infrastructure.Common.Languages;
using JSMR.Infrastructure.Common.SupportedLanguages;
using JSMR.Infrastructure.Common.Time;
using JSMR.Infrastructure.Data.Repositories.Circles;
using JSMR.Infrastructure.Data.Repositories.Creators;
using JSMR.Infrastructure.Data.Repositories.Jobs;
using JSMR.Infrastructure.Data.Repositories.Tags;
using JSMR.Infrastructure.Data.Repositories.Users;
using JSMR.Infrastructure.Data.Repositories.VoiceWorks;
using JSMR.Infrastructure.Http;
using JSMR.Infrastructure.Ingestion;
using JSMR.Infrastructure.Integrations.Chobit;
using JSMR.Infrastructure.Integrations.DLSite;
using JSMR.Infrastructure.Scanning;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Resilience;
using Polly;
using System.Net;

namespace JSMR.Infrastructure.DI;

public static class InfrastructureServiceCollectionExtensions
{
    public static IServiceCollection AddInfrastructure(this IServiceCollection services)
    {
        services.AddScoped<ICircleSearchProvider, CircleSearchProvider>();
        services.AddScoped<ICircleTagsProvider, CircleTagsProvider>();
        services.AddScoped<ICircleCreatorsProvider, CircleCreatorsProvider>();

        services.AddScoped<IVoiceWorkSearchProvider, VoiceWorkSearchProvider>();
        services.AddScoped<IVoiceWorkFullTextSearch, MySqlVoiceWorkFullTextSearch>();
        services.AddScoped<IVoiceWorkWriter, VoiceWorkWriter>();

        services.AddKeyedScoped<IVoiceWorksScanner, JapaneseVoiceWorksScanner>(Locale.Japanese);
        services.AddKeyedScoped<IVoiceWorksScanner, EnglishVoiceWorksScanner>(Locale.English);
        services.AddScoped<IVoiceWorkScannerRepository, VoiceWorkScannerRepository>();

        services.AddScoped<IReleasedWorksProvider, ReleasedWorksProvider>();
        services.AddScoped<IVoiceWorkIngestBuilder,  VoiceWorkIngestBuilder>();

        services.AddKeyedScoped<IVoiceWorkUpdater, VoiceWorkUpdater>(Locale.Japanese);
        services.AddKeyedScoped<IVoiceWorkUpdater, EnglishVoiceWorkUpdater>(Locale.English);
        services.AddScoped<IVoiceWorkUpdaterRepository, VoiceWorkUpdaterRepository>();

        services.AddScoped<IVoiceWorkSearchUpdater, VoiceWorkSearchUpdater>();

        //services.AddKeyedScoped<ISupportedLanguage, JapaneseLanguage>(Locale.Japanese);
        //services.AddKeyedScoped<ISupportedLanguage, EnglishLanguage>(Locale.English);
        //services.AddKeyedScoped<ISupportedLanguage, SimplifiedChineseLanguage>(Locale.ChineseSimplified);
        //services.AddKeyedScoped<ISupportedLanguage, TraditionalChineseLanguage>(Locale.ChineseTraditional);
        //services.AddKeyedScoped<ISupportedLanguage, KoreanLanguage>(Locale.Korean);

        services.AddScoped<ITagSearchProvider, TagSearchProvider>();
        services.AddScoped<ITagWriter, TagWriter>();

        services.AddScoped<ICreatorSearchProvider, CreatorSearchProvider>();
        services.AddScoped<ICreatorWriter, CreatorWriter>();

        services.AddScoped<IJobRepository, JobRepository>();
        services.AddScoped<IJobProgressWriter, JobProgressWriter>();

        services.AddSingleton<ICache, MemoryCacheAdapter>();
        services.AddSingleton<ISpamCircleCache, SpamCircleCache>();

        services.AddSingleton<ILanguageIdentifier, LanguageIdentifier>();

        services.AddSingleton<IClock, Clock>();
        services.AddSingleton<ITimeProvider, TokyoTimeProvider>();

        services.AddHttpServices();
        services.AddNewHttpServices();

        services.AddScoped<IUserRepository, UserRepository>();

        return services;
    }

    private static IServiceCollection AddHttpServices(this IServiceCollection services)
    {
        //services.AddHttpClient<IHttpService, HttpService>(client =>
        //{
        //    client.DefaultRequestHeaders.UserAgent.ParseAdd("JSMR/1.0");
        //});

        //services.AddScoped<IHttpService, HttpService>();
        services.AddScoped<IHtmlLoader, HtmlLoader>();

        // ONE registration for IHttpService as a typed client:
        services.AddHttpClient<IHttpService, HttpService>((sp, http) =>
        {
            http.BaseAddress = new Uri("https://www.dlsite.com/");
            http.DefaultRequestHeaders.UserAgent.ParseAdd("JSMR/1.0");
            http.Timeout = TimeSpan.FromSeconds(15);
        })
        .AddResilienceHandler("dlsite", builder => {
            builder.AddRetry(new HttpRetryStrategyOptions
            {
                MaxRetryAttempts = 3,
                UseJitter = true,
                Delay = TimeSpan.FromMilliseconds(200),
                BackoffType = DelayBackoffType.Exponential,
                ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                    .Handle<HttpRequestException>()
                    .HandleResult(r => (int)r.StatusCode >= 500 || (int)r.StatusCode == 429)
            });
        });

        // Register DLSiteClient as a normal scoped service
        services.AddScoped<IDLSiteClient, DLSiteClient>();

        return services;
    }

    private static IServiceCollection AddNewHttpServices(this IServiceCollection services)
    {
        services.AddHttpClient<IDLSiteClient, DLSiteClient>((sp, http) =>
        {
            http.BaseAddress = new Uri("https://www.dlsite.com/");
            http.DefaultRequestHeaders.UserAgent.ParseAdd("JSMR/1.0");
            http.Timeout = TimeSpan.FromSeconds(15);
        })
        .AddResilienceHandler("dlsite", builder =>
        {
            builder.AddRetry(new HttpRetryStrategyOptions
            {
                MaxRetryAttempts = 3,
                UseJitter = true,
                Delay = TimeSpan.FromMilliseconds(200),
                BackoffType = DelayBackoffType.Exponential,
                ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                    .Handle<HttpRequestException>()
                    .HandleResult(r => (int)r.StatusCode >= 500 || (int)r.StatusCode == 429)
            });
        });

        services.AddHttpClient<IChobitClient, ChobitClient>((sp, http) =>
        {
            http.BaseAddress = new Uri("https://chobit.cc/");
            http.DefaultRequestHeaders.UserAgent.ParseAdd("JSMR/1.0");
            http.Timeout = TimeSpan.FromSeconds(15);
        })
        .AddResilienceHandler("chobit", builder =>
        {
            builder.AddRetry(new HttpRetryStrategyOptions
            {
                MaxRetryAttempts = 3,
                UseJitter = true,
                Delay = TimeSpan.FromMilliseconds(200),
                BackoffType = DelayBackoffType.Exponential,
                ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                    .Handle<HttpRequestException>()
                    .HandleResult(r => (int)r.StatusCode >= 500 || (int)r.StatusCode == 429)
            });
        });

        return services;
    }
}