using System.Text;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using NUnit.Framework;

namespace e_suite.Nuget.PasswordHasher.UnitTests;

[TestFixture]
public class CustomPasswordHasherUnitTests
{
    public class User
    {
        public string Password = string.Empty;
    }

    private CustomPasswordHasher<User> _passwordHasher = null!;
    private CustomPasswordHasher<User> _passwordHasherFormulaTwo = null!;

    public class Options<TOptions> : IOptions<TOptions> where TOptions : class, new()
    {
        public TOptions Value { get; } = new TOptions();
    }

    [SetUp]
    public void Setup()
    {
        _passwordHasher = new CustomPasswordHasher<User>();

        var options = new Options<CustomPasswordHasherOptions>
        {
            Value =
            {
                CompatibilityMode = CustomPasswordHasherMethod.formulaTwo
            }
        };

        _passwordHasherFormulaTwo = new CustomPasswordHasher<User>(options);
    }

    [Test]
    public void HashPassword_WhenHashingPassword_EnsuresNewSaltedHash()
    {
        //Arrange
        var user = new User();
        var password = "12345";

        //Act
        var result = _passwordHasher.HashPassword(user, password);
        var result2 = _passwordHasher.HashPassword(user, password);

        //Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(result, Is.Not.EqualTo(string.Empty));
        Assert.That(result, Is.Not.EqualTo(password));
        Assert.That(result, Is.Not.EqualTo(result2));
    }


    [Test]
    public void VerifyingPassword_PasswordEmpty_ReturnsFailed()
    {
        //Arrange
        var user = new User
        {
            Password = ""
        };
        var password = "12345";

        //Act
        var result = _passwordHasher.VerifyHashedPassword(user, user.Password,  password);

        //Assert
        Assert.That(result, Is.EqualTo( PasswordVerificationResult.Failed));
    }

    [Test]
    public void VerifyingPassword_PasswordCorrect_ReturnsSuccess()
    {
        //Arrange
        var user = new User
        {
            Password = "AgAAAAIAACcQAAAAAAAAABBgrjKk7U86gEbFKXkU83K95YntV93+eyArPbnMVI6vLyrLL9IhaqZmoo64aAU0zKqhecnuOG2eO4XtHP2kz3RabjvbUSfHBMiP6O1F/DIq1Q=="
        };
        var password = "12345";

        //Act
        var result = _passwordHasher.VerifyHashedPassword(user, user.Password, password);

        //Assert
        Assert.That(result, Is.EqualTo(PasswordVerificationResult.Success));
    }

    [Test]
    public void VerifyingPassword_PasswordInCorrect_ReturnsFailed()
    {
        //Arrange
        var user = new User
        {
            Password = "AgAAAAIAACcQAAAAAAAAABBgrjKk7U86gEbFKXkU83K95YntV93+eyArPbnMVI6vLyrLL9IhaqZmoo64aAU0zKqhecnuOG2eO4XtHP2kz3RabjvbUSfHBMiP6O1F/DIq1Q=="
        };
        var password = "ThisWillNeverWork";

        //Act
        var result = _passwordHasher.VerifyHashedPassword(user, user.Password, password);

        //Assert
        Assert.That(result, Is.EqualTo(PasswordVerificationResult.Failed));
    }

    [Test]
    public void VerifyingPassword_CorrectPasswordUsingOldAlgorithm_ReturnsSuccessRehashNeeded()
    {
        //Arrange
        var user = new User
        {
            Password = ""
        };
        var password = "12345";

        user.Password = _passwordHasherFormulaTwo.HashPassword(user, password);

        //Act
        var result = _passwordHasher.VerifyHashedPassword(user, user.Password, password);

        //Assert
        Assert.That(result, Is.EqualTo(PasswordVerificationResult.SuccessRehashNeeded));
    }

    [Test]
    public void VerifyingPassword_WhenPepperPresent_ReturnsSuccess()
    {
        //Arrange
        var options = new Options<CustomPasswordHasherOptions>();
        options.Value.PepperDictionary.Add(1, Encoding.ASCII.GetBytes("MyPepper"));

        var localPasswordHasher = new CustomPasswordHasher<User>(options);

        var user = new User
        {
            Password = ""
        };
        var password = "12345";


        user.Password = localPasswordHasher.HashPassword(user, password);

        //Act
        var result = localPasswordHasher.VerifyHashedPassword(user, user.Password, password);

        //Assert
        Assert.That(result, Is.EqualTo(PasswordVerificationResult.Success));
    }

    [Test]
    public void VerifyingPassword_WhenPepperNewPepperAdded_ReturnsSuccess()
    {
        //Arrange
        var options = new Options<CustomPasswordHasherOptions>();
        options.Value.PepperDictionary.Add(1, Encoding.ASCII.GetBytes("MyPepper"));

        var localPasswordHasher = new CustomPasswordHasher<User>(options);

        var user = new User
        {
            Password = ""
        };
        var password = "12345";

        user.Password = localPasswordHasher.HashPassword(user, password);

        options.Value.PepperDictionary.Add(2, Encoding.ASCII.GetBytes("NewPepper"));

        var localPasswordHasher2 = new CustomPasswordHasher<User>(options);

        //Act
        var result = localPasswordHasher2.VerifyHashedPassword(user, user.Password, password);

        //Assert
        Assert.That(result, Is.EqualTo(PasswordVerificationResult.Success));
    }

    [Test]
    public void VerifyingPassword_WhenPepperNewPepperAddedFirst_ReturnsFailed()
    {
        //Arrange
        var options = new Options<CustomPasswordHasherOptions>();
        options.Value.PepperDictionary.Add(1, Encoding.ASCII.GetBytes("MyPepper"));

        var localPasswordHasher = new CustomPasswordHasher<User>(options);

        var user = new User
        {
            Password = ""
        };
        var password = "12345";

        user.Password = localPasswordHasher.HashPassword(user, password);

        options.Value.PepperDictionary[1] = Encoding.ASCII.GetBytes("NewPepper");
        options.Value.PepperDictionary.Add(2, Encoding.ASCII.GetBytes("MyPepper"));

        var localPasswordHasher2 = new CustomPasswordHasher<User>(options);

        //Act
        var result = localPasswordHasher2.VerifyHashedPassword(user, user.Password, password);

        //Assert
        Assert.That(result, Is.EqualTo(PasswordVerificationResult.Failed));
    }

    [Test]
    public void VerifyingPassword_WhenPepperDoesNotMatch_ReturnsFailed()
    {
        //Arrange
        var options = new Options<CustomPasswordHasherOptions>();
        options.Value.PepperDictionary.Add(1, Encoding.ASCII.GetBytes("MyPepper"));

        var localPasswordHasher = new CustomPasswordHasher<User>(options);

        var user = new User
        {
            Password = ""
        };
        var password = "12345";

        user.Password = localPasswordHasher.HashPassword(user, password);

        options.Value.PepperDictionary[1] = Encoding.ASCII.GetBytes("NewPepper");

        var localPasswordHasher2 = new CustomPasswordHasher<User>(options);

        //Act
        var result = localPasswordHasher2.VerifyHashedPassword(user, user.Password, password);

        //Assert
        Assert.That(result, Is.EqualTo(PasswordVerificationResult.Failed));
    }
}