unit TestFutures; // Or TestFutures as per your previous version

interface

uses
    DUnitX.TestFramework,
    System.SysUtils,
    System.SyncObjs, // For TSemaphore, TEvent etc.
    System.Classes, // For TThread, TThread.Sleep
    Myc.Signals,
    Myc.Core.Signals, // For State factory methods like State.CreateLatch, State.Null
    Myc.TaskManager, // For the global TaskManager variable
    Myc.Core.Tasks, // For TMycTaskFactory
    Myc.Futures; // For Future<T>

type

    [TestFixture]
    TTestFuture = class(TObject)
    public
        [Setup]
        procedure Setup;
        [TearDown]
        procedure TearDown;

        [Test]
        procedure TestConstructSimple;
        [Test]
        procedure TestConstructWithNilGate;
        [Test]
        procedure TestConstructWithPresetGate;
        [Test]
        procedure TestConstructWithDelayedGate;
        [Test]
        [IgnoreMemoryLeaks]
        procedure TestChainSimple;
        [Test]
        procedure TestChainWithGate;
        [Test]
        procedure TestMultipleChains;
        [Test]
        [TestCase('StringFutureTest', 'Test String')]
        [TestCase('IntegerFutureTestForParam', '12345')]
        procedure TestConstructSimple_Parametric(const ParamValue: string);

        [Test]
        procedure TestStress_StateAll;
        [Test]
        procedure TestStress_StateAny;

        [Test]
        procedure TestNestedFuture_Construct; // New test for Future<Future<T>> via Construct
        [Test]
        procedure TestNestedFuture_Chain; // New test for Future<Future<T>> via Chain
    end;

implementation

{ TTestFuture }

procedure TTestFuture.Setup;
begin
    //    SetupTaskManagerMock;
end;

procedure TTestFuture.TearDown;
// Executed after each test
begin
end;

[Test]
procedure TTestFuture.TestConstructSimple;
var
    fut: TFuture<Integer>;
    resultValue: Integer;
begin
    // Test construction with a simple function that returns an Integer
    fut :=
        TFuture<Integer>.Construct( // [cite: 146]
            function: Integer
            begin
                TThread.Sleep(20); // Simulate some background work
                Result := 42;
            end
        );

    Assert.IsNotNull(fut.Done, 'Future.Done property should not be nil after construction.'); // Static string [cite: 148]
    fut.WaitFor(); // Wait for the future to complete its execution [cite: 148]

    Assert.IsTrue(fut.Done.IsSet, 'Future.Done.IsSet should be true after completion.'); // Static string [cite: 148]
    resultValue := fut.Value; // Retrieve the result of the future [cite: 148]
    Assert.AreEqual(42, resultValue, 'The result of the future is not the expected value.'); // Static string
end;

[Test]
procedure TTestFuture.TestConstructWithNilGate;
var
    fut: TFuture<Integer>;
    resultValue: Integer;
begin
    // Test construction with a nil gate, which should execute the task immediately
    fut :=
        TFuture<Integer>.Construct(
            nil, // Explicitly providing a nil gate [cite: 147]
            function: Integer
            begin
                TThread.Sleep(20); // Simulate work
                Result := 43;
            end
        );

    Assert.IsNotNull(fut.Done, 'Future.Done should not be nil when constructed with a nil gate.'); // Static string [cite: 148]
    fut.WaitFor(); // [cite: 148]

    Assert.IsTrue(fut.Done.IsSet, 'Future.Done.IsSet should be true for nil gate construct.'); // Static string [cite: 148]
    resultValue := fut.Value; // [cite: 148]
    Assert.AreEqual(43, resultValue, 'Future result is incorrect for nil gate construct.'); // Static string
end;

[Test]
procedure TTestFuture.TestConstructWithPresetGate;
var
    fut: TFuture<Integer>;
    presetGate: TState.IState;
    resultValue: Integer;
begin
    presetGate := TState.Null; // State.Null is an TState.IState that is always set [cite: 68]
    Assert.IsTrue(presetGate.IsSet, 'The preset gate (State.Null) should be initially set.'); // Static string [cite: 55]

    // Construct a future with a gate that is already set
    fut :=
        TFuture<Integer>.Construct(
            presetGate, // [cite: 147]
            function: Integer
            begin
                Result := 44; // This should execute quickly
            end
        );

    Assert.IsNotNull(fut.Done, 'Future.Done should not be nil for preset gate construct.'); // Static string [cite: 148]
    fut.WaitFor(); // [cite: 148]

    Assert.IsTrue(fut.Done.IsSet, 'Future.Done.IsSet should be true for preset gate construct.'); // Static string [cite: 148]
    resultValue := fut.Value; // [cite: 148]
    Assert.AreEqual(44, resultValue, 'Future result is incorrect for preset gate construct.'); // Static string
