using FluentAssertions;
using Workflow.Domain.Enums;
using Workflow.Domain.Exceptions;
using Workflow.Domain.StateMachine;
using Xunit;

namespace Workflow.Tests.StateMachine;

/// <summary>
/// TokenStateMachine TDD 红灯阶段测试
/// 测试令牌状态机的所有合法转换与非法转换
///
/// 令牌状态: Active, Completed, Terminated
/// 操作: NodeComplete, InstanceTerminate
/// </summary>
public class TokenStateMachineTests
{
    private readonly TokenStateMachine _machine = new();

    #region Active → Completed

    [Fact]
    public void Transition_Active_To_Completed_On_NodeComplete()
    {
        // Arrange - 节点执行完毕，令牌正常完成
        var currentState = TokenStatus.Active;
        var operation = TokenOperation.NodeComplete;
        var context = new TokenTransitionContext();

        // Act
        var newState = _machine.Transition(currentState, operation, context);

        // Assert
        newState.Should().Be(TokenStatus.Consumed);
    }

    #endregion

    #region Active → Terminated

    [Fact]
    public void Transition_Active_To_Terminated_On_InstanceTerminate()
    {
        // Arrange - 所属实例被终止，令牌随之终止
        var currentState = TokenStatus.Active;
        var operation = TokenOperation.InstanceTerminate;
        var context = new TokenTransitionContext();

        // Act
        var newState = _machine.Transition(currentState, operation, context);

        // Assert
        newState.Should().Be(TokenStatus.Terminated);
    }

    #endregion

    #region Illegal Transitions - Completed Is Terminal

    [Fact]
    public void Transition_Completed_To_Active_Should_Throw()
    {
        // Arrange - 已完成的令牌不能重新激活
        var currentState = TokenStatus.Consumed;
        var operation = TokenOperation.NodeComplete;
        var context = new TokenTransitionContext();

        // Act
        var act = () => _machine.Transition(currentState, operation, context);

        // Assert
        act.Should().Throw<InvalidStateTransitionException>();
    }

    [Fact]
    public void Transition_Completed_To_Terminated_Should_Throw()
    {
        // Arrange - 已完成的令牌不能再终止
        var currentState = TokenStatus.Consumed;
        var operation = TokenOperation.InstanceTerminate;
        var context = new TokenTransitionContext();

        // Act
        var act = () => _machine.Transition(currentState, operation, context);

        // Assert
        act.Should().Throw<InvalidStateTransitionException>();
    }

    #endregion

    #region Illegal Transitions - Terminated Is Terminal

    [Fact]
    public void Transition_Terminated_To_Active_Should_Throw()
    {
        // Arrange - 已终止的令牌不能重新激活
        var currentState = TokenStatus.Terminated;
        var operation = TokenOperation.NodeComplete;
        var context = new TokenTransitionContext();

        // Act
        var act = () => _machine.Transition(currentState, operation, context);

        // Assert
        act.Should().Throw<InvalidStateTransitionException>();
    }

    [Fact]
    public void Transition_Terminated_To_Completed_Should_Throw()
    {
        // Arrange - 已终止的令牌不能变为完成
        var currentState = TokenStatus.Terminated;
        var operation = TokenOperation.NodeComplete;
        var context = new TokenTransitionContext();

        // Act
        var act = () => _machine.Transition(currentState, operation, context);

        // Assert
        act.Should().Throw<InvalidStateTransitionException>();
    }

    #endregion

    #region Illegal Transitions - Active State Restrictions

    [Fact]
    public void Transition_Active_NodeComplete_Twice_Should_Throw_On_Second_Call()
    {
        // Arrange - 模拟令牌已完成后再尝试完成
        // 第一次转换成功
        var firstState = _machine.Transition(TokenStatus.Active, TokenOperation.NodeComplete, new TokenTransitionContext());
        firstState.Should().Be(TokenStatus.Consumed);

        // Act - 第二次从 Completed 状态再尝试完成
        var act = () => _machine.Transition(firstState, TokenOperation.NodeComplete, new TokenTransitionContext());

        // Assert
        act.Should().Throw<InvalidStateTransitionException>();
    }

    [Fact]
    public void Transition_Active_InstanceTerminate_Twice_Should_Throw_On_Second_Call()
    {
        // Arrange - 模拟令牌已终止后再尝试终止
        var firstState = _machine.Transition(TokenStatus.Active, TokenOperation.InstanceTerminate, new TokenTransitionContext());
        firstState.Should().Be(TokenStatus.Terminated);

        // Act - 第二次从 Terminated 状态再尝试终止
        var act = () => _machine.Transition(firstState, TokenOperation.InstanceTerminate, new TokenTransitionContext());

        // Assert
        act.Should().Throw<InvalidStateTransitionException>();
    }

    #endregion
}
