using Eshop.Application.Auth;
using Eshop.Infrastructure;
using Eshop.Infrastructure.Persistence;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace WebTests;

[TestClass]
public class AuthServiceTests
{
    private ServiceProvider _serviceProvider = null!;
    private IAuthenticationService _authenticationService = null!;
    private IUserAccountService _userAccountService = null!;

    [TestInitialize]
    public void Initialize()
    {
        var services = new ServiceCollection();

        services.AddLogging();
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

        services.AddDbContext<AppDbContext>(options =>
            options.UseInMemoryDatabase(Guid.NewGuid().ToString()));

        services.AddIdentity<UserEntity, IdentityRole>()
            .AddEntityFrameworkStores<AppDbContext>()
            .AddDefaultTokenProviders();

        services.AddInfrastructure();

        _serviceProvider = services.BuildServiceProvider();

        var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
        _serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext = httpContext;

        var roleManager = _serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
        foreach (var role in new[] { "Admin", "User" })
        {
            if (!roleManager.RoleExistsAsync(role).GetAwaiter().GetResult())
                roleManager.CreateAsync(new IdentityRole(role)).GetAwaiter().GetResult();
        }

        _authenticationService = _serviceProvider.GetRequiredService<IAuthenticationService>();
        _userAccountService = _serviceProvider.GetRequiredService<IUserAccountService>();
    }

    [TestCleanup]
    public void Cleanup()
    {
        _serviceProvider.Dispose();
    }

    [TestMethod]
    public async Task RegisterAsync_ShouldReturnSuccess_WhenUserIsNew()
    {
        var result = await _userAccountService.RegisterAsync("testuser", "test@example.com", "Password123!");

        Assert.IsTrue(result.Success);

        var profile = await _userAccountService.GetProfileAsync(
            (await _serviceProvider.GetRequiredService<UserManager<UserEntity>>()
                .FindByNameAsync("testuser"))!.Id);

        Assert.IsNotNull(profile);
        Assert.AreEqual("testuser", profile.UserName);
        Assert.AreEqual("test@example.com", profile.Email);
    }

    [TestMethod]
    public async Task RegisterAsync_ShouldReturnFailure_WhenUsernameAlreadyExists()
    {
        await _userAccountService.RegisterAsync("existing", "existing@example.com", "Password123!");

        var result = await _userAccountService.RegisterAsync("existing", "other@example.com", "Password123!");

        Assert.IsFalse(result.Success);
        Assert.IsTrue(result.Errors.Any(e => e.Contains("already taken", StringComparison.OrdinalIgnoreCase)));
    }

    [TestMethod]
    public async Task SignInAsync_ShouldReturnSuccess_WithValidCredentials()
    {
        await _userAccountService.RegisterAsync("testuser", "test@example.com", "Password123!");

        var result = await _authenticationService.SignInAsync("testuser", "Password123!", rememberMe: false);

        Assert.IsTrue(result.Succeeded);
    }

    [TestMethod]
    public async Task SignInAsync_ShouldReturnFailure_WithInvalidCredentials()
    {
        await _userAccountService.RegisterAsync("testuser", "test@example.com", "Password123!");

        var result = await _authenticationService.SignInAsync("testuser", "WrongPassword!", rememberMe: false);

        Assert.IsFalse(result.Succeeded);
    }

    [TestMethod]
    public async Task SignInAsync_ShouldReturnFailure_WhenUserNotFound()
    {
        var result = await _authenticationService.SignInAsync("nonexistent", "Password123!", rememberMe: false);

        Assert.IsFalse(result.Succeeded);
    }
}
