#pragma once

namespace MayaFlux::Vruta {
class TaskScheduler;
class SoundRoutine;
}

namespace MayaFlux::Kriya {

/**
 * @class EventChain
 * @brief A sequential chain of timed events with precise temporal control
 *
 * The EventChain class provides a way to schedule a sequence of events to occur
 * at specific time intervals. It's designed for creating temporal sequences of
 * actions with sample-accurate timing, which is essential for deterministic computational flows.
 *
 * This approach is inspired by reactive programming and temporal logic systems
 * where precise sequencing of operations is critical. It allows for creating complex
 * temporal behaviors with a simple, declarative API.
 *
 * Example usage:
 * ```cpp
 * // Create an event chain
 * EventChain chain(*scheduler);
 *
 * // Add events with specific delays
 * chain.then([]() { process_initial_state(); })      // Immediate
 *      .then([]() { transform_data(); }, 0.5)        // After 0.5 seconds
 *      .then([]() { apply_filter(); }, 0.25)         // After another 0.25 seconds
 *      .then([]() { finalize_output(); }, 0.25);     // After another 0.25 seconds
 *
 * // Start the chain
 * chain.start();
 * ```
 *
 * The EventChain is particularly useful for creating precisely timed computational sequences,
 * state transitions, or any series of time-based events that need to occur in a specific
 * order with deterministic timing.
 */
class MAYAFLUX_API EventChain {
public:
    /**
     * @brief Constructs an EventChain with an explicit scheduler
     * @param scheduler The TaskScheduler to use for timing
     * @param name Optional name for the event chain (useful for debugging)
     *
     * Creates a new EventChain that will use the provided scheduler for timing
     * operations. This allows for more control over which scheduler is used,
     * which is useful in contexts where multiple processing engines might exist.
     */
    EventChain(Vruta::TaskScheduler& scheduler, std::string name = "");

    /**
     * @brief Adds an event to the chain with a specified delay
     * @param action Function to execute when the event occurs
     * @param delay_seconds Time to wait before executing this event (in seconds)
     * @return Reference to this EventChain for method chaining
     *
     * This method adds an event to the chain, to be executed after the specified
     * delay from the previous event. The first event's delay is measured from
     * when start() is called.
     *
     * The method returns a reference to the EventChain itself, allowing for a
     * fluent, declarative API style.
     */
    EventChain& then(std::function<void()> action, double delay_seconds = 0.F);

    /**
     * @brief Repeat the last event N times
     * @param count Number of times to repeat
     * @return Reference to this EventChain for method chaining
     */
    EventChain& repeat(size_t count);

    /**
     * @brief Repeat entire chain N times
     * @param count Number of times to repeat the entire sequence
     * @return Reference to this EventChain for method chaining
     */
    EventChain& times(size_t count);

    /**
     * @brief Add a wait/delay without an action
     * @param delay_seconds Time to wait in seconds
     * @return Reference to this EventChain for method chaining
     */
    EventChain& wait(double delay_seconds);

    /**
     * @brief Convenience method for repeating an action at regular intervals
     * @param interval_seconds Time between each execution
     * @param action Function to execute
     * @return Reference to this EventChain for method chaining
     */
    EventChain& every(double interval_seconds, std::function<void()> action);

    /**
     * @brief Starts executing the event chain
     *
     * This method begins the execution of the event chain, scheduling each event
     * to occur at its specified time. The events are executed in the order they
     * were added, with the specified delays between them.
     *
     * The timing is sample-accurate, ensuring that each event occurs at precisely
     * the right moment in the computational timeline.
     */
    void start();

    /**
     * @brief Cancels the event chain if it's currently executing
     *
     * Terminates the underlying coroutine, preventing any remaining
     * events from executing. Safe to call even if chain has completed.
     */
    void cancel();

    /**
     * @brief Checks if the event chain is currently active
     * @return True if chain is executing, false if completed or not started
     */
    [[nodiscard]] bool is_active() const;

    /**
     * @brief Sets a callback to execute when the chain stops executing
     * @param callback Function to call after chain completes or is cancelled
     *
     * The callback fires regardless of how the chain stops:
     * - After final event completes normally
     * - After cancel() is called
     * - After an exception in an action (action exceptions are caught)
     *
     * Use this for cleanup that must happen regardless of completion reason.
     * For actions that should only run on successful completion, use .then()
     */
    EventChain& on_complete(std::function<void()> callback);

    /**
     * @brief Gets the name of the event chain
     * @return Name of the event chain
     *
     * The name can be used for debugging or management purposes, especially when
     * multiple chains are active. If no name was set, this will return an empty string.
     */
    [[nodiscard]] const std::string& name() const { return m_name; }

    /**
     * @brief Gets the number of events in the chain
     * @return Number of events
     */
    [[nodiscard]] size_t event_count() const { return m_events.size(); }

    /**
     * @brief Gets the repeat count for the entire chain
     * @return Number of times the chain will repeat
     */
    [[nodiscard]] size_t repeat_count() const { return m_repeat_count; }

private:
    /**
     * @brief Structure representing a timed event in the chain
     *
     * This structure encapsulates an action to perform and the delay before
     * performing it, relative to the previous event in the chain.
     */
    struct TimedEvent {
        std::function<void()> action; ///< Function to execute
        double delay_seconds; ///< Delay before execution
    };

    /**
     * @brief Collection of events in this chain
     *
     * This vector contains all the events that have been added to the chain,
     * in the order they will be executed.
     */
    std::vector<TimedEvent> m_events;

    /**
     * @brief Reference to the scheduler that manages timing
     *
     * The scheduler provides the timing infrastructure needed for
     * precise execution of events in the chain.
     */
    Vruta::TaskScheduler& m_Scheduler;

    /**
     * @brief The underlying computational routine that implements the chain
     *
     * This coroutine handles the actual timing and execution of events
     * in the chain. It's created when start() is called.
     */
    std::shared_ptr<Vruta::SoundRoutine> m_routine;

    /**
     * @brief Optional callback to execute when the chain completes
     *
     * This function is called after the final event in the chain has executed.
     * It can be used for cleanup, triggering subsequent actions, or any other
     * behavior that should occur after the chain finishes.
     */
    std::function<void()> m_on_complete;

    /**
     * @brief Optional name for the event chain
     *
     * This name can be used for debugging or management purposes, especially when
     * multiple chains are active. If no name is provided, it will be an empty string.
     */
    std::string m_name;

    bool m_on_complete_fired {}; ///< Flag to ensure on_complete is only fired once

    /**
     * @brief Internal method to safely fire the on_complete callback
     */
    void fire_on_complete();

    size_t m_repeat_count { 1 }; ///< Number of times to repeat the entire chain

    uint64_t m_default_rate { 48000 };
};

}
