using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Mirea.Api.Security.Common.Domain;
using Mirea.Api.Security.Common.Interfaces;
using Mirea.Api.Security.Services;
using System;
using System.Collections.Generic;
using System.Text;

namespace Mirea.Api.Security;

public static class DependencyInjection
{
    private static ReadOnlyMemory<byte> NormalizeKey(string key, int requiredLength)
    {
        var keyBytes = Encoding.UTF8.GetBytes(key);

        if (keyBytes.Length < requiredLength)
        {
            var normalizedKey = new byte[requiredLength];
            Array.Copy(keyBytes, normalizedKey, keyBytes.Length);
            return new ReadOnlyMemory<byte>(normalizedKey);
        }

        if (keyBytes.Length > requiredLength)
            Array.Resize(ref keyBytes, requiredLength);

        return new ReadOnlyMemory<byte>(keyBytes);
    }

    public static IServiceCollection AddSecurityServices(this IServiceCollection services, IConfiguration configuration)
    {
        var saltSize = int.Parse(configuration["SECURITY_SALT_SIZE"]!);
        var hashSize = int.Parse(configuration["SECURITY_HASH_SIZE"]!);
        var iteration = int.Parse(configuration["SECURITY_HASH_ITERATION"]!);
        var memory = int.Parse(configuration["SECURITY_HASH_MEMORY"]!);
        var parallelism = int.Parse(configuration["SECURITY_HASH_PARALLELISM"]!);

        services.AddSingleton(new PasswordHashService
        {
            SaltSize = saltSize,
            HashSize = hashSize,
            Iterations = iteration,
            Memory = memory,
            Parallelism = parallelism,
            Secret = configuration["SECURITY_HASH_TOKEN"]
        });

        var lifeTimeRefreshToken = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_RT"]!));
        var lifeTimeFirstAuthToken = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_1_FA"]!));

        services.AddSingleton(provider =>
        {
            var cacheService = provider.GetRequiredService<ICacheService>();
            var accessTokenService = provider.GetRequiredService<IAccessToken>();
            var revokedTokenService = provider.GetRequiredService<IRevokedToken>();
            var logger = provider.GetRequiredService<ILogger<AuthService>>();
            var passwordService = provider.GetRequiredService<PasswordHashService>();

            return new AuthService(cacheService, accessTokenService, revokedTokenService, logger, passwordService)
            {
                Lifetime = lifeTimeRefreshToken,
                LifetimeFirstAuth = lifeTimeFirstAuthToken
            };
        });

        var providers = new Dictionary<OAuthProvider, (string ClientId, string Secret)>();

        foreach (var provider in Enum.GetValues<OAuthProvider>())
        {
            var providerName = Enum.GetName(provider)!.ToUpper();
            var clientId = configuration[$"{providerName}_CLIENT_ID"];
            var secret = configuration[$"{providerName}_CLIENT_SECRET"];

            if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(secret))
                continue;

            providers.Add(provider, (clientId, secret));
        }

        services.AddSingleton(provider => new OAuthService(
            provider.GetRequiredService<ILogger<OAuthService>>(),
            providers,
            provider.GetRequiredService<ICacheService>())
        {
            SecretKey = NormalizeKey(configuration["SECURITY_ENCRYPTION_TOKEN"]!, 32)
        });

        return services;
    }
}