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

namespace MultiWheelC.TrajectoryPlanning.PathSmoothing;

/// <summary>
/// Local G2 路径平滑管线产生的不可变结果。
/// 只有可发布状态携带完整、已验证的路径和方向段；失败、取消和无效输入结果始终提供空集合，不能被当作部分路径消费。
/// </summary>
public sealed class PathSmoothingResult
{
    private static readonly IReadOnlyList<SmoothedPathPoint> EmptyPath =
        new ReadOnlyCollection<SmoothedPathPoint>(new List<SmoothedPathPoint>());
    private static readonly IReadOnlyList<SmoothedPathSegment> EmptySegments =
        new ReadOnlyCollection<SmoothedPathSegment>(new List<SmoothedPathSegment>());
    private static readonly IReadOnlyList<PathSmoothingRegionReport> EmptyRegionReports =
        new ReadOnlyCollection<PathSmoothingRegionReport>(new List<PathSmoothingRegionReport>());

    private PathSmoothingResult(
        PathSmoothingStatus status,
        SmoothingMethod? method,
        IReadOnlyList<SmoothedPathPoint> path,
        IReadOnlyList<SmoothedPathSegment> segments,
        PathSmoothingDiagnostics diagnostics,
        IReadOnlyList<PathSmoothingRegionReport> regionReports)
    {
        Status = status;
        Method = method;
        Path = path;
        Segments = segments;
        RegionReports = regionReports;
        Diagnostics = diagnostics ?? new PathSmoothingDiagnostics(
            new PathQualityMetrics(),
            TimeSpan.Zero,
            "No smoothing diagnostics were supplied.");
    }

    /// <summary>本次平滑的终止状态；决定 <see cref="Path"/> 和 <see cref="Segments"/> 是否可消费。</summary>
    public PathSmoothingStatus Status { get; }

    /// <summary>成功发布路径时实际采用的平滑方法；失败结果为 <see langword="null"/>。</summary>
    public SmoothingMethod? Method { get; }

    /// <summary>成功时按起点到终点排列的不可变平滑路径；位置和弧长单位为 m，航向单位为 rad；失败时为空。</summary>
    public IReadOnlyList<SmoothedPathPoint> Path { get; }

    /// <summary>成功时覆盖 <see cref="Path"/> 的不可变方向段集合；失败时为空。</summary>
    public IReadOnlyList<SmoothedPathSegment> Segments { get; }

    /// <summary>各 Local G2 区域的不可变处理报告；失败结果不包含区域发布记录。</summary>
    public IReadOnlyList<PathSmoothingRegionReport> RegionReports { get; }

    /// <summary>质量指标、耗时和终止原因；始终存在，供调用方诊断成功或失败。</summary>
    public PathSmoothingDiagnostics Diagnostics { get; }

    /// <summary>发布已经过独立几何、曲率、净空和连续碰撞复核的 Local G2 结果。</summary>
    /// <param name="status">可发布状态，只能是完整、部分改进、不需要平滑或保持原样之一。</param>
    /// <param name="path">按起点到终点顺序排列的完整平滑路径；位置与弧长单位为 m，航向单位为 rad。</param>
    /// <param name="segments">覆盖完整路径的前进/倒车方向段集合。</param>
    /// <param name="diagnostics">包含可行质量指标和终止说明的诊断快照。</param>
    /// <param name="regionReports">每个局部区域的不可变处理报告集合，不能为 <see langword="null"/>。</param>
    /// <returns>携带防御性复制路径、分段和区域报告的不可变可消费结果。</returns>
    public static PathSmoothingResult PublishLocalG2(
        PathSmoothingStatus status,
        IReadOnlyList<SmoothedPathPoint> path,
        IReadOnlyList<SmoothedPathSegment> segments,
        PathSmoothingDiagnostics diagnostics,
        IReadOnlyList<PathSmoothingRegionReport> regionReports)
    {
        if (status != PathSmoothingStatus.Complete &&
            status != PathSmoothingStatus.PartialImprovement &&
            status != PathSmoothingStatus.NotNeeded &&
            status != PathSmoothingStatus.Unchanged)
        {
            throw new ArgumentException("Use a Local G2 publication status.", nameof(status));
        }
        if (regionReports == null)
            throw new ArgumentNullException(nameof(regionReports));

        ValidatePublishedResult(path, segments, diagnostics);
        return new PathSmoothingResult(
            status,
            SmoothingMethod.LocalG2Quintic,
            CopyReadOnly(path),
            CopyReadOnly(segments),
            diagnostics,
            CopyReadOnly(regionReports));
    }

    /// <summary>创建明确不发布路径的失败、取消或无效输入结果。</summary>
    /// <param name="status">非可发布的终止状态。</param>
    /// <param name="diagnostics">失败原因、质量指标和耗时；为 <see langword="null"/> 时生成默认诊断。</param>
    /// <returns>路径、方向段和区域报告均为空的不可变结果，不能作为部分路径使用。</returns>
    public static PathSmoothingResult Failure(PathSmoothingStatus status, PathSmoothingDiagnostics diagnostics)
    {
        if (status == PathSmoothingStatus.Complete ||
            status == PathSmoothingStatus.PartialImprovement ||
            status == PathSmoothingStatus.NotNeeded ||
            status == PathSmoothingStatus.Unchanged)
        {
            throw new ArgumentException("Use PublishLocalG2 to publish a path.", nameof(status));
        }
        if (!Enum.IsDefined(typeof(PathSmoothingStatus), status))
            throw new ArgumentOutOfRangeException(nameof(status));
        return new PathSmoothingResult(status, null, EmptyPath, EmptySegments, diagnostics, EmptyRegionReports);
    }

    private static void ValidatePublishedResult(
        IReadOnlyList<SmoothedPathPoint> path,
        IReadOnlyList<SmoothedPathSegment> segments,
        PathSmoothingDiagnostics diagnostics)
    {
        if (path == null || path.Count == 0)
            throw new ArgumentException("Published smoothing results require a non-empty path.", nameof(path));
        if (segments == null || segments.Count == 0)
            throw new ArgumentException("Published smoothing results require non-empty segments.", nameof(segments));
        if (diagnostics == null || diagnostics.Metrics == null || !diagnostics.Metrics.IsFeasible)
            throw new ArgumentException("Published smoothing results require feasible diagnostics.", nameof(diagnostics));
    }

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