end;

[Test]
procedure TTestFuture.TestConstructWithDelayedGate;
var
    fut: TFuture<Integer>;
    delayedGate: TLatch.ILatch; // TLatch.ILatch implements TState.IState [cite: 58]
    resultValue: Integer;
begin
    delayedGate := TLatch.CreateLatch(1); // Create a latch that requires one notification to be set [cite: 66]
    Assert.IsNotNull(delayedGate, 'The delayed gate (TLatch.ILatch) should not be nil.'); // Static string
    Assert.IsFalse(delayedGate.State.IsSet, 'The delayed gate should not be initially set.'); // Static string [cite: 59, 55]

    // Construct a future with a gate that is not yet set
    fut :=
        TFuture<Integer>.Construct(
            delayedGate.State, // Get the TState.IState interface from the latch [cite: 147, 59]
            function: Integer begin Result := 45; end
        );

    Assert.IsNotNull(fut.Done, 'Future.Done should not be nil for delayed gate construct.'); // Static string [cite: 148]

    // Verify the future is not yet done as the gate is not set
    TThread.Sleep(50); // Allow some time for task scheduling
    Assert.IsFalse(fut.Done.IsSet, 'Future.Done.IsSet should be false before the delayed gate is triggered.');
    // Static string [cite: 148, 55]

    delayedGate.Notify; // Trigger the latch [cite: 46, 94] (IMycSubscriber.Notify)

    fut.WaitFor(); // Wait for the future to complete now that the gate is set [cite: 148]

    Assert.IsTrue(delayedGate.State.IsSet, 'The delayed gate should be set after Notify.'); // Static string [cite: 59, 55]
    Assert.IsTrue(fut.Done.IsSet, 'Future.Done.IsSet should be true after the delayed gate is triggered.');
    // Static string [cite: 148, 55]
    resultValue := fut.Value; // [cite: 148]
    Assert.AreEqual(45, resultValue, 'Future result is incorrect for delayed gate construct.'); // Static string
end;

[Test]
procedure TTestFuture.TestChainSimple;
var
    fut1: TFuture<Integer>;
    fut2: TFuture<string>;
    resultValue: string;
begin
    // Create an initial future
    fut1 :=
        TFuture<Integer>.Construct( // [cite: 146]
            function: Integer
            begin
                TThread.Sleep(20); // Simulate work
                Result := 100;
            end
        );

    // Chain a second future that depends on the result of the first
    fut2 :=
        fut1.Chain<string>( // [cite: 147]
            function(Input: Integer): string // This function receives the result of fut1
            begin
                TThread.Sleep(20); // Simulate further work
                Result := 'Value: ' + Input.ToString; // Use ToString for converting Integer to String
            end
        );

    Assert.IsNotNull(fut2.Done, 'Chained Future.Done should not be nil.'); // Static string [cite: 148]
    fut2.WaitFor(); // Wait for the chained future to complete [cite: 148]

    Assert.IsTrue(fut2.Done.IsSet, 'Chained Future.Done.IsSet should be true after completion.'); // Static string [cite: 148, 55]
    resultValue := fut2.Value; // [cite: 148]
    Assert.AreEqual('Value: 100', resultValue, 'Chained Future result is incorrect.'); // Static string
end;

[Test]
procedure TTestFuture.TestChainWithGate;
var
    fut1: TFuture<Integer>;
    fut2: TFuture<string>;
    delayedGate: TLatch.ILatch;
    resultValue: string;
