using Process.Interface;
using Process.Interface.Configuration;
using System;
using System.Collections.Generic;

namespace Process.Abstract.Configuration;

internal class AnonymousProcessor<TInput, TOutput> : Processor<TInput, TOutput>
{
  private readonly Func<IEnumerable<TInput>, IEnumerable<TOutput>> _func;

  public AnonymousProcessor(Func<IEnumerable<TInput>, IEnumerable<TOutput>> func)
  {
    _func = func;
  }


  #region Overrides of Processor<TInput,TOutput>

  /// <inheritdoc />
  public override IEnumerable<TOutput> Process(IEnumerable<TInput> inputs)
  {
    return _func.Invoke(inputs);
  }

  #endregion
}

/// <summary>
/// Basic implementation for <see cref="ProcessorChainConfiguration{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>
public class ProcessorChainConfiguration<TInput, TOutput>
  : ProcessorChain<TInput, TOutput>, IProcessorChainConfiguration<TInput, TOutput>
{
  /// <inheritdoc />
  public IProcessorChainConfiguration<T, TOutput, TInput, TOutput> Use<T>(
    IProcessor<TInput, T> processor
  )
  {
    Processors.Add(processor);
    return new ProcessorChainConfiguration<T, TOutput, TInput, TOutput>(this);
  }

  /// <inheritdoc />
  public IProcessorChainConfiguration<T, TOutput, TInput, TOutput> Use<T>(
    Func<IEnumerable<TInput>, IEnumerable<T>> processorFunc
  )
  {
    return Use<T>(new AnonymousProcessor<TInput, T>(processorFunc));
  }

  /// <inheritdoc />
  public IProcessorChainConfiguration<TInput, TOutput> Complete(
    IProcessor<TInput, TOutput> processor
  )
  {
    Processors.Add(processor);
    return this;
  }

  /// <inheritdoc />
  public IProcessorChainConfiguration<TInput, TOutput> Complete(
    Func<IEnumerable<TInput>, IEnumerable<TOutput>> processorFunc
  )
  {
    return Complete(new AnonymousProcessor<TInput, TOutput>(processorFunc));
  }
}

/// <summary>
/// Basic implementation for <see cref="ProcessorChainConfiguration{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 class ProcessorChainConfiguration<TInput, TOutput, TInChain, TOutChain>
  : IProcessorChainConfiguration<TInput, TOutput, TInChain, TOutChain>
{
  private readonly IProcessorChain<TInChain, TOutChain> _chain;

  /// <summary>
  /// Constructor
  /// </summary>
  /// <param name="chain">Parent <see cref="IProcessorChain{TInput,TOutput}"/></param>
  internal ProcessorChainConfiguration(IProcessorChain<TInChain, TOutChain> chain)
  {
    _chain = chain;
  }

  #region Implementation of IProcessorStep<out TInput,TOutput>

  /// <inheritdoc />
  public IProcessorChainConfiguration<T, TOutput, TInChain, TOutChain> Use<T>(
    IProcessor<TInput, T> processor
  )
  {
    _chain.Processors.Add(processor);
    return new ProcessorChainConfiguration<T, TOutput, TInChain, TOutChain>(_chain);
  }

  /// <inheritdoc />
  public IProcessorChainConfiguration<T, TOutput, TInChain, TOutChain> Use<T>(
    Func<IEnumerable<TInput>, IEnumerable<T>> processorFunc
  )
  {
    return Use(new AnonymousProcessor<TInput, T>(processorFunc));
  }

  /// <inheritdoc />
  public IProcessorChain<TInChain, TOutChain> Complete(IProcessor<TInput, TOutput> processor)
  {
    _chain.Processors.Add(processor);
    return _chain;
  }

  /// <inheritdoc />
  public IProcessorChain<TInChain, TOutChain> Complete(
    Func<IEnumerable<TInput>, IEnumerable<TOutput>> processorFunc
  )
  {
    return Complete(new AnonymousProcessor<TInput, TOutput>(processorFunc));
  }

  #endregion
}
