using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using StellaOps.Feedser.Source.Common.Fetch;
using StellaOps.Feedser.Source.Common.Http;
using StellaOps.Feedser.Source.Vndr.Cisco.Configuration;
using StellaOps.Feedser.Source.Vndr.Cisco.Internal;

namespace StellaOps.Feedser.Source.Vndr.Cisco;

public static class CiscoServiceCollectionExtensions
{
    public static IServiceCollection AddCiscoConnector(this IServiceCollection services, Action<CiscoOptions> configure)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentNullException.ThrowIfNull(configure);

        services.AddOptions<CiscoOptions>()
            .Configure(configure)
            .PostConfigure(static opts => opts.Validate())
            .ValidateOnStart();

        services.TryAddSingleton<TimeProvider>(_ => TimeProvider.System);
        services.AddSingleton<CiscoDiagnostics>();
        services.AddSingleton<CiscoAccessTokenProvider>();
        services.AddTransient<CiscoOAuthMessageHandler>();

        services.AddHttpClient(CiscoOptions.AuthHttpClientName)
            .ConfigureHttpClient((sp, client) =>
            {
                var options = sp.GetRequiredService<IOptions<CiscoOptions>>().Value;
                client.Timeout = options.RequestTimeout;
                client.DefaultRequestHeaders.UserAgent.ParseAdd("StellaOps.Feedser.Cisco/1.0");
                client.DefaultRequestHeaders.Accept.ParseAdd("application/json");
                if (options.TokenEndpoint is not null)
                {
                    client.BaseAddress = new Uri(options.TokenEndpoint.GetLeftPart(UriPartial.Authority));
                }
            });

        services.AddSourceHttpClient(CiscoOptions.HttpClientName, static (sp, clientOptions) =>
        {
            var options = sp.GetRequiredService<IOptions<CiscoOptions>>().Value;
            clientOptions.Timeout = options.RequestTimeout;
            clientOptions.UserAgent = "StellaOps.Feedser.Cisco/1.0";
            clientOptions.AllowedHosts.Clear();
            clientOptions.AllowedHosts.Add(options.BaseUri.Host);
            clientOptions.AllowedHosts.Add("sec.cloudapps.cisco.com");
            clientOptions.AllowedHosts.Add("www.cisco.com");
            clientOptions.MaxAttempts = 5;
            clientOptions.BaseDelay = TimeSpan.FromSeconds(2);
        }).AddHttpMessageHandler<CiscoOAuthMessageHandler>();

        services.AddSingleton<CiscoOpenVulnClient>(sp =>
        {
            var fetchService = sp.GetRequiredService<SourceFetchService>();
            var optionsMonitor = sp.GetRequiredService<IOptionsMonitor<CiscoOptions>>();
            var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<CiscoOpenVulnClient>>();
            return new CiscoOpenVulnClient(fetchService, optionsMonitor, logger, VndrCiscoConnectorPlugin.SourceName);
        });

        services.AddSingleton<ICiscoCsafClient, CiscoCsafClient>();
        services.AddSingleton<CiscoDtoFactory>();
        services.AddTransient<CiscoConnector>();
        return services;
    }
}