begin
    delayedGate := TLatch.CreateLatch(1); // [cite: 66]
    Assert.IsFalse(delayedGate.State.IsSet, 'The delayed gate for chain test should not be initially set.');
    // Static string [cite: 59, 55]

    // First future depends on the delayedGate
    fut1 :=
        TFuture<Integer>.Construct(
            delayedGate.State, // [cite: 147, 59]
            function: Integer begin Result := 200; end
        );

    // Second future is chained to the first
    fut2 :=
        fut1.Chain<string>( // [cite: 147]
            function(Input: Integer): string begin Result := 'ChainVal: ' + Input.ToString; end
        );

    Assert.IsNotNull(fut2.Done, 'Chained (with gate) Future.Done should not be nil.'); // Static string [cite: 148]

    // Verify futures are not done yet
    TThread.Sleep(50);
    Assert.IsFalse(fut1.Done.IsSet, 'Initial future (gated) should not be done before gate is set.'); // Static string [cite: 148, 55]
    Assert.IsFalse(fut2.Done.IsSet, 'Chained future (gated) should not be done before gate is set.'); // Static string [cite: 148, 55]

    delayedGate.Notify; // Trigger the gate [cite: 46, 94]

    fut2.WaitFor(); // Wait for the final chained future to complete [cite: 148]

    Assert.IsTrue(fut1.Done.IsSet, 'Initial future (gated) should be done after gate is set.'); // Static string [cite: 148, 55]
    Assert.IsTrue(fut2.Done.IsSet, 'Chained future (gated) should be done after gate is set.'); // Static string [cite: 148, 55]

    resultValue := fut2.Value; // [cite: 148]
    Assert.AreEqual('ChainVal: 200', resultValue, 'Chained future (gated) result is incorrect.'); // Static string
end;

[Test]
procedure TTestFuture.TestMultipleChains;
var
    futA: TFuture<Integer>;
    futB: TFuture<Real>; // Using Real for intermediate type
    futC: TFuture<string>; // Final result as string
    finalResult: string;
begin
    // Initial future A
    futA :=
        TFuture<Integer>.Construct( // [cite: 146]
            function: Integer
            begin
                TThread.Sleep(10);
                Result := 10;
            end
        );

    // Future B, chained from A
    futB :=
        futA.Chain<Real>( // [cite: 147]
            function(InputA: Integer): Real
            begin
                TThread.Sleep(10);
                Result := InputA * 2.5; // Calculation: 10 * 2.5 = 25.0
            end
        );

    // Future C, chained from B
    futC :=
        futB.Chain<string>( // [cite: 147]
            function(InputB: Real): string
            begin
                TThread.Sleep(10);
                Result := 'Final: ' + FloatToStr(InputB); // Convert Real to String
            end
        );

    Assert.IsNotNull(futC.Done, 'Multi-chained Future.Done should not be nil.'); // Static string [cite: 148]
    futC.WaitFor(); // Wait for the last future in the chain [cite: 148]

    Assert.IsTrue(futA.Done.IsSet, 'Future A in chain should be done.'); // Static string [cite: 148, 55]
    Assert.IsTrue(futB.Done.IsSet, 'Future B in chain should be done.'); // Static string [cite: 148, 55]
    Assert.IsTrue(futC.Done.IsSet, 'Future C (multi-chained) should be done.'); // Static string [cite: 148, 55]

    finalResult := futC.Value; // [cite: 148]
    // Ensure FloatToStr conversion is consistent for comparison
    Assert.AreEqual('Final: ' + FloatToStr(25.0), finalResult, 'Multi-chained Future result is incorrect.'); // Static string
end;

[Test]
[TestCase('StringFutureTest', 'Test String')]
[TestCase('IntegerFutureTestForParam', '12345')]
procedure TTestFuture.TestConstructSimple_Parametric(const ParamValue: string);
var
    fut: TFuture<string>;
    resultValue: string;
begin
    // Parametric test for Future<string>
    fut :=
        TFuture<string>.Construct( // [cite: 146]
            function: string
            begin
                TThread.Sleep(10);
                Result := ParamValue; // Use the parameter in the future's function
            end
        );

    Assert.IsNotNull(fut.Done, 'Parametric Future.Done should not be nil.'); // Static string [cite: 148]
    fut.WaitFor(); // [cite: 148]

    Assert.IsTrue(fut.Done.IsSet, 'Parametric Future.Done.IsSet should be true.'); // Static string [cite: 148, 55]
    resultValue := fut.Value; // [cite: 148]
    Assert.AreEqual(ParamValue, resultValue, 'Parametric Future result does not match input parameter.'); // Static string
end;

[Test]
procedure TTestFuture.TestStress_StateAll;
const
    StressTestFutureCount = 100; // Number of futures for the stress test
var
    Futures: TArray<TFuture<Integer>>;
    doneStates: TArray<TState>;
    combinedStateAll: TState.IState;
    masterFuture: TFuture<Boolean>;
    i: Integer;
