using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using MultiWheelC.TrajectoryPlanning.EMPlanner;

namespace EMPlannerVerificationHost;

internal sealed class FakeQpSolver : IQpSolver
{
    private readonly Queue<QpSolveResult> _results;
    private readonly List<QuadraticProgram> _problems = new List<QuadraticProgram>();
    private readonly List<IReadOnlyList<double>> _warmStarts = new List<IReadOnlyList<double>>();

    public FakeQpSolver(QpSolveResult result)
        : this(new[] { result })
    {
    }

    public FakeQpSolver(IEnumerable<QpSolveResult> results)
    {
        if (results == null)
            throw new ArgumentNullException(nameof(results));
        _results = new Queue<QpSolveResult>();
        foreach (QpSolveResult result in results)
            _results.Enqueue(result ?? throw new ArgumentException("Fake solver results cannot contain null values.", nameof(results)));
        if (_results.Count == 0)
            throw new ArgumentException("At least one fake solver result is required.", nameof(results));
        LastWarmStart = Array.Empty<double>();
    }

    public QuadraticProgram? LastProblem { get; private set; }

    public QpSolverSettings? LastSettings { get; private set; }

    public IReadOnlyList<double> LastWarmStart { get; private set; }

    public IReadOnlyList<QuadraticProgram> Problems => new ReadOnlyCollection<QuadraticProgram>(_problems);

    public IReadOnlyList<IReadOnlyList<double>> WarmStarts => new ReadOnlyCollection<IReadOnlyList<double>>(_warmStarts);

    public int SolveCallCount => _problems.Count;

    public QpSolveResult Solve(QuadraticProgram problem, QpSolverSettings settings, IReadOnlyList<double> warmStart,
        CancellationToken cancellationToken)
    {
        LastProblem = problem ?? throw new ArgumentNullException(nameof(problem));
        LastSettings = settings ?? throw new ArgumentNullException(nameof(settings));
        var copy = new List<double>(warmStart == null ? 0 : warmStart.Count);
        if (warmStart != null)
        {
            for (int index = 0; index < warmStart.Count; index++)
                copy.Add(warmStart[index]);
        }
        LastWarmStart = new ReadOnlyCollection<double>(copy);
        _problems.Add(LastProblem);
        _warmStarts.Add(LastWarmStart);
        if (_results.Count == 0)
            throw new InvalidOperationException("Fake solver was called more often than its scripted result sequence.");
        return _results.Dequeue();
    }
}
