using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace MultiWheelC.TrajectoryPlanning.PathSmoothing.Output.Comparison;

/// <summary>Immutable raw-baseline or Local G2 report entry.</summary>
public sealed class PathSmoothingComparisonEntry
{
    public PathSmoothingComparisonEntry(
        SmoothingMethod method,
        PathSmoothingStatus status,
        PathQualityMetrics metrics,
        SmoothingTimingSummary timing,
        string stableGeometryDigest,
        string diagnostic)
        : this(method, false, status, metrics, timing, stableGeometryDigest, diagnostic, Empty<SmoothedPathPoint>(), Empty<SmoothedPathSegment>())
    {
    }

    private PathSmoothingComparisonEntry(
        SmoothingMethod? method,
        bool isRawPathBaseline,
        PathSmoothingStatus status,
        PathQualityMetrics metrics,
        SmoothingTimingSummary timing,
        string stableGeometryDigest,
        string diagnostic,
        IReadOnlyList<SmoothedPathPoint> path,
        IReadOnlyList<SmoothedPathSegment> segments)
    {
        Method = method;
        IsRawPathBaseline = isRawPathBaseline;
        Status = status;
        Metrics = metrics ?? new PathQualityMetrics();
        Timing = timing ?? new SmoothingTimingSummary(new double[] { 0d, 0d, 0d, 0d, 0d }, false, "No timing result was supplied.");
        StableGeometryDigest = stableGeometryDigest ?? string.Empty;
        Diagnostic = diagnostic ?? string.Empty;
        Path = CopyReadOnly(path);
        Segments = CopyReadOnly(segments);
    }

    public SmoothingMethod? Method { get; }

    public bool IsRawPathBaseline { get; }

    public PathSmoothingStatus Status { get; }

    public PathQualityMetrics Metrics { get; }

    public SmoothingTimingSummary Timing { get; }

    public string StableGeometryDigest { get; }

    public string Diagnostic { get; }

    public IReadOnlyList<SmoothedPathPoint> Path { get; }

    public IReadOnlyList<SmoothedPathSegment> Segments { get; }

    public bool IsEligibleForRecommendation =>
        !IsRawPathBaseline &&
        IsPublishedLocalG2Status(Status) &&
        Metrics.IsFeasible &&
        Timing.IsDeterministic;

    internal static bool IsPublishedLocalG2Status(PathSmoothingStatus status)
    {
        return status == PathSmoothingStatus.Complete ||
            status == PathSmoothingStatus.PartialImprovement ||
            status == PathSmoothingStatus.NotNeeded ||
            status == PathSmoothingStatus.Unchanged;
    }

    internal static PathSmoothingComparisonEntry CreateCandidate(
        SmoothingMethod method,
        PathSmoothingStatus status,
        PathQualityMetrics metrics,
        SmoothingTimingSummary timing,
        string stableGeometryDigest,
        string diagnostic,
        IReadOnlyList<SmoothedPathPoint> path,
        IReadOnlyList<SmoothedPathSegment> segments)
    {
        return new PathSmoothingComparisonEntry(
            method, false, status, metrics, timing, stableGeometryDigest, diagnostic, path, segments);
    }

    internal static PathSmoothingComparisonEntry CreateRawPathBaseline(
        PathSmoothingStatus status,
        PathQualityMetrics metrics,
        string stableGeometryDigest,
        string diagnostic,
        IReadOnlyList<SmoothedPathPoint> path,
        IReadOnlyList<SmoothedPathSegment> segments)
    {
        return new PathSmoothingComparisonEntry(
            null, true, status, metrics,
            new SmoothingTimingSummary(new double[] { 0d, 0d, 0d, 0d, 0d }, true, string.Empty),
            stableGeometryDigest, diagnostic, path, segments);
    }

    private static IReadOnlyList<T> Empty<T>()
    {
        return new ReadOnlyCollection<T>(new List<T>());
    }

    private static IReadOnlyList<T> CopyReadOnly<T>(IReadOnlyList<T> source)
    {
        var copy = new List<T>(source == null ? 0 : source.Count);
        if (source != null)
        {
            for (int index = 0; index < source.Count; index++) copy.Add(source[index]);
        }
        return new ReadOnlyCollection<T>(copy);
    }
}
