using FluentAssertions;
using SocialService.Domain.AggregatesModel.RelationshipAggregate;
using SocialService.Domain.Events;
using SocialService.Domain.Exceptions;
using Xunit;

namespace SocialService.UnitTests.Domain;

/// <summary>
/// EN: Unit tests for Relationship aggregate root.
/// VI: Unit tests cho Relationship aggregate root.
/// </summary>
public class RelationshipAggregateTests
{
    private readonly Guid _requesterId = Guid.NewGuid();
    private readonly Guid _addresseeId = Guid.NewGuid();

    #region Constructor Tests

    [Fact]
    public void Create_Friendship_SetsStatusToPending()
    {
        // Act
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);

        // Assert
        relationship.Status.Should().Be(RelationshipStatus.Pending);
        relationship.Type.Should().Be(RelationshipType.Friendship);
        relationship.RequesterId.Should().Be(_requesterId);
        relationship.AddresseeId.Should().Be(_addresseeId);
    }

    [Fact]
    public void Create_Friendship_RaisesFriendRequestSentEvent()
    {
        // Act
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);

        // Assert
        relationship.DomainEvents.Should().ContainSingle(e => e is FriendRequestSentDomainEvent);
    }

    [Fact]
    public void Create_Following_AutoAccepts()
    {
        // Act
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Following);

        // Assert
        relationship.Status.Should().Be(RelationshipStatus.Accepted);
        relationship.Type.Should().Be(RelationshipType.Following);
    }

    [Fact]
    public void Create_Following_RaisesUserFollowedEvent()
    {
        // Act
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Following);

        // Assert
        relationship.DomainEvents.Should().ContainSingle(e => e is UserFollowedDomainEvent);
    }

    [Fact]
    public void Create_WithSameUserId_ThrowsException()
    {
        // Act
        var act = () => new Relationship(_requesterId, _requesterId, RelationshipType.Friendship);

        // Assert
        act.Should().Throw<SocialDomainException>()
            .WithMessage("*yourself*");
    }

    [Fact]
    public void Create_WithEmptyRequesterId_ThrowsException()
    {
        // Act
        var act = () => new Relationship(Guid.Empty, _addresseeId, RelationshipType.Friendship);

        // Assert
        act.Should().Throw<SocialDomainException>()
            .WithMessage("*Requester ID*");
    }

    [Fact]
    public void Create_WithEmptyAddresseeId_ThrowsException()
    {
        // Act
        var act = () => new Relationship(_requesterId, Guid.Empty, RelationshipType.Friendship);

        // Assert
        act.Should().Throw<SocialDomainException>()
            .WithMessage("*Addressee ID*");
    }

    #endregion

    #region Accept Tests

    [Fact]
    public void Accept_PendingFriendship_SetsStatusToAccepted()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);
        relationship.ClearDomainEvents();

        // Act
        relationship.Accept();

        // Assert
        relationship.Status.Should().Be(RelationshipStatus.Accepted);
        relationship.UpdatedAt.Should().NotBeNull();
    }

    [Fact]
    public void Accept_PendingFriendship_RaisesFriendshipCreatedEvent()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);
        relationship.ClearDomainEvents();

        // Act
        relationship.Accept();

        // Assert
        relationship.DomainEvents.Should().Contain(e => e is FriendshipCreatedDomainEvent);
        relationship.DomainEvents.Should().Contain(e => e is RelationshipStatusChangedDomainEvent);
    }

    [Fact]
    public void Accept_Following_ThrowsException()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Following);

        // Act
        var act = () => relationship.Accept();

        // Assert
        act.Should().Throw<SocialDomainException>()
            .WithMessage("*Only friendship*");
    }

    [Fact]
    public void Accept_AlreadyAccepted_ThrowsException()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);
        relationship.Accept();

        // Act
        var act = () => relationship.Accept();

        // Assert
        act.Should().Throw<SocialDomainException>()
            .WithMessage("*Only pending*");
    }

    #endregion

    #region Reject Tests

    [Fact]
    public void Reject_PendingFriendship_SetsStatusToRejected()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);
        relationship.ClearDomainEvents();

        // Act
        relationship.Reject();

        // Assert
        relationship.Status.Should().Be(RelationshipStatus.Rejected);
        relationship.UpdatedAt.Should().NotBeNull();
    }

    [Fact]
    public void Reject_Following_ThrowsException()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Following);

        // Act
        var act = () => relationship.Reject();

        // Assert
        act.Should().Throw<SocialDomainException>()
            .WithMessage("*Only friendship*");
    }

    #endregion

    #region Cancel Tests

    [Fact]
    public void Cancel_PendingRequest_SetsStatusToCancelled()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);
        relationship.ClearDomainEvents();

        // Act
        relationship.Cancel();

        // Assert
        relationship.Status.Should().Be(RelationshipStatus.Cancelled);
        relationship.UpdatedAt.Should().NotBeNull();
    }

    [Fact]
    public void Cancel_AcceptedRelationship_ThrowsException()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);
        relationship.Accept();

        // Act
        var act = () => relationship.Cancel();

        // Assert
        act.Should().Throw<SocialDomainException>()
            .WithMessage("*Only pending*");
    }

    #endregion

    #region Remove Tests

    [Fact]
    public void Remove_AcceptedFriendship_SetsStatusToCancelled()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);
        relationship.Accept();
        relationship.ClearDomainEvents();

        // Act
        relationship.Remove();

        // Assert
        relationship.Status.Should().Be(RelationshipStatus.Cancelled);
        relationship.UpdatedAt.Should().NotBeNull();
    }

    [Fact]
    public void Remove_AcceptedFollowing_SetsStatusToCancelled()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Following);
        relationship.ClearDomainEvents();

        // Act
        relationship.Remove();

        // Assert
        relationship.Status.Should().Be(RelationshipStatus.Cancelled);
    }

    [Fact]
    public void Remove_AcceptedRelationship_RaisesRelationshipRemovedEvent()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);
        relationship.Accept();
        relationship.ClearDomainEvents();

        // Act
        relationship.Remove();

        // Assert
        relationship.DomainEvents.Should().Contain(e => e is RelationshipRemovedDomainEvent);
    }

    [Fact]
    public void Remove_PendingRequest_ThrowsException()
    {
        // Arrange
        var relationship = new Relationship(_requesterId, _addresseeId, RelationshipType.Friendship);

        // Act
        var act = () => relationship.Remove();

        // Assert
        act.Should().Throw<SocialDomainException>()
            .WithMessage("*Only accepted*");
    }

    #endregion
}
