using System;
using System.Collections.Generic;

namespace Process.Interface.Configuration;

/// <summary>
/// Configuration interface for <see cref="IProcessorChain"/>s
/// </summary>
/// <typeparam name="TInput"><see cref="IProcessor{TInput,TOutput}"/> input type</typeparam>
/// <typeparam name="TOutput"><see cref="IProcessor{TInput,TOutput}"/> output type</typeparam>
public interface IProcessorChainConfiguration<TInput, TOutput> : IProcessorChain<TInput, TOutput>
{
  /// <summary>
  /// Add the specified <paramref name="processor"/> to <see cref="IProcessor{TInput,TOutput}"/> chain
  /// </summary>
  /// <param name="processor">The <see cref="IProcessor{TInput,TOutput}"/> to add to the chain</param>
  /// <returns>The <see cref="IProcessorChainConfiguration{TInput,TOutput}"/> for fluent chaining of method calls</returns>
  IProcessorChainConfiguration<T, TOutput, TInput, TOutput> Use<T>(
    IProcessor<TInput, T> processor
  );

  /// <inheritdoc cref="Use{T}(IProcessor{TInput,T})"/>
  IProcessorChainConfiguration<T, TOutput, TInput, TOutput> Use<T>(
    Func<IEnumerable<TInput>, IEnumerable<T>> processorFunc
  );

  /// <summary>
  /// Ends the configuration process and converts the
  /// <see cref="IProcessorChainConfiguration{TInput,TOutput}"/>
  /// to a <see cref="IProcessorChain"/>
  /// </summary>
  /// <param name="processor">The <see cref="IProcessor{TInput,TOutput}"/> to add to the chain</param>
  /// <returns>The configured <see cref="IProcessorChain{TInput,TOutput}"/></returns>
  IProcessorChainConfiguration<TInput, TOutput> Complete(
    IProcessor<TInput, TOutput> processor
  );

  /// <inheritdoc cref="Complete(IProcessor{TInput,TOutput})"/>
  IProcessorChainConfiguration<TInput, TOutput> Complete(
    Func<IEnumerable<TInput>, IEnumerable<TOutput>> processorFunc
  );
}

/// <summary>
/// Interface for configuring sub-steps of <see cref="IProcessorChain{TInput,TOutput}"/>s
/// </summary>
/// <typeparam name="TInput"><see cref="IProcessor{TInput,TOutput}"/> input type</typeparam>
/// <typeparam name="TOutput"><see cref="IProcessor{TInput,TOutput}"/> output type</typeparam>
/// <typeparam name="TInChain">Input type of the base <see cref="IProcessorChainConfiguration{TInput,TOutput}"/></typeparam>
/// <typeparam name="TOutChain">Output type of the base <see cref="IProcessorChainConfiguration{TInput,TOutput}"/></typeparam>
public interface IProcessorChainConfiguration<TInput, TOutput, TInChain, TOutChain>
{
  /// <summary>
  /// Add the specified <paramref name="processor"/> to <see cref="IProcessor{TInput,TOutput}"/>
  /// </summary>
  /// <param name="processor">The <see cref="IProcessor{TInput,TOutput}"/> to add to the chain</param>
  /// <returns>The <see cref="IProcessorChainConfiguration{TInput,TOutput, TInChain, TOutChain}"/> for fluent chaining of method calls</returns>
  IProcessorChainConfiguration<T, TOutput, TInChain, TOutChain> Use<T>(
    IProcessor<TInput, T> processor
  );

  /// <inheritdoc cref="Use{T}(IProcessor{TInput,T})"/>
  IProcessorChainConfiguration<T, TOutput, TInChain, TOutChain> Use<T>(
    Func<IEnumerable<TInput>, IEnumerable<T>> processorFunc
  );

  /// <summary>
  /// Ends the configuration process and returns the base <see cref="IProcessorChainConfiguration{TInput,TOutput}"/>
  /// to a <see cref="IProcessorChain"/>
  /// </summary>
  /// <param name="processor">The <see cref="IProcessor{TInput,TOutput}"/> to add to the chain</param>
  /// <returns>The configured <see cref="IProcessorChain{TInput,TOutput}"/></returns>
  IProcessorChain<TInChain, TOutChain> Complete(
    IProcessor<TInput, TOutput> processor
  );

  /// <inheritdoc cref="Complete(IProcessor{TInput,TOutput})"/>
  IProcessorChain<TInChain, TOutChain> Complete(
    Func<IEnumerable<TInput>, IEnumerable<TOutput>> processorFunc
  );
}
