using Common.Distance;

namespace Ocr.Report.Models;

public readonly struct ProcessorStat : IDistanceComparer<IEnumerable<string>>
{
  /// <summary>
  /// The name of the processor
  /// </summary>
  public string Name { get; }

  /// <summary>
  /// The total milliseconds it took the processor to process the data
  /// </summary>
  public double ProcessingTime { get; }

  /// <inheritdoc />
  public IEnumerable<string> Reference { get; }

  /// <inheritdoc />
  public IEnumerable<string>? Hypothesis { get; }

  /// <inheritdoc />
  public double Distance { get; }

  /// <summary>
  /// Information about the processed values
  /// </summary>
  public ICollection<IDistanceComparer<string>> Words { get; }

  public ProcessorStat(
    string name,
    double processingTime,
    ICollection<string> reference,
    ICollection<string> hypothesis
  )
  {
    Name = name;
    ProcessingTime = processingTime;

    Reference = reference;
    Hypothesis = hypothesis;

    Distance = Calculator.GetDistance(
      reference.OrderBy(s => s).ToArray(),
      hypothesis.OrderBy(s => s).ToArray()
    ) / reference.Count;

    Words = GetDistanceInfos(reference, hypothesis).ToArray();
  }


  /// <summary>
  /// Compares the <paramref name="referenceCollection"/> with all given values in the
  /// <paramref name="hypothesisCollection"/>, determining the <see cref="IDistanceComparer{T}"/>
  /// with the lowest error
  /// </summary>
  private static IEnumerable<IDistanceComparer<string>> GetDistanceInfos(
    ICollection<string> referenceCollection,
    ICollection<string> hypothesisCollection
  )
  {
    var results = new List<IDistanceComparer<string>>();

    foreach (var reference in referenceCollection)
    {
      var tResults = hypothesisCollection
        .Select(hypothesis => (IDistanceComparer<string>)new DistanceComparer<string>(reference, hypothesis))
        .ToList();

      results.AddRange(tResults);
    }

    var lookup = results
      .OrderBy(result => result.Distance)
      .ToLookup(result => result.Reference);

    foreach (var reference in referenceCollection)
    {
      yield return lookup[reference].FirstOrDefault() ?? new DistanceComparer<string>(reference);
    }
  }
}
