using System.Text;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.Providers;
using adas_core.module.ProxyDevices;
using adas_core.module.ProxyDevices.Services;
using adas_core.module.Relays.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Serilog;

namespace adas_core.Configurations
{
    /// <summary>
    /// Provides static extension methods for service-related operations.
    /// </summary>
    public static class ServiceExtension
    {
        /// <summary>
        /// Registers application configuration sections as strongly-typed options in the dependency injection container,
        /// binding Database, RabbitMQ, Providers, Recording, Relay, Api, Auth, Redis, Cache, and List settings to the
        /// corresponding configuration sections.
        /// </summary>
        /// <param name="builder">The <see cref="WebApplicationBuilder"/> whose services and configuration are used to register the option bindings.</param>
        public static void CreateConfigEntitiesFromAppSettingsSections(this WebApplicationBuilder builder)
        {
            builder.Services.Configure<DatabaseSettings>(builder.Configuration.GetSection("DatabaseSettings"));
            builder.Services.Configure<RabbitMqSettings>(builder.Configuration.GetSection("RabbitMQSettings"));
            builder.Services.Configure<List<ProvidersSettings>>(builder.Configuration.GetSection("ProvidersSettings"));
            builder.Services.Configure<RecordingSettings>(builder.Configuration.GetSection("RecordingSettings"));
            builder.Services.Configure<RelaySettings>(builder.Configuration.GetSection("RelaySettings"));
            builder.Services.Configure<ApiSettings>(builder.Configuration.GetSection("ApiSettings"));
            builder.Services.Configure<AuthSettings>(builder.Configuration.GetSection("AuthSettings"));
            builder.Services.Configure<RedisSettings>(builder.Configuration.GetSection("RedisSettings"));
            builder.Services.Configure<CacheSettings>(builder.Configuration.GetSection("CacheSettings"));
            builder.Services.Configure<ListSettings>(builder.Configuration.GetSection("ListSettings"));
        }

        /// <summary>
        /// Registers the proxy device service and its configuration in the application's dependency injection container,
        /// binding <see cref="ProxyDeviceSettings"/> from the "ProxyDevicesSettings" configuration section.
        /// </summary>
        /// <param name="builder">The <see cref="WebApplicationBuilder"/> used to configure services and application settings.</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="builder"/> is <c>null</c>.</exception>
        public static void UseProxyDevices(this WebApplicationBuilder builder)
        {
            if (builder == null)
                throw new ArgumentNullException(nameof(builder));

            builder.Services.AddSingleton<IProxyDeviceService, ProxyDeviceService>();
            builder.Services.Configure<ProxyDeviceSettings>(builder.Configuration.GetSection("ProxyDevicesSettings"));
        }

        /// <summary>
        /// Configures JWT Bearer authentication and authorization on the provided <see cref="WebApplicationBuilder"/> using settings bound from the "AuthSettings" configuration section.
        /// If the JWT configuration is missing, the method returns without making changes; if the secret key is empty, an error is logged and an exception is thrown.
        /// </summary>
        /// <param name="builder">The <see cref="WebApplicationBuilder"/> to which authentication and authorization services are added.</param>
        /// <exception cref="Exception">Thrown when the JWT signing key is not configured in the application settings.</exception>
        public static void ConfigureJwtAuthentication(this WebApplicationBuilder builder)
        {
            var auth = new AuthSettings();
            builder.Configuration.GetSection("AuthSettings").Bind(auth);
            var jwt = auth.JwtConfig;
            if (jwt == null)
                return;

            if (string.IsNullOrEmpty(jwt.Key))
            {
                Log.Error("Jwt secret is empty on appSettings.json");
                throw new Exception("Cant configure jwt without secret");
            }

            builder.Services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
                .AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;
                    options.SaveToken = true;
                    SymmetricSecurityKey symmetricSecurityKey = new(Encoding.UTF8.GetBytes(jwt.Key));
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = jwt.ValidateIssuer,
                        ValidateAudience = jwt.ValidateAudience,
                        ValidateIssuerSigningKey = true,
                        ValidAudience = jwt.Audience,
                        ValidIssuer = jwt.Issuer,
                        IssuerSigningKey = symmetricSecurityKey
                    };
                });

            builder.Services.AddAuthorization();
        }
    }
}