begin
    SetLength(Futures, StressTestFutureCount);
    SetLength(doneStates, StressTestFutureCount);
    Randomize; // Initialize random number generator for varied delays

    // Create multiple futures, each with a small random delay
    for i := 0 to High(Futures) do
    begin
        // Capture loop variable for use in anonymous method
        Futures[i] :=
            TFuture<Integer>.Construct( // [cite: 146]
                (
                    function(captureIndex: Integer): TFunc<Integer>
                    begin
                        Result :=
                            function: Integer
                            var
                                delay: Integer;
                            begin
                                delay := 10 + Random(40); // Random delay between 10ms and 49ms
                                TThread.Sleep(delay);
                                Result := captureIndex; // Return the captured index
                            end;
                    end
                )(i)
            );
        doneStates[i] := Futures[i].Done; // [cite: 148]
    end;

    combinedStateAll := TState.All(doneStates); // [cite: 67]
    Assert.IsNotNull(combinedStateAll, 'State.All should return a valid TState.IState.'); // Static string

    // Create a master future that waits on the combined State.All state
    masterFuture :=
        TFuture<Boolean>.Construct(
            combinedStateAll, // [cite: 147]
            function: Boolean
            begin
                Result := True; // This function executes when combinedStateAll is set
            end
        );
    masterFuture.WaitFor(); // Wait for all futures to complete via the master future [cite: 148]

    Assert.IsTrue(combinedStateAll.IsSet, 'Combined State.All should be set after masterFuture.WaitFor().'); // Static string [cite: 55]

    // Verify all individual futures are done and their results are correct
    for i := 0 to High(Futures) do
    begin
        Assert.IsTrue(Futures[i].Done.IsSet, 'An individual future was not set after State.All completed.');
        // Static string [cite: 148, 55]
        if Futures[i].Done.IsSet then // Additional check to safely access Result
        begin
            Assert.AreEqual(i, Futures[i].Value, 'A future''s result was incorrect in State.All stress test.');
            // Static string [cite: 148]
        end;
    end;
end;

[Test]
procedure TTestFuture.TestStress_StateAny;
const
    StressTestFutureCount = 50; // Number of futures for the stress test
    QuickFutureIndex = StressTestFutureCount div 3; // Designate one future to be quicker
var
    Futures: TArray<TFuture<Integer>>;
    doneStates: TArray<TState>;
    combinedStateAny: TState;
    masterFuture: TFuture<Boolean>;
    i: Integer;
    isAtLeastOneSet: Boolean;
begin
    SetLength(Futures, StressTestFutureCount);
    SetLength(doneStates, StressTestFutureCount);
    Randomize; // Initialize random number generator

    // Create multiple futures, one of which is designed to finish quickly
    for i := 0 to High(Futures) do
    begin
        // Capture loop variable
        Futures[i] :=
            TFuture<Integer>.Construct( // [cite: 146]
                (
                    function(captureIndex: Integer): TFunc<Integer>
                    begin
                        Result :=
                            function: Integer
                            var
                                delay: Integer;
                            begin
                                if captureIndex = QuickFutureIndex then
                                    delay := 5 // Short delay for the 'quick' future
                                else
                                    delay := 50 + Random(100); // Longer random delay (50-149ms) for others
                                TThread.Sleep(delay);
                                Result := captureIndex;
                            end;
                    end
                )(i)
            );
        doneStates[i] := Futures[i].Done; // [cite: 148]
    end;

    combinedStateAny := TState.Any(doneStates); // [cite: 68]
    Assert.IsNotNull(combinedStateAny, 'State.Any should return a valid TState.IState.'); // Static string

    // Create a master future that waits on the combined State.Any state
    masterFuture :=
        TFuture<Boolean>.Construct(
            combinedStateAny, // [cite: 147]
            function: Boolean
            begin
                Result := True; // This function executes when combinedStateAny is set
            end
        );
    masterFuture.WaitFor(); // Wait for at least one future to complete [cite: 148]

    Assert.IsTrue(combinedStateAny.IsSet, 'Combined State.Any should be set after masterFuture.WaitFor().'); // Static string [cite: 55]

    // Verify that at least one of the original futures is now set
    isAtLeastOneSet := False;
    for i := 0 to High(Futures) do
    begin
        if Futures[i].Done.IsSet then // [cite: 148, 55]
        begin
            isAtLeastOneSet := True;
        end;
    end;
    Assert.IsTrue(isAtLeastOneSet, 'At least one underlying future should be set after State.Any completed.'); // Static string

    // It's good practice to ensure all futures complete
    for i := 0 to High(Futures) do
    begin
        if not Futures[i].Done.IsSet then // [cite: 148, 55]
        begin
            Futures[i].WaitFor(); // Wait for any remaining futures [cite: 148]
        end;
    end;
