using MiningService.Domain.AggregatesModel.MinerAggregate;
using MiningService.Domain.Exceptions;
using Xunit;

namespace MiningService.UnitTests.Domain;

/// <summary>
/// EN: Unit tests for Miner aggregate.
/// VI: Unit tests cho Miner aggregate.
/// </summary>
public class MinerAggregateTests
{
    [Fact]
    public void Create_WithValidUserId_ShouldCreateMiner()
    {
        // Arrange
        var userId = Guid.NewGuid();

        // Act
        var miner = Miner.Create(userId);

        // Assert
        Assert.NotEqual(Guid.Empty, miner.Id);
        Assert.Equal(userId, miner.UserId);
        Assert.Equal(MinerRole.Pioneer, miner.Role);
        Assert.Equal(0, miner.TotalMinedPoints);
        Assert.Equal(MinerStatus.Active, miner.Status);
        Assert.NotEmpty(miner.ReferralCode);
        Assert.Single(miner.DomainEvents); // MinerCreatedDomainEvent
    }

    [Fact]
    public void StartMiningSession_WhenActive_ShouldCreateSession()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());
        miner.ClearDomainEvents();

        // Act
        var session = miner.StartMiningSession();

        // Assert
        Assert.NotNull(session);
        Assert.Equal(MiningSessionStatus.Active, session.Status);
        Assert.True(session.HourlyRate > 0);
        Assert.Single(miner.DomainEvents); // MiningSessionStartedDomainEvent
    }

    [Fact]
    public void StartMiningSession_WhenSuspended_ShouldThrow()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());
        miner.Suspend();

        // Act & Assert
        Assert.Throws<MiningDomainException>(() => miner.StartMiningSession());
    }

    [Fact]
    public void StartMiningSession_WhenAlreadyActive_ShouldThrow()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());
        miner.StartMiningSession();

        // Act & Assert
        Assert.Throws<MiningDomainException>(() => miner.StartMiningSession());
    }

    [Fact]
    public void Suspend_WhenActive_ShouldChangeStatus()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());

        // Act
        miner.Suspend();

        // Assert
        Assert.Equal(MinerStatus.Suspended, miner.Status);
    }

    [Fact]
    public void Restore_WhenSuspended_ShouldActivate()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());
        miner.Suspend();

        // Act
        miner.Restore();

        // Assert
        Assert.Equal(MinerStatus.Active, miner.Status);
    }

    [Fact]
    public void AddBonusPoints_WithPositiveAmount_ShouldAddPoints()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());
        var initialPoints = miner.TotalMinedPoints;

        // Act
        miner.AddBonusPoints(100, "Test Bonus");

        // Assert
        Assert.Equal(initialPoints + 100, miner.TotalMinedPoints);
        Assert.Single(miner.MiningHistories);
    }

    [Fact]
    public void AddBonusPoints_WithNegativeAmount_ShouldThrow()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());

        // Act & Assert
        Assert.Throws<MiningDomainException>(() => miner.AddBonusPoints(-10, "Invalid"));
    }

    [Fact]
    public void JoinCircle_WhenNotInCircle_ShouldSetCircleId()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());
        var circleId = Guid.NewGuid();

        // Act
        miner.JoinCircle(circleId);

        // Assert
        Assert.Equal(circleId, miner.CircleId);
        Assert.Equal(MinerRole.Contributor, miner.Role); // Upgraded from Pioneer
    }

    [Fact]
    public void JoinCircle_WhenAlreadyInCircle_ShouldThrow()
    {
        // Arrange
        var miner = Miner.Create(Guid.NewGuid());
        miner.JoinCircle(Guid.NewGuid());

        // Act & Assert
        Assert.Throws<MiningDomainException>(() => miner.JoinCircle(Guid.NewGuid()));
    }
}
