using API.Database;
using API.Models.Internal.User;
using API.Repository.RegistrationKeyRepo;
using Microsoft.EntityFrameworkCore;

namespace API.Tests.Services;

public class RegistrationKeyServiceTests : IDisposable
{
    private readonly ApplicationDbContext _context;
    private readonly RegistrationKeyService _service;

    public RegistrationKeyServiceTests()
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;

        _context = new ApplicationDbContext(options);
        _service = new RegistrationKeyService(_context);
    }

    public void Dispose()
    {
        _context.Database.EnsureDeleted();
        _context.Dispose();
    }

    [Fact]
    public async Task GetAllAsync_EmptyDatabase_ReturnsEmptyList()
    {
        // Act
        var result = await _service.GetAllAsync();

        // Assert
        Assert.NotNull(result);
        Assert.Empty(result);
    }

    [Fact]
    public async Task GetAllAsync_WithData_ReturnsAllKeys()
    {
        // Arrange
        var key1 = new RegistrationKey { LinkedRole = "Admin" };
        var key2 = new RegistrationKey { LinkedRole = "User" };
        await _context.RegistrationKeys.AddRangeAsync(key1, key2);
        await _context.SaveChangesAsync();

        // Act
        var result = await _service.GetAllAsync();

        // Assert
        Assert.Equal(2, result.Count);
        Assert.Contains(result, k => k.LinkedRole == "Admin");
        Assert.Contains(result, k => k.LinkedRole == "User");
    }

    [Fact]
    public async Task GetAsync_ExistingId_ReturnsKey()
    {
        // Arrange
        var key = new RegistrationKey { LinkedRole = "Moderator" };
        await _context.RegistrationKeys.AddAsync(key);
        await _context.SaveChangesAsync();

        // Act
        var result = await _service.GetAsync(key.Id);

        // Assert
        Assert.NotNull(result);
        Assert.Equal("Moderator", result.LinkedRole);
    }

    [Fact]
    public async Task GetAsync_NonExistingId_ReturnsNull()
    {
        // Act
        var result = await _service.GetAsync("nonexistent-id");

        // Assert
        Assert.Null(result);
    }

    [Fact]
    public async Task CreateAsync_ValidKey_AddsToDatabase()
    {
        // Arrange
        var keyIngoing = new RegistrationKeyIngoing { LinkedRole = "Admin" };

        // Act
        var result = await _service.CreateAsync(keyIngoing);

        // Assert
        Assert.NotNull(result);
        Assert.Equal("Admin", result.LinkedRole);
        Assert.NotNull(result.Id);

        var dbKey = await _context.RegistrationKeys.FindAsync(result.Id);
        Assert.NotNull(dbKey);
        Assert.Equal("Admin", dbKey.LinkedRole);
    }

    [Fact]
    public async Task CreateAsync_NullRole_AddsToDatabase()
    {
        // Arrange
        var keyIngoing = new RegistrationKeyIngoing { LinkedRole = null };

        // Act
        var result = await _service.CreateAsync(keyIngoing);

        // Assert
        Assert.NotNull(result);
        Assert.Null(result.LinkedRole);

        var dbKey = await _context.RegistrationKeys.FindAsync(result.Id);
        Assert.NotNull(dbKey);
    }

    [Fact]
    public async Task DeleteAsync_ExistingKey_RemovesFromDatabase()
    {
        // Arrange
        var key = new RegistrationKey { LinkedRole = "ToDelete" };
        await _context.RegistrationKeys.AddAsync(key);
        await _context.SaveChangesAsync();

        // Act
        var result = await _service.DeleteAsync(key.Id);

        // Assert
        Assert.NotNull(result);
        Assert.Equal("ToDelete", result.LinkedRole);

        var dbKey = await _context.RegistrationKeys.FindAsync(key.Id);
        Assert.Null(dbKey);
    }

    [Fact]
    public async Task DeleteAsync_NonExistingKey_ReturnsNull()
    {
        // Act
        var result = await _service.DeleteAsync("nonexistent-id");

        // Assert
        Assert.Null(result);
    }

    [Fact]
    public async Task DeleteOldRegistrationKeysAsync_ZeroDays_ReturnsZero()
    {
        // Arrange
        var key = new RegistrationKey { LinkedRole = "Old", Created = DateTime.UtcNow.AddDays(-10) };
        await _context.RegistrationKeys.AddAsync(key);
        await _context.SaveChangesAsync();

        // Act
        var result = await _service.DeleteOldRegistrationKeysAsync(0);

        // Assert
        Assert.Equal(0, result);
        Assert.Single(await _context.RegistrationKeys.ToListAsync());
    }

    [Fact]
    public async Task DeleteOldRegistrationKeysAsync_NegativeDays_ReturnsZero()
    {
        // Arrange
        var key = new RegistrationKey { LinkedRole = "Old", Created = DateTime.UtcNow.AddDays(-10) };
        await _context.RegistrationKeys.AddAsync(key);
        await _context.SaveChangesAsync();

        // Act
        var result = await _service.DeleteOldRegistrationKeysAsync(-5);

        // Assert
        Assert.Equal(0, result);
        Assert.Single(await _context.RegistrationKeys.ToListAsync());
    }

    [Fact]
    public async Task DeleteOldRegistrationKeysAsync_NoOldKeys_ReturnsZero()
    {
        // Arrange
        var key = new RegistrationKey { LinkedRole = "New", Created = DateTime.UtcNow };
        await _context.RegistrationKeys.AddAsync(key);
        await _context.SaveChangesAsync();

        // Act
        var result = await _service.DeleteOldRegistrationKeysAsync(7);

        // Assert
        Assert.Equal(0, result);
        Assert.Single(await _context.RegistrationKeys.ToListAsync());
    }

    [Fact]
    public async Task DeleteOldRegistrationKeysAsync_WithOldKeys_DeletesOldKeysOnly()
    {
        // Arrange
        var oldKey1 = new RegistrationKey { LinkedRole = "Old1", Created = DateTime.UtcNow.AddDays(-10) };
        var oldKey2 = new RegistrationKey { LinkedRole = "Old2", Created = DateTime.UtcNow.AddDays(-15) };
        var newKey = new RegistrationKey { LinkedRole = "New", Created = DateTime.UtcNow.AddDays(-2) };

        await _context.RegistrationKeys.AddRangeAsync(oldKey1, oldKey2, newKey);
        await _context.SaveChangesAsync();

        // Act
        var result = await _service.DeleteOldRegistrationKeysAsync(7);

        // Assert
        Assert.Equal(2, result);

        var remainingKeys = await _context.RegistrationKeys.ToListAsync();
        Assert.Single(remainingKeys);
        Assert.Equal("New", remainingKeys[0].LinkedRole);
    }

    [Fact]
    public async Task DeleteOldRegistrationKeysAsync_ExactCutoffDate_KeyNotDeleted()
    {
        // Arrange - key created exactly at cutoff should NOT be deleted
        var keyAtCutoff = new RegistrationKey { LinkedRole = "AtCutoff", Created = DateTime.UtcNow.AddDays(-7) };
        await _context.RegistrationKeys.AddAsync(keyAtCutoff);
        await _context.SaveChangesAsync();

        // Act
        var result = await _service.DeleteOldRegistrationKeysAsync(7);

        // Assert - cutoff is < not <=, so exactly 7 days old should not be deleted
        Assert.Equal(0, result);
        Assert.Single(await _context.RegistrationKeys.ToListAsync());
    }

    [Fact]
    public async Task DeleteOldRegistrationKeysAsync_EmptyDatabase_ReturnsZero()
    {
        // Act
        var result = await _service.DeleteOldRegistrationKeysAsync(7);

        // Assert
        Assert.Equal(0, result);
    }
}
