using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
using ColaFlow.Modules.Identity.Infrastructure.Persistence;
using ColaFlow.Modules.Identity.Infrastructure.Persistence.Repositories;
using ColaFlow.Modules.Identity.Infrastructure.Services;
using FluentAssertions;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Moq;

namespace ColaFlow.Modules.Identity.Infrastructure.Tests.Repositories;

public class TenantRepositoryTests : IDisposable
{
    private readonly IdentityDbContext _context;
    private readonly TenantRepository _repository;

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

        var mockTenantContext = new Mock<ITenantContext>();
        mockTenantContext.Setup(x => x.IsSet).Returns(false);

        var mockMediator = new Mock<IMediator>();

        var mockEnvironment = new Mock<IHostEnvironment>();
        mockEnvironment.Setup(e => e.EnvironmentName).Returns("Development");

        var mockLogger = new Mock<ILogger<IdentityDbContext>>();

        _context = new IdentityDbContext(options, mockTenantContext.Object, mockMediator.Object, mockEnvironment.Object, mockLogger.Object);
        _repository = new TenantRepository(_context);
    }

    [Fact]
    public async Task AddAsync_ShouldPersistTenant()
    {
        // Arrange
        var tenant = Tenant.Create(
            TenantName.Create("Test Company"),
            TenantSlug.Create("test-company"),
            SubscriptionPlan.Professional);

        // Act
        await _repository.AddAsync(tenant);

        // Assert
        var retrieved = await _repository.GetByIdAsync(TenantId.Create(tenant.Id));
        retrieved.Should().NotBeNull();
        retrieved!.Name.Value.Should().Be("Test Company");
        retrieved.Slug.Value.Should().Be("test-company");
        retrieved.Plan.Should().Be(SubscriptionPlan.Professional);
    }

    [Fact]
    public async Task GetBySlugAsync_ShouldReturnTenant()
    {
        // Arrange
        var slug = TenantSlug.Create("acme-corp");
        var tenant = Tenant.Create(
            TenantName.Create("Acme Corp"),
            slug,
            SubscriptionPlan.Enterprise);
        await _repository.AddAsync(tenant);

        // Act
        var retrieved = await _repository.GetBySlugAsync(slug);

        // Assert
        retrieved.Should().NotBeNull();
        retrieved!.Slug.Value.Should().Be("acme-corp");
        retrieved.Name.Value.Should().Be("Acme Corp");
    }

    [Fact]
    public async Task ExistsBySlugAsync_ShouldReturnTrue_WhenSlugExists()
    {
        // Arrange
        var slug = TenantSlug.Create("unique-slug");
        var tenant = Tenant.Create(
            TenantName.Create("Unique Company"),
            slug,
            SubscriptionPlan.Free);
        await _repository.AddAsync(tenant);

        // Act
        var exists = await _repository.ExistsBySlugAsync(slug);

        // Assert
        exists.Should().BeTrue();
    }

    [Fact]
    public async Task ExistsBySlugAsync_ShouldReturnFalse_WhenSlugDoesNotExist()
    {
        // Arrange
        var slug = TenantSlug.Create("non-existent");

        // Act
        var exists = await _repository.ExistsBySlugAsync(slug);

        // Assert
        exists.Should().BeFalse();
    }

    [Fact]
    public async Task UpdateAsync_ShouldModifyTenant()
    {
        // Arrange
        var tenant = Tenant.Create(
            TenantName.Create("Original Name"),
            TenantSlug.Create("original-slug"),
            SubscriptionPlan.Free);
        await _repository.AddAsync(tenant);

        // Act
        tenant.UpdateName(TenantName.Create("Updated Name"));
        await _repository.UpdateAsync(tenant);

        // Assert
        var retrieved = await _repository.GetByIdAsync(TenantId.Create(tenant.Id));
        retrieved.Should().NotBeNull();
        retrieved!.Name.Value.Should().Be("Updated Name");
    }

    [Fact]
    public async Task GetAllAsync_ShouldReturnAllTenants()
    {
        // Arrange
        var tenant1 = Tenant.Create(TenantName.Create("Tenant 1"), TenantSlug.Create("tenant-1"), SubscriptionPlan.Free);
        var tenant2 = Tenant.Create(TenantName.Create("Tenant 2"), TenantSlug.Create("tenant-2"), SubscriptionPlan.Starter);
        await _repository.AddAsync(tenant1);
        await _repository.AddAsync(tenant2);

        // Act
        var allTenants = await _repository.GetAllAsync();

        // Assert
        allTenants.Should().HaveCount(2);
        allTenants.Should().Contain(t => t.Name.Value == "Tenant 1");
        allTenants.Should().Contain(t => t.Name.Value == "Tenant 2");
    }

    [Fact]
    public async Task DeleteAsync_ShouldRemoveTenant()
    {
        // Arrange
        var tenant = Tenant.Create(
            TenantName.Create("To Delete"),
            TenantSlug.Create("to-delete"),
            SubscriptionPlan.Free);
        await _repository.AddAsync(tenant);

        // Act
        await _repository.DeleteAsync(tenant);

        // Assert
        var retrieved = await _repository.GetByIdAsync(TenantId.Create(tenant.Id));
        retrieved.Should().BeNull();
    }

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