namespace MemberCenter.Infrastructure.Configuration;

public sealed class IdentitySecurityOptions
{
    public PasswordPolicyOptions Password { get; set; } = new();
    public LockoutPolicyOptions Lockout { get; set; } = new();
}

public sealed class PasswordPolicyOptions
{
    public int RequiredLength { get; set; } = 8;
    public bool RequireDigit { get; set; } = true;
    public bool RequireLowercase { get; set; } = true;
    public bool RequireUppercase { get; set; } = true;
    public bool RequireNonAlphanumeric { get; set; } = false;
}

public sealed class LockoutPolicyOptions
{
    public bool AllowedForNewUsers { get; set; } = true;
    public int MaxFailedAccessAttempts { get; set; } = 5;
    public int DefaultLockoutMinutes { get; set; } = 15;
}

public sealed class FixedWindowRateLimitSettings
{
    public int PermitLimit { get; set; }
    public int WindowSeconds { get; set; }

    public FixedWindowRateLimitSettings()
    {
    }

    public FixedWindowRateLimitSettings(int permitLimit, int windowSeconds)
    {
        PermitLimit = permitLimit;
        WindowSeconds = windowSeconds;
    }
}

public sealed class RateLimitSettings
{
    public WebRateLimitSettings Web { get; set; } = new();
    public ApiRateLimitSettings Api { get; set; } = new();
}

public sealed class WebRateLimitSettings
{
    public FixedWindowRateLimitSettings AuthLogin { get; set; } = new(10, 300);
    public FixedWindowRateLimitSettings AuthRegister { get; set; } = new(5, 900);
    public FixedWindowRateLimitSettings AuthRecovery { get; set; } = new(5, 900);
    public FixedWindowRateLimitSettings AuthTokenConsumption { get; set; } = new(10, 600);
}

public sealed class ApiRateLimitSettings
{
    public FixedWindowRateLimitSettings AuthRegister { get; set; } = new(5, 900);
    public FixedWindowRateLimitSettings AuthRecovery { get; set; } = new(5, 900);
    public FixedWindowRateLimitSettings AuthTokenConsumption { get; set; } = new(10, 600);
    public FixedWindowRateLimitSettings NewsletterSubscribe { get; set; } = new(20, 600);
    public FixedWindowRateLimitSettings NewsletterUnsubscribeToken { get; set; } = new(10, 600);
    public FixedWindowRateLimitSettings NewsletterOneClickToken { get; set; } = new(60, 60);
    public FixedWindowRateLimitSettings OAuthToken { get; set; } = new(30, 60);
}

public sealed class NewsletterTokenOptions
{
    public int ConfirmTokenLifetimeDays { get; set; } = 7;
    public int UnsubscribeTokenLifetimeDays { get; set; } = 7;
    public int OneClickTokenLifetimeDays { get; set; } = 7;
    public int OneClickBatchSizeLimit { get; set; } = 1000;
}

public sealed class FileAccessTokenOptions
{
    public int DefaultLifetimeSeconds { get; set; } = 300;
    public int MinimumLifetimeSeconds { get; set; } = 30;
    public int MaximumLifetimeSeconds { get; set; } = 900;
}

public static class SecurityRuntimeOptionsValidator
{
    public static void Validate(IdentitySecurityOptions options)
    {
        if (options.Password.RequiredLength < 8)
        {
            throw new InvalidOperationException("IdentitySecurity:Password:RequiredLength must be at least 8.");
        }
        if (options.Lockout.MaxFailedAccessAttempts <= 0 || options.Lockout.DefaultLockoutMinutes <= 0)
        {
            throw new InvalidOperationException("IdentitySecurity lockout values must be greater than zero.");
        }
    }

    public static void Validate(RateLimitSettings options)
    {
        var policies = new[]
        {
            options.Web.AuthLogin, options.Web.AuthRegister, options.Web.AuthRecovery, options.Web.AuthTokenConsumption,
            options.Api.AuthRegister, options.Api.AuthRecovery, options.Api.AuthTokenConsumption,
            options.Api.NewsletterSubscribe, options.Api.NewsletterUnsubscribeToken,
            options.Api.NewsletterOneClickToken, options.Api.OAuthToken
        };
        if (policies.Any(policy => policy.PermitLimit <= 0 || policy.WindowSeconds <= 0))
        {
            throw new InvalidOperationException("All RateLimits permit and window values must be greater than zero.");
        }
    }

    public static void Validate(NewsletterTokenOptions options)
    {
        if (options.ConfirmTokenLifetimeDays <= 0 || options.UnsubscribeTokenLifetimeDays <= 0 ||
            options.OneClickTokenLifetimeDays <= 0 || options.OneClickBatchSizeLimit <= 0)
        {
            throw new InvalidOperationException("All NewsletterTokens values must be greater than zero.");
        }
    }

    public static void Validate(FileAccessTokenOptions options)
    {
        if (options.MinimumLifetimeSeconds <= 0 ||
            options.DefaultLifetimeSeconds < options.MinimumLifetimeSeconds ||
            options.MaximumLifetimeSeconds < options.DefaultLifetimeSeconds)
        {
            throw new InvalidOperationException(
                "FileAccessTokens must satisfy 0 < MinimumLifetimeSeconds <= DefaultLifetimeSeconds <= MaximumLifetimeSeconds.");
        }
    }
}
