using System.Net;
using Microsoft.AspNetCore.HttpOverrides;
using Tiku.Api.Background;
using Tiku.Api.Options;
using Tiku.Application.PlatformAdmin;
using Tiku.Application.Tenancy;

namespace Tiku.Api.Configuration;

internal static class NetworkConfigurationExtensions
{
    internal static IServiceCollection AddNetworkConfiguration(
        this IServiceCollection services,
        IConfiguration configuration,
        IHostEnvironment environment)
    {
        services.AddOptions<TenantResolutionOptions>()
            .Bind(configuration.GetSection(TenantResolutionOptions.SectionName))
            .Validate(
                options => OptionsValidation.BeValidTenantResolutionOptions(options, configuration,
                    environment.IsProduction()),
                "Production requires formal platform hosts, non-wildcard AllowedHosts, and trusted proxy addresses.")
            .ValidateOnStart();
        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders =
                ForwardedHeaders.XForwardedFor |
                ForwardedHeaders.XForwardedHost |
                ForwardedHeaders.XForwardedProto;
            options.ForwardLimit = 1;
            options.KnownProxies.Clear();
            options.KnownIPNetworks.Clear();

            var resolution = configuration
                .GetSection(TenantResolutionOptions.SectionName)
                .Get<TenantResolutionOptions>() ?? new TenantResolutionOptions();
            foreach (var address in resolution.TrustedProxyAddresses)
                if (IPAddress.TryParse(address, out var proxy))
                    options.KnownProxies.Add(proxy);
        });
        services.AddOptions<DomainLifecycleOptions>()
            .Bind(configuration.GetSection("TenantDomains"))
            .Validate(options => environment.IsDevelopment() || !options.EnableDevelopmentLocalhostBypass,
                "The .localhost domain lifecycle bypass can only be enabled in Development.")
            .ValidateOnStart();
        if (environment.IsDevelopment()) services.AddHostedService<DevelopmentTenantDomainLifecycleHostedService>();
        services.AddOptions<TenantProvisioningOptions>()
            .Bind(configuration.GetSection(TenantProvisioningOptions.SectionName))
            .Validate(options => !string.IsNullOrWhiteSpace(options.DefaultBaseOfferingCode) &&
                                 options.DefaultTrialDays is >= 1 and <= 365 &&
                                 options.OwnerActivationMinutes is >= 5 and <= 1440 &&
                                 options.OwnerActivationUrlTemplate.Contains("{host}", StringComparison.Ordinal) &&
                                 Uri.TryCreate(
                                     options.OwnerActivationUrlTemplate.Replace("{host}", "tenant.example.com",
                                         StringComparison.Ordinal),
                                     UriKind.Absolute,
                                     out var activationOrigin) &&
                                 activationOrigin.Scheme == Uri.UriSchemeHttps &&
                                 ValidateDevelopmentActivationTemplate(options, environment.IsDevelopment()),
                "Tenant provisioning requires a default offering code, valid trial/activation durations, an HTTPS owner activation URL template, and permits an HTTP template only for Development .localhost sites.")
            .ValidateOnStart();
        if (environment.IsProduction()) services.AddHostedService<TenantProvisioningStartupValidator>();

        services.AddOptions<CorsOptions>()
            .Bind(configuration.GetSection(CorsOptions.SectionName))
            .ValidateDataAnnotations()
            .Validate(
                OptionsValidation.BeValidCorsOptions,
                "CORS origins must be absolute HTTP/HTTPS origins, and credentials require explicit origins.")
            .ValidateOnStart();

        var corsOptions = configuration
            .GetSection(CorsOptions.SectionName)
            .Get<CorsOptions>() ?? new CorsOptions();
        services.AddCors(options =>
        {
            options.AddPolicy(CorsOptions.PolicyName, policy =>
            {
                var origins = corsOptions.AllowedOrigins
                    .Where(origin => !string.IsNullOrWhiteSpace(origin))
                    .Select(origin => origin.Trim().TrimEnd('/'))
                    .Distinct(StringComparer.OrdinalIgnoreCase)
                    .ToArray();

                if (origins.Length > 0) policy.WithOrigins(origins);

                policy
                    .WithHeaders(corsOptions.AllowedHeaders)
                    .WithMethods(corsOptions.AllowedMethods);

                if (corsOptions.AllowCredentials) policy.AllowCredentials();
            });
        });

        return services;
    }

    private static bool ValidateDevelopmentActivationTemplate(
        TenantProvisioningOptions options,
        bool isDevelopment)
    {
        if (string.IsNullOrWhiteSpace(options.DevelopmentLocalhostOwnerActivationUrlTemplate)) return true;

        return isDevelopment &&
               options.DevelopmentLocalhostOwnerActivationUrlTemplate.Contains("{host}", StringComparison.Ordinal) &&
               Uri.TryCreate(
                   options.DevelopmentLocalhostOwnerActivationUrlTemplate.Replace(
                       "{host}", "tenant.localhost", StringComparison.Ordinal),
                   UriKind.Absolute,
                   out var developmentOrigin) &&
               (developmentOrigin.Scheme == Uri.UriSchemeHttp ||
                developmentOrigin.Scheme == Uri.UriSchemeHttps);
    }
}