using FluentAssertions;
using MiningService.API.Application.Commands;
using MiningService.Domain.AggregatesModel.MinerAggregate;
using MiningService.Domain.Exceptions;
using MiningService.Domain.SeedWork;
using NSubstitute;
using Xunit;

namespace MiningService.UnitTests.Application.Commands;

/// <summary>
/// EN: Unit tests for StartMiningCommandHandler.
/// VI: Unit tests cho StartMiningCommandHandler.
/// </summary>
public class StartMiningCommandHandlerTests
{
    private readonly IMinerRepository _minerRepository;
    private readonly IUnitOfWork _unitOfWork;
    private readonly StartMiningCommandHandler _handler;

    public StartMiningCommandHandlerTests()
    {
        _unitOfWork = Substitute.For<IUnitOfWork>();
        _unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);
        
        _minerRepository = Substitute.For<IMinerRepository>();
        _minerRepository.UnitOfWork.Returns(_unitOfWork);
        _handler = new StartMiningCommandHandler(_minerRepository);
    }

    [Fact]
    public async Task Handle_ExistingMiner_StartsSession()
    {
        // Arrange
        var userId = Guid.NewGuid();
        var miner = Miner.Create(userId);
        var command = new StartMiningCommand(userId);

        _minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
            .Returns(miner);

        // Act
        var result = await _handler.Handle(command, CancellationToken.None);

        // Assert
        result.Should().NotBeNull();
        result.SessionId.Should().NotBeEmpty();
        result.HourlyRate.Should().BeGreaterThan(0);
        
        await _unitOfWork.Received(1).SaveEntitiesAsync(Arg.Any<CancellationToken>());
    }

    [Fact(Skip = "Handler requires existing miner - auto-create has been removed")]
    public async Task Handle_NewUser_CreatesMinerAndStartsSession()
    {
        // This test is skipped because StartMiningCommandHandler
        // now requires an existing miner - it throws MinerNotFoundException
        // for new users. Miner creation happens during registration.
        await Task.CompletedTask;
    }

    [Fact]
    public async Task Handle_SuspendedMiner_ThrowsException()
    {
        // Arrange
        var userId = Guid.NewGuid();
        var miner = Miner.Create(userId);
        miner.Suspend();
        var command = new StartMiningCommand(userId);

        _minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
            .Returns(miner);

        // Act & Assert
        await Assert.ThrowsAsync<MiningDomainException>(() =>
            _handler.Handle(command, CancellationToken.None));
    }

    [Fact]
    public async Task Handle_AlreadyMining_ThrowsException()
    {
        // Arrange
        var userId = Guid.NewGuid();
        var miner = Miner.Create(userId);
        miner.StartMiningSession();
        var command = new StartMiningCommand(userId);

        _minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
            .Returns(miner);

        // Act & Assert
        await Assert.ThrowsAsync<MiningDomainException>(() =>
            _handler.Handle(command, CancellationToken.None));
    }
}

