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

namespace Workflow.Tests.StateMachine;

/// <summary>
/// InstanceStatusMachine TDD 红灯阶段测试
/// 测试工作流实例状态机的所有合法转换与非法转换
///
/// 实例状态: Running, Suspended, Completed, Terminated
/// 操作: Complete, Suspend, Resume, Terminate, Withdraw
/// </summary>
public class InstanceStatusMachineTests
{
    private readonly InstanceStateMachine _machine = new();

    #region Running → Completed

    [Fact]
    public void Transition_Running_To_Completed_When_No_Active_Tokens()
    {
        // Arrange
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Complete;
        var context = new InstanceTransitionContext
        {
            HasActiveTokens = false,
        };

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

        // Assert
        newState.Should().Be(InstanceStatus.Completed);
    }

    [Fact]
    public void Transition_Running_Complete_With_Active_Tokens_Should_Throw()
    {
        // Arrange
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Complete;
        var context = new InstanceTransitionContext
        {
            HasActiveTokens = true,
        };

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

        // Assert
        act.Should().Throw<InvalidStateTransitionException>()
            .WithMessage("*active token*");
    }

    #endregion

    #region Running → Suspended

    [Fact]
    public void Transition_Running_To_Suspended_By_Admin()
    {
        // Arrange
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Suspend;
        var context = new InstanceTransitionContext
        {
            IsAdmin = true,
            IsInitiator = false,
        };

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

        // Assert
        newState.Should().Be(InstanceStatus.Suspended);
    }

    [Fact]
    public void Transition_Running_To_Suspended_By_Initiator()
    {
        // Arrange
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Suspend;
        var context = new InstanceTransitionContext
        {
            IsAdmin = false,
            IsInitiator = true,
        };

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

        // Assert
        newState.Should().Be(InstanceStatus.Suspended);
    }

    [Fact]
    public void Transition_Running_Suspend_By_Unauthorized_User_Should_Throw()
    {
        // Arrange - 既不是 Admin 也不是 Initiator
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Suspend;
        var context = new InstanceTransitionContext
        {
            IsAdmin = false,
            IsInitiator = false,
        };

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

        // Assert
        act.Should().Throw<InvalidStateTransitionException>()
            .WithMessage("*admin*initiator*");
    }

    #endregion

    #region Suspended → Running

    [Fact]
    public void Transition_Suspended_To_Running_By_Admin()
    {
        // Arrange
        var currentState = InstanceStatus.Suspended;
        var operation = InstanceOperation.Resume;
        var context = new InstanceTransitionContext
        {
            IsAdmin = true,
            IsInitiator = false,
        };

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

        // Assert
        newState.Should().Be(InstanceStatus.Running);
    }

    [Fact]
    public void Transition_Suspended_To_Running_By_Initiator()
    {
        // Arrange
        var currentState = InstanceStatus.Suspended;
        var operation = InstanceOperation.Resume;
        var context = new InstanceTransitionContext
        {
            IsAdmin = false,
            IsInitiator = true,
        };

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

        // Assert
        newState.Should().Be(InstanceStatus.Running);
    }

    [Fact]
    public void Transition_Suspended_Resume_By_Unauthorized_User_Should_Throw()
    {
        // Arrange
        var currentState = InstanceStatus.Suspended;
        var operation = InstanceOperation.Resume;
        var context = new InstanceTransitionContext
        {
            IsAdmin = false,
            IsInitiator = false,
        };

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

        // Assert
        act.Should().Throw<InvalidStateTransitionException>()
            .WithMessage("*admin*initiator*");
    }

    #endregion

    #region Running → Terminated

