using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Validation.AspNetCore;
using OpenIddict.Validation.SystemNetHttp;
using StackExchange.Redis;
using Ablera.Serdica.Authentication.Models;
using Ablera.Serdica.Authentication.Models.Oidc;
using Ablera.Serdica.Authentication.Utilities;
using Microsoft.AspNetCore.DataProtection;
using Ablera.Serdica.Authentication.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Ablera.Serdica.Authentication.Constants;
using OpenIddict.Client;
using OpenIddict.Validation;
using System.Linq;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using System.Security.Principal;
using OpenIddict.Client.AspNetCore;

using Microsoft.AspNetCore.Authorization;
using Ablera.Serdica.DependencyInjection;


using static Ablera.Serdica.Authentication.Constants.ConstantsClass;
using static OpenIddict.Abstractions.OpenIddictConstants;
using System.IdentityModel.Tokens.Jwt;
using static OpenIddict.Client.OpenIddictClientEvents;

namespace Ablera.Serdica.DependencyInjection;
public sealed class AcceptAnyIssuer :
    IOpenIddictClientHandler<OpenIddict.Client.OpenIddictClientEvents.HandleConfigurationResponseContext> 
{
    public ValueTask HandleAsync(HandleConfigurationResponseContext ctx)
    {
        // Short-circuit the built-in ValidateIssuer handler.
        ctx.SkipRequest();
        return default;
    }
}

public static class JwtBearerWithSessionAuthenticationExtensions
{
    public static IServiceCollection AddDataProtection(this IServiceCollection services, IConfiguration configuration)
    {
        //------------------------------------------------------------------
        // 1)  read configuration
        //------------------------------------------------------------------
        var redisConfiguration = RedisConfigurationGetter.GetRedisConfiguration(configuration);
        var multiplexer = ConnectionMultiplexer.Connect(redisConfiguration);
        services.AddSingleton<IConnectionMultiplexer>(multiplexer);

        //------------------------------------------------------------------
        // 2)  Data-Protection (encrypt/sign cookies) – keys stored in Redis
        //------------------------------------------------------------------

        var xmlRepo = new RedisAndFileSystemXmlRepository(
                          multiplexer.GetDatabase(), RedisKeyPrefixKey);

        services.AddDataProtection()
                .SetApplicationName(DataProtectionApplicationName)
                .PersistKeysToStackExchangeRedis(multiplexer, RedisKeyPrefixKey)
                .AddKeyManagementOptions(o => o.XmlRepository = xmlRepo)
                .SetDefaultKeyLifetime(TimeSpan.FromDays(30));

        return services;
    }
    public static IServiceCollection AddMicroserviceAuthentication(
    this IServiceCollection services,
    IConfiguration cfg,
    IHostEnvironment env)
    {
        // ---------------------------------------------------------------------
        // 1) Read and validate the OIDC client settings
        // ---------------------------------------------------------------------
        var oidc = cfg.GetSection(nameof(OidcValidation)).Get<OidcValidation>()
                  ?? throw new InvalidOperationException($"{nameof(OidcValidation)} section is missing.");

        if (string.IsNullOrWhiteSpace(oidc.EncryptionKey))
            throw new InvalidOperationException($"{nameof(oidc.EncryptionKey)} is not defined.");

        // Issuer value found in the `iss` claim of the tokens (HTTPS as issued by the IdP)
        var issuerUrl = new Uri(oidc.IssuerUrl
            ?? throw new InvalidOperationException($"{nameof(oidc.IssuerUrl)} is not defined."));

        services.Configure<OidcValidation>(cfg.GetSection(nameof(OidcValidation)));

        services
            .AddDataProtection(cfg)
            .AddOpenIddict()
            .AddValidation(opt =>
            {
                opt.UseSystemNetHttp();
                opt.UseAspNetCore();
                opt.SetIssuer(issuerUrl);
                if (!string.IsNullOrWhiteSpace(oidc.ConfigurationUrl))
                {
                    opt.Configure(x =>
                    {
                        x.ConfigurationEndpoint = new Uri(oidc.ConfigurationUrl);
                    });
                }
                opt.AddEncryptionKey(
                    new SymmetricSecurityKey(Convert.FromBase64String(oidc.EncryptionKey)));
            });
        services.AddAuthorization(options =>
                options.FallbackPolicy = new AuthorizationPolicyBuilder()
                                             .RequireAuthenticatedUser()
                                             .Build())
                .AddAuthentication(options =>
                {
                    options.DefaultScheme = ConstantsClass.AuthenticationScheme;
                    options.DefaultChallengeScheme = ConstantsClass.AuthenticationScheme;
                })
                .AddScheme<JwtBearerOptions, SerdicaJwtBearerAuthenticationHandler>(
                    ConstantsClass.AuthenticationScheme, _ => { });

        return services;
    }

}