using Serilog;
using Tiku.Application;
using Tiku.Infrastructure;
using Tiku.Infrastructure.Security;
using Tiku.Api.Caching;
using Tiku.Api.BackgroundProcessing;
using Microsoft.AspNetCore.ResponseCompression;
using System.IO.Compression;
using Tiku.Application.PlatformBilling;
using Tiku.Application.Security;
using Tiku.Application.Tenancy;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Tiku.Api.Configuration;

public static class DependencyInjection
{
    public static WebApplicationBuilder AddApiServices(this WebApplicationBuilder builder)
    {
        builder.Services.AddSerilog((services, configuration) => configuration
            .ReadFrom.Configuration(builder.Configuration)
            .ReadFrom.Services(services)
            .Enrich.FromLogContext(),
            preserveStaticLogger: true);

        builder.Services.AddApiPresentation();
        builder.Services.AddHealthChecks();
        builder.Services.AddApiObservability(builder.Configuration, builder.Environment);
        builder.Services.AddApplication();
        builder.Services.AddNetworkConfiguration(builder.Configuration, builder.Environment);
        builder.Services.AddApiRateLimiting(builder.Configuration);

        var connectionString = Options.OptionsValidation.ResolveDatabaseConnectionString(
            builder.Configuration,
            builder.Environment.IsDevelopment());
        builder.Services.AddInfrastructure(connectionString);
        var redisConnectionString = builder.Configuration.GetConnectionString("Redis") ?? builder.Configuration["REDIS_URL"];
        builder.Services.AddOptions<RedisSecurityConnectionOptions>()
            .Configure(options => options.ConnectionString = redisConnectionString ?? string.Empty)
            .Validate(
                options => !builder.Environment.IsProduction() || !string.IsNullOrWhiteSpace(options.ConnectionString),
                "Redis is required in Production.")
            .ValidateOnStart();
        if (!string.IsNullOrWhiteSpace(redisConnectionString))
        {
            builder.Services.AddRedisSecurity(redisConnectionString, builder.Environment.EnvironmentName);
        }
        else if (builder.Environment.IsProduction())
        {
            throw new InvalidOperationException(
                "Redis is required in Production. Configure ConnectionStrings:Redis or REDIS_URL.");
        }

        builder.Services.AddOutputCache(options =>
        {
            options.AddPolicy("TenantPublic", new TenantPublicOutputCachePolicy());
            options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(60);
        });
        builder.Services.RemoveAll<ITenantPublicCacheInvalidator>();
        builder.Services.AddSingleton<ITenantPublicCacheInvalidator, TenantPublicCacheInvalidator>();
        if (builder.Environment.IsProduction() && !string.IsNullOrWhiteSpace(redisConnectionString))
        {
            builder.Services.AddStackExchangeRedisOutputCache(options =>
            {
                options.Configuration = redisConnectionString;
                options.InstanceName = $"tiku:{builder.Environment.EnvironmentName.ToLowerInvariant()}:output:";
            });
        }
        builder.Services.AddResponseCompression(options =>
        {
            options.EnableForHttps = true;
            options.Providers.Add<BrotliCompressionProvider>();
            options.Providers.Add<GzipCompressionProvider>();
            options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(["application/json"]);
        });
        builder.Services.Configure<BrotliCompressionProviderOptions>(options => options.Level = CompressionLevel.Fastest);
        builder.Services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.Fastest);
        builder.Services.AddOptions<BackgroundProcessingOptions>()
            .Bind(builder.Configuration.GetSection(BackgroundProcessingOptions.SectionName))
            .Validate(BackgroundProcessingOptions.BeValid, "Background processing settings are invalid.")
            .ValidateOnStart();
        builder.Services.AddOptions<DomainLifecycleOptions>()
            .Bind(builder.Configuration.GetSection("TenantDomains"));
        builder.Services.AddOptions<SaasSubscriptionLifecycleOptions>()
            .Bind(builder.Configuration.GetSection("SaasSubscriptions"));
        builder.Services.AddOptions<FeatureUsageReconciliationOptions>()
            .Bind(builder.Configuration.GetSection("FeatureUsageReconciliation"));
        builder.Services.AddHostedService<TenantDomainBackgroundService>();
        builder.Services.AddHostedService<SaasSubscriptionBackgroundService>();
        builder.Services.AddHostedService<FeatureUsageBackgroundService>();
        builder.Services.AddHostedService<BackgroundJobsBackgroundService>();

        builder.Services.AddApiDataProtection(builder.Configuration, builder.Environment);
        builder.Services.AddExternalServiceOptions(builder.Configuration, builder.Environment);
        builder.Services.AddApiAuthenticationAndAuthorization(builder.Configuration, builder.Environment);

        return builder;
    }
}
