// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Agents.AI.Workflows;

/// <summary>
/// Provides services for an <see cref="Executor"/> during the execution of a workflow.
/// </summary>
public interface IWorkflowContext
{
    /// <summary>
    /// Adds an event to the workflow's output queue. These events will be raised to the caller of the workflow at the
    /// end of the current SuperStep.
    /// </summary>
    /// <param name="workflowEvent">The event to be raised.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
    ValueTask AddEventAsync(WorkflowEvent workflowEvent, CancellationToken cancellationToken = default);

    /// <summary>
    /// Queues a message to be sent to connected executors. The message will be sent during the next SuperStep.
    /// </summary>
    /// <param name="message">The message to be sent.</param>
    /// <param name="targetId">An optional identifier of the target executor. If null, the message is sent to all connected
    /// executors. If the target executor is not connected from this executor via an edge, it will still not receive the
    /// message.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
    ValueTask SendMessageAsync(object message, string? targetId, CancellationToken cancellationToken = default);

    /// <summary>
    /// Adds an output value to the workflow's output queue. These outputs will be bubbled out of the workflow using the
    /// <see cref="WorkflowOutputEvent"/>
    /// </summary>
    /// <remarks>
    /// The type of the output message must match one of the output types declared by the Executor. By default, the return
    /// types of registered message handlers are considered output types, unless otherwise specified using <see cref="ExecutorOptions"/>.
    /// </remarks>
    /// <param name="output">The output value to be returned.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
    ValueTask YieldOutputAsync(object output, CancellationToken cancellationToken = default);

    /// <summary>
    /// Adds a request to "halt" workflow execution at the end of the current SuperStep.
    /// </summary>
    /// <returns></returns>
    ValueTask RequestHaltAsync();

    /// <summary>
    /// Reads a state value from the workflow's state store. If no scope is provided, the executor's
    /// default scope is used.
    /// </summary>
    /// <typeparam name="T">The type of the state value.</typeparam>
    /// <param name="key">The key of the state value.</param>
    /// <param name = "scopeName" > An optional name that specifies the scope to read.If null, the default scope is
    /// used.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    /// <returns>A <see cref="ValueTask{T}"/> representing the asynchronous operation.</returns>
    ValueTask<T?> ReadStateAsync<T>(string key, string? scopeName = null, CancellationToken cancellationToken = default);

    /// <summary>
    /// Reads or initialized a state value from the workflow's state store. If no scope is provided, the executor's
    /// default scope is used.
    /// </summary>
    /// <remarks>
    /// When initializing the state, the state will be queued as an update. If multiple initializations are done in the same
    /// SuperStep from different executors, an error will be generated at the end of the SuperStep.
    /// </remarks>
    /// <typeparam name="T">The type of the state value.</typeparam>
    /// <param name="key">The key of the state value.</param>
    /// <param name="initialStateFactory">A factory to initialize the state if the key has no value associated with it.</param>
    /// <param name = "scopeName" > An optional name that specifies the scope to read. If null, the default scope is
    /// used.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    /// <returns>A <see cref="ValueTask{T}"/> representing the asynchronous operation.</returns>
    ValueTask<T> ReadOrInitStateAsync<T>(string key, Func<T> initialStateFactory, string? scopeName = null, CancellationToken cancellationToken = default);

#if NET // See above for musings about this construction
    /// <summary>
    /// Reads a state value from the workflow's state store. If no scope is provided, the executor's
    /// default scope is used.
    /// </summary>
    /// <typeparam name="T">The type of the state value.</typeparam>
    /// <param name="key">The key of the state value.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
    /// <returns>A <see cref="ValueTask{T}"/> representing the asynchronous operation.</returns>
    ValueTask<T?> ReadStateAsync<T>(string key, CancellationToken cancellationToken)
        => this.ReadStateAsync<T>(key, null, cancellationToken);

    /// <summary>
    /// Reads a state value from the workflow's state store. If no scope is provided, the executor's
    /// default scope is used.
    /// </summary>
    /// <typeparam name="T">The type of the state value.</typeparam>
    /// <param name="key">The key of the state value.</param>
    /// <param name="initialStateFactory">A factory to initialize the state if the key has no value associated with it.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    /// <returns>A <see cref="ValueTask{T}"/> representing the asynchronous operation.</returns>
    ValueTask<T> ReadOrInitStateAsync<T>(string key, Func<T> initialStateFactory, CancellationToken cancellationToken)
        => this.ReadOrInitStateAsync(key, initialStateFactory, null, cancellationToken);
#endif