    [Fact]
    public void Transition_Running_To_Terminated_By_Admin()
    {
        // Arrange
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Terminate;
        var context = new InstanceTransitionContext
        {
            IsAdmin = true,
        };

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

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

    [Fact]
    public void Transition_Running_Terminate_By_Non_Admin_Should_Throw()
    {
        // Arrange - Terminate 操作要求 Admin 权限
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Terminate;
        var context = new InstanceTransitionContext
        {
            IsAdmin = false,
        };

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

        // Assert
        act.Should().Throw<InvalidStateTransitionException>()
            .WithMessage("*admin*");
    }

    #endregion

    #region Running → Terminated (Withdraw)

    [Fact]
    public void Transition_Running_Withdraw_By_Initiator_With_No_Processed_Nodes()
    {
        // Arrange - 发起人撤回，且没有任何节点被处理过
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Withdraw;
        var context = new InstanceTransitionContext
        {
            IsInitiator = true,
            HasProcessedNodes = false,
        };

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

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

    [Fact]
    public void Transition_Running_Withdraw_By_Non_Initiator_Should_Throw()
    {
        // Arrange - 非发起人尝试撤回
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Withdraw;
        var context = new InstanceTransitionContext
        {
            IsInitiator = false,
            HasProcessedNodes = false,
        };

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

        // Assert
        act.Should().Throw<InvalidStateTransitionException>()
            .WithMessage("*initiator*");
    }

    [Fact]
    public void Transition_Running_Withdraw_With_Processed_Nodes_Should_Throw()
    {
        // Arrange - 发起人撤回，但已有节点被处理过
        var currentState = InstanceStatus.Running;
        var operation = InstanceOperation.Withdraw;
        var context = new InstanceTransitionContext
        {
            IsInitiator = true,
            HasProcessedNodes = true,
        };

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

        // Assert
        act.Should().Throw<InvalidStateTransitionException>()
            .WithMessage("*processed*");
    }

    #endregion

    #region Illegal Transitions

    [Fact]
    public void Transition_Completed_To_Running_Should_Throw()
    {
        // Arrange - 已完成的实例不能回到运行中
        var currentState = InstanceStatus.Completed;
        var operation = InstanceOperation.Resume;
        var context = new InstanceTransitionContext
        {
            IsAdmin = true,
        };

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

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

    [Fact]
    public void Transition_Terminated_To_Suspended_Should_Throw()
    {
        // Arrange - 已终止的实例不能挂起
        var currentState = InstanceStatus.Terminated;
        var operation = InstanceOperation.Suspend;
        var context = new InstanceTransitionContext
        {
            IsAdmin = true,
        };

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

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

    [Fact]
    public void Transition_Terminated_To_Running_Should_Throw()
    {
        // Arrange - 已终止的实例不能恢复运行
        var currentState = InstanceStatus.Terminated;
        var operation = InstanceOperation.Resume;
        var context = new InstanceTransitionContext
        {
            IsAdmin = true,
        };

        // 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 = InstanceStatus.Completed;
        var operation = InstanceOperation.Terminate;
        var context = new InstanceTransitionContext
        {
            IsAdmin = true,
        };

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

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

    [Fact]
    public void Transition_Suspended_To_Completed_Should_Throw()
    {
        // Arrange - 挂起状态的实例不能直接完成
        var currentState = InstanceStatus.Suspended;
        var operation = InstanceOperation.Complete;
        var context = new InstanceTransitionContext
        {
            HasActiveTokens = false,
        };

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

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

    [Fact]
    public void Transition_Suspended_To_Terminated_Should_Throw()
    {
        // Arrange - 挂起状态的实例不能直接终止，需先恢复
        var currentState = InstanceStatus.Suspended;
        var operation = InstanceOperation.Terminate;
        var context = new InstanceTransitionContext
        {
            IsAdmin = true,
        };

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

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

    [Fact]
    public void Transition_Suspended_Withdraw_Should_Throw()
    {
        // Arrange - 挂起状态不允许撤回操作
        var currentState = InstanceStatus.Suspended;
        var operation = InstanceOperation.Withdraw;
        var context = new InstanceTransitionContext
        {
            IsInitiator = true,
            HasProcessedNodes = false,
        };

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

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

    #endregion
}
