using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using StellaOps.Feedser.Source.Common.Xml;

namespace StellaOps.Feedser.Source.Common.Http;

public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Registers a named HTTP client configured for a source connector with allowlisted hosts and sensible defaults.
    /// </summary>
    public static IHttpClientBuilder AddSourceHttpClient(this IServiceCollection services, string name, Action<SourceHttpClientOptions> configure)
        => services.AddSourceHttpClient(name, (_, options) => configure(options));

    public static IHttpClientBuilder AddSourceHttpClient(this IServiceCollection services, string name, Action<IServiceProvider, SourceHttpClientOptions> configure)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentException.ThrowIfNullOrEmpty(name);
        ArgumentNullException.ThrowIfNull(configure);

        services.AddOptions<SourceHttpClientOptions>(name).Configure<IServiceProvider>((options, sp) => configure(sp, options));

        return services
            .AddHttpClient(name)
            .ConfigureHttpClient((sp, client) =>
            {
                var options = sp.GetRequiredService<IOptionsMonitor<SourceHttpClientOptions>>().Get(name);

                if (options.BaseAddress is not null)
                {
                    client.BaseAddress = options.BaseAddress;
                }

                client.Timeout = options.Timeout;
                client.DefaultRequestHeaders.UserAgent.Clear();
                client.DefaultRequestHeaders.UserAgent.ParseAdd(options.UserAgent);

                foreach (var header in options.DefaultRequestHeaders)
                {
                    client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
                }
            })
            .ConfigurePrimaryHttpMessageHandler((sp) =>
            {
                var options = sp.GetRequiredService<IOptionsMonitor<SourceHttpClientOptions>>().Get(name).Clone();
                return new HttpClientHandler
                {
                    AllowAutoRedirect = options.AllowAutoRedirect,
                    AutomaticDecompression = System.Net.DecompressionMethods.All,
                };
            })
            .AddHttpMessageHandler(sp =>
            {
                var options = sp.GetRequiredService<IOptionsMonitor<SourceHttpClientOptions>>().Get(name).Clone();
                return new AllowlistedHttpMessageHandler(options);
            });
    }

    /// <summary>
    /// Registers shared helpers used by source connectors.
    /// </summary>
    public static IServiceCollection AddSourceCommon(this IServiceCollection services)
    {
        ArgumentNullException.ThrowIfNull(services);

        services.AddSingleton<Json.JsonSchemaValidator>();
        services.AddSingleton<Json.IJsonSchemaValidator>(sp => sp.GetRequiredService<Json.JsonSchemaValidator>());
        services.AddSingleton<XmlSchemaValidator>();
        services.AddSingleton<IXmlSchemaValidator>(sp => sp.GetRequiredService<XmlSchemaValidator>());
        services.AddSingleton<Fetch.IJitterSource, Fetch.CryptoJitterSource>();
        services.AddSingleton<Fetch.RawDocumentStorage>();
        services.AddSingleton<Fetch.SourceFetchService>();

        return services;
    }
}