    /// <summary>
    /// Asynchronously reads all state keys within the specified scope.
    /// </summary>
    /// <param name="scopeName">An optional name that specifies the scope to read. If null, the default scope is
    /// used.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    ValueTask<HashSet<string>> ReadStateKeysAsync(string? scopeName = null, CancellationToken cancellationToken = default);

    /// <summary>
    /// Asynchronously updates the state of a queue entry identified by the specified key and optional scope.
    /// </summary>
    /// <remarks>
    /// Subsequent reads by this executor will result in the new value of the state. Other executors will only see
    /// the new state starting from the next SuperStep.
    /// </remarks>
    /// <typeparam name="T">The type of the value to associate with the queue entry.</typeparam>
    /// <param name="key">The unique identifier for the queue entry to update. Cannot be null or empty.</param>
    /// <param name="value">The value to set for the queue entry. If null, the entry's state may be cleared or reset depending on
    /// implementation.</param>
    /// <param name="scopeName">An optional name that specifies the scope to update. If null, the default scope is
    /// used.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    /// <returns>A ValueTask that represents the asynchronous update operation.</returns>
    ValueTask QueueStateUpdateAsync<T>(string key, T? value, string? scopeName = null, CancellationToken cancellationToken = default);

#if NET // See above for musings about this construction
    /// <summary>
    /// Asynchronously updates the state of a queue entry identified by the specified key and optional scope.
    /// </summary>
    /// <remarks>
    /// Subsequent reads by this executor will result in the new value of the state. Other executors will only see
    /// the new state starting from the next SuperStep.
    /// </remarks>
    /// <typeparam name="T">The type of the value to associate with the queue entry.</typeparam>
    /// <param name="key">The unique identifier for the queue entry to update. Cannot be null or empty.</param>
    /// <param name="value">The value to set for the queue entry. If null, the entry's state may be cleared or reset depending on
    /// implementation.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
    /// <returns>A ValueTask that represents the asynchronous update operation.</returns>
    ValueTask QueueStateUpdateAsync<T>(string key, T? value, CancellationToken cancellationToken) => this.QueueStateUpdateAsync(key, value, null, cancellationToken);
#endif

    /// <summary>
    /// Asynchronously clears all state entries within the specified scope.
    ///
    /// This semantically equivalent to retrieving all keys in the scope and deleting them one-by-one.
    /// </summary>
    /// <remarks>
    /// Subsequent reads by this executor will not find any entries in the cleared scope. Other executors will only
    /// see the cleared state starting from the next SuperStep.
    /// </remarks>
    /// <param name="scopeName">An optional name that specifies the scope to clear. If null, the default scope is used.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
    /// The default is <see cref="CancellationToken.None"/>.</param>
    /// <returns>A ValueTask that represents the asynchronous clear operation.</returns>
    ValueTask QueueClearScopeAsync(string? scopeName = null, CancellationToken cancellationToken = default);

#if NET // See above for musings about this construction
    /// <summary>
    /// Asynchronously clears all state entries within the specified scope.
    ///
    /// This semantically equivalent to retrieving all keys in the scope and deleting them one-by-one.
    /// </summary>
    /// <remarks>
    /// Subsequent reads by this executor will not find any entries in the cleared scope. Other executors will only
    /// see the cleared state starting from the next SuperStep.
    /// </remarks>
    /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
    /// <returns>A ValueTask that represents the asynchronous clear operation.</returns>
    ValueTask QueueClearScopeAsync(CancellationToken cancellationToken) => this.QueueClearScopeAsync(null, cancellationToken);
#endif

    /// <summary>
    /// The trace context associated with the current message about to be processed by the executor, if any.
    /// </summary>
    IReadOnlyDictionary<string, string>? TraceContext { get; }

    /// <summary>
    /// Whether the current execution environment support concurrent runs against the same workflow instance.
    /// </summary>
    bool ConcurrentRunsEnabled { get; }
}
