using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants.Events;
using FluentAssertions;
using Xunit;

namespace ColaFlow.Modules.Identity.Domain.Tests.Aggregates;

public sealed class TenantTests
{
    [Fact]
    public void Create_ShouldSucceed()
    {
        // Arrange
        var name = TenantName.Create("Acme Corporation");
        var slug = TenantSlug.Create("acme");

        // Act
        var tenant = Tenant.Create(name, slug);

        // Assert
        tenant.Should().NotBeNull();
        tenant.Id.Should().NotBe(Guid.Empty);
        tenant.Name.Value.Should().Be("Acme Corporation");
        tenant.Slug.Value.Should().Be("acme");
        tenant.Status.Should().Be(TenantStatus.Active);
        tenant.Plan.Should().Be(SubscriptionPlan.Free);
        tenant.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
    }

    [Fact]
    public void Create_ShouldRaiseTenantCreatedEvent()
    {
        // Arrange
        var name = TenantName.Create("Acme Corporation");
        var slug = TenantSlug.Create("acme");

        // Act
        var tenant = Tenant.Create(name, slug);

        // Assert
        tenant.DomainEvents.Should().ContainSingle();
        tenant.DomainEvents.Should().ContainItemsAssignableTo<TenantCreatedEvent>();
        var domainEvent = tenant.DomainEvents.First() as TenantCreatedEvent;
        domainEvent.Should().NotBeNull();
        domainEvent!.TenantId.Should().Be(tenant.Id);
        domainEvent.Slug.Should().Be("acme");
    }

    [Fact]
    public void Activate_ShouldChangeStatus()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"));
        tenant.Suspend("Payment failed");
        tenant.ClearDomainEvents();

        // Act
        tenant.Activate();

        // Assert
        tenant.Status.Should().Be(TenantStatus.Active);
        tenant.SuspendedAt.Should().BeNull();
        tenant.SuspensionReason.Should().BeNull();
        tenant.DomainEvents.Should().ContainSingle();
        tenant.DomainEvents.Should().ContainItemsAssignableTo<TenantActivatedEvent>();
    }

    [Fact]
    public void Suspend_ShouldChangeStatus()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"));
        tenant.ClearDomainEvents();

        // Act
        tenant.Suspend("Payment failed");

        // Assert
        tenant.Status.Should().Be(TenantStatus.Suspended);
        tenant.SuspendedAt.Should().NotBeNull();
        tenant.SuspendedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
        tenant.SuspensionReason.Should().Be("Payment failed");
        tenant.DomainEvents.Should().ContainSingle();
        tenant.DomainEvents.Should().ContainItemsAssignableTo<TenantSuspendedEvent>();
    }

    [Fact]
    public void Cancel_ShouldChangeStatus()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"));
        tenant.ClearDomainEvents();

        // Act
        tenant.Cancel();

        // Assert
        tenant.Status.Should().Be(TenantStatus.Cancelled);
        tenant.DomainEvents.Should().ContainSingle();
        tenant.DomainEvents.Should().ContainItemsAssignableTo<TenantCancelledEvent>();
    }

    [Fact]
    public void CancelledTenant_CannotBeActivated()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"));
        tenant.Cancel();

        // Act & Assert
        var act = () => tenant.Activate();
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("Cannot activate cancelled tenant");
    }

    [Fact]
    public void ConfigureSso_ShouldSucceed_ForProfessionalPlan()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"));
        tenant.UpgradePlan(SubscriptionPlan.Professional);
        tenant.ClearDomainEvents();

        var ssoConfig = SsoConfiguration.CreateOidc(
            SsoProvider.AzureAD,
            "https://login.microsoftonline.com/tenant-id",
            "client-id",
            "client-secret");

        // Act
        tenant.ConfigureSso(ssoConfig);

        // Assert
        tenant.SsoConfig.Should().NotBeNull();
        tenant.SsoConfig!.Provider.Should().Be(SsoProvider.AzureAD);
        tenant.DomainEvents.Should().ContainSingle();
        tenant.DomainEvents.Should().ContainItemsAssignableTo<SsoConfiguredEvent>();
    }

    [Fact]
    public void ConfigureSso_ShouldThrow_ForFreePlan()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"));
        var ssoConfig = SsoConfiguration.CreateOidc(
            SsoProvider.AzureAD,
            "https://login.microsoftonline.com/tenant-id",
            "client-id",
            "client-secret");

        // Act & Assert
        var act = () => tenant.ConfigureSso(ssoConfig);
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("SSO is only available for Professional and Enterprise plans");
    }

    [Fact]
    public void UpgradePlan_ShouldIncreaseResourceLimits()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"));
        tenant.ClearDomainEvents();

        // Act
        tenant.UpgradePlan(SubscriptionPlan.Professional);

        // Assert
        tenant.Plan.Should().Be(SubscriptionPlan.Professional);
        tenant.MaxUsers.Should().Be(100);
        tenant.MaxProjects.Should().Be(100);
        tenant.MaxStorageGB.Should().Be(100);
        tenant.DomainEvents.Should().ContainSingle();
        tenant.DomainEvents.Should().ContainItemsAssignableTo<TenantPlanUpgradedEvent>();
    }

    [Fact]
    public void UpgradePlan_ShouldThrow_WhenDowngrading()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"), SubscriptionPlan.Professional);

        // Act & Assert
        var act = () => tenant.UpgradePlan(SubscriptionPlan.Free);
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("New plan must be higher than current plan");
    }

    [Fact]
    public void DisableSso_ShouldSucceed()
    {
        // Arrange
        var tenant = Tenant.Create(TenantName.Create("Test"), TenantSlug.Create("test"));
        tenant.UpgradePlan(SubscriptionPlan.Enterprise);
        var ssoConfig = SsoConfiguration.CreateOidc(
            SsoProvider.Google,
            "https://accounts.google.com",
            "client-id",
            "client-secret");
        tenant.ConfigureSso(ssoConfig);
        tenant.ClearDomainEvents();

        // Act
        tenant.DisableSso();

        // Assert
        tenant.SsoConfig.Should().BeNull();
        tenant.DomainEvents.Should().ContainSingle();
        tenant.DomainEvents.Should().ContainItemsAssignableTo<SsoDisabledEvent>();
    }
}
