using System;

namespace StellaOps.Authority.Plugin.Standard;

internal sealed class StandardPluginOptions
{
    public BootstrapUserOptions? BootstrapUser { get; set; }

    public PasswordPolicyOptions PasswordPolicy { get; set; } = new();

    public LockoutOptions Lockout { get; set; } = new();

    public TokenSigningOptions TokenSigning { get; set; } = new();

    public void Validate(string pluginName)
    {
        BootstrapUser?.Validate(pluginName);
        PasswordPolicy.Validate(pluginName);
        Lockout.Validate(pluginName);
    }
}

internal sealed class BootstrapUserOptions
{
    public string? Username { get; set; }

    public string? Password { get; set; }

    public bool RequirePasswordReset { get; set; } = true;

    public bool IsConfigured => !string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password);

    public void Validate(string pluginName)
    {
        var hasUsername = !string.IsNullOrWhiteSpace(Username);
        var hasPassword = !string.IsNullOrWhiteSpace(Password);

        if (hasUsername ^ hasPassword)
        {
            throw new InvalidOperationException($"Standard plugin '{pluginName}' requires both bootstrapUser.username and bootstrapUser.password when configuring a bootstrap user.");
        }
    }
}

internal sealed class PasswordPolicyOptions
{
    public int MinimumLength { get; set; } = 12;

    public bool RequireUppercase { get; set; } = true;

    public bool RequireLowercase { get; set; } = true;

    public bool RequireDigit { get; set; } = true;

    public bool RequireSymbol { get; set; } = true;

    public void Validate(string pluginName)
    {
        if (MinimumLength <= 0)
        {
            throw new InvalidOperationException($"Standard plugin '{pluginName}' requires passwordPolicy.minimumLength to be greater than zero.");
        }
    }
}

internal sealed class LockoutOptions
{
    public bool Enabled { get; set; } = true;

    public int MaxAttempts { get; set; } = 5;

    public int WindowMinutes { get; set; } = 15;

    public TimeSpan Window => TimeSpan.FromMinutes(WindowMinutes <= 0 ? 15 : WindowMinutes);

    public void Validate(string pluginName)
    {
        if (Enabled && MaxAttempts <= 0)
        {
            throw new InvalidOperationException($"Standard plugin '{pluginName}' requires lockout.maxAttempts to be greater than zero when lockout is enabled.");
        }

        if (Enabled && WindowMinutes <= 0)
        {
            throw new InvalidOperationException($"Standard plugin '{pluginName}' requires lockout.windowMinutes to be greater than zero when lockout is enabled.");
        }
    }
}

internal sealed class TokenSigningOptions
{
    public string? KeyDirectory { get; set; }
}