end;

[Test]
procedure TTestFuture.TestNestedFuture_Construct;
var
    outerFuture: TFuture<TFuture<Integer>>;
    innerFuture: TFuture<Integer>;
    finalResult: Integer;
begin
    // Create an outer future that, when resolved, produces another (inner) future.
    outerFuture :=
        TFuture<TFuture<Integer>>.Construct( // [cite: 146]
            function: TFuture<Integer> // This lambda returns a Future<Integer>
            begin
                TThread.Sleep(10); // Simulate work for the outer future to produce the inner one
                Result :=
                    TFuture<Integer>.Construct( // [cite: 146]
                        function: Integer
                        begin
                            TThread.Sleep(10); // Simulate work for the inner future
                            Result := 123; // The final value
                        end
                    );
            end
        );

    Assert.IsNotNull(outerFuture.Done, 'Outer future''s Done state should not be nil.'); // Static string [cite: 148]
    outerFuture.WaitFor(); // Wait for the outer future to complete and yield the inner future [cite: 148]
    Assert.IsTrue(outerFuture.Done.IsSet, 'Outer future should be done after WaitFor.'); // Static string [cite: 148, 55]

    innerFuture := outerFuture.Value; // Retrieve the inner future [cite: 148]
    Assert.IsNotNull(innerFuture.Done, 'Inner future (from outer.Result) should have a non-nil Done state.'); // Static string [cite: 148]

    innerFuture.WaitFor(); // Wait for the inner future to complete and yield the final result [cite: 148]
    Assert.IsTrue(innerFuture.Done.IsSet, 'Inner future should be done after its WaitFor.'); // Static string [cite: 148, 55]

    finalResult := innerFuture.Value; // Retrieve the final integer result [cite: 148]
    Assert.AreEqual(123, finalResult, 'Nested future final result from Construct is incorrect.'); // Static string
end;

[Test]
procedure TTestFuture.TestNestedFuture_Chain;
var
    initialFuture: TFuture<Integer>;
    outerChainedFuture: TFuture<TFuture<string>>; // Future<S> where S is Future<string>
    innerStringFuture: TFuture<string>;
    finalResult: string;
begin
    // Create an initial future
    initialFuture :=
        TFuture<Integer>.Construct( // [cite: 146]
            function: Integer
            begin
                TThread.Sleep(10);
                Result := 77;
            end
        );

    // Chain it with a function that itself returns a new Future<string>
    outerChainedFuture :=
        initialFuture.Chain<TFuture<string>>( // [cite: 147]
            function(Input: Integer): TFuture<string> // This lambda returns a Future<string>
            begin
                TThread.Sleep(10); // Simulate work in the chain function
                // Input is the result of initialFuture (77)
                Result :=
                    TFuture<string>.Construct( // [cite: 146]
                        function: string
                        begin
                            TThread.Sleep(10); // Simulate work for the inner-most future
                            Result := 'Value: ' + Input.ToString; // Input is captured (77)
                        end
                    );
            end
        );

    Assert.IsNotNull(outerChainedFuture.Done, 'Outer chained future''s Done state should not be nil.'); // Static string [cite: 148]
    outerChainedFuture.WaitFor(); // Wait for initialFuture to complete AND the chain function to execute [cite: 148]
    Assert.IsTrue(outerChainedFuture.Done.IsSet, 'Outer chained future should be done after WaitFor.'); // Static string [cite: 148, 55]

    innerStringFuture := outerChainedFuture.Value; // Get the Future<string> produced by the chain function [cite: 148]
    Assert.IsNotNull(innerStringFuture.Done, 'Inner string future (from chain) should have a non-nil Done state.');
    // Static string [cite: 148]

    innerStringFuture.WaitFor(); // Wait for the inner Future<string> to complete [cite: 148]
    Assert.IsTrue(innerStringFuture.Done.IsSet, 'Inner string future should be done after its WaitFor.'); // Static string [cite: 148, 55]

    finalResult := innerStringFuture.Value; // Get the final string result [cite: 148]
    Assert.AreEqual('Value: 77', finalResult, 'Nested future (via Chain) final result is incorrect.'); // Static string
end;

end.
