namespace Flyshot.Core.Domain;

/// <summary>
/// 表示飞拍链路在进入运行时之前就已经准备完成的最终发送结果。
/// </summary>
public sealed class FlyshotPreparedExecution
{
    /// <summary>
    /// 初始化一份飞拍最终发送结果。
    /// </summary>
    /// <param name="samples">最终 8ms 发送点列。</param>
    /// <param name="triggerBindings">与最终发送点列对齐的触发绑定结果。</param>
    /// <param name="timingRows">与最终发送点列一致的时间映射诊断行。</param>
    /// <param name="jerkRows">与最终发送点列一致的跃度诊断行。</param>
    /// <param name="requestSpeedRatio">请求的执行倍率。</param>
    /// <param name="finalSpeedRatio">最终发送队列采用的采样倍率；请求倍率已折算进规划时为 1。</param>
    /// <param name="finalDurationSeconds">最终发送总时长，单位为秒。</param>
    /// <param name="stretchIterationCount">历史诊断字段；当前执行侧不自动改写 speed_ratio，因此正常为 0。</param>
    public FlyshotPreparedExecution(
        IEnumerable<FlyshotPreparedSample> samples,
        IEnumerable<FlyshotPreparedTriggerBinding> triggerBindings,
        IEnumerable<IReadOnlyList<double>> timingRows,
        IEnumerable<IReadOnlyList<double>> jerkRows,
        double requestSpeedRatio,
        double finalSpeedRatio,
        double finalDurationSeconds,
        int stretchIterationCount)
    {
        ArgumentNullException.ThrowIfNull(samples);
        ArgumentNullException.ThrowIfNull(triggerBindings);
        ArgumentNullException.ThrowIfNull(timingRows);
        ArgumentNullException.ThrowIfNull(jerkRows);

        if (requestSpeedRatio <= 0.0 || double.IsNaN(requestSpeedRatio) || double.IsInfinity(requestSpeedRatio))
        {
            throw new ArgumentOutOfRangeException(nameof(requestSpeedRatio), "请求速度倍率必须是有限正数。");
        }

        if (finalSpeedRatio <= 0.0 || double.IsNaN(finalSpeedRatio) || double.IsInfinity(finalSpeedRatio))
        {
            throw new ArgumentOutOfRangeException(nameof(finalSpeedRatio), "最终速度倍率必须是有限正数。");
        }

        if (finalDurationSeconds < 0.0 || double.IsNaN(finalDurationSeconds) || double.IsInfinity(finalDurationSeconds))
        {
            throw new ArgumentOutOfRangeException(nameof(finalDurationSeconds), "最终发送总时长必须是有限非负数。");
        }

        if (stretchIterationCount < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(stretchIterationCount), "倍率改写迭代次数必须是非负整数。");
        }

        Samples = samples.Select(static sample => sample).ToArray();
        TriggerBindings = triggerBindings.Select(static binding => binding).ToArray();
        TimingRows = timingRows.Select(static row => row.ToArray()).ToArray();
        JerkRows = jerkRows.Select(static row => row.ToArray()).ToArray();
        RequestSpeedRatio = requestSpeedRatio;
        FinalSpeedRatio = finalSpeedRatio;
        FinalDurationSeconds = finalDurationSeconds;
        StretchIterationCount = stretchIterationCount;
    }

    /// <summary>
    /// 获取最终 8ms 发送点列。
    /// </summary>
    public IReadOnlyList<FlyshotPreparedSample> Samples { get; }

    /// <summary>
    /// 获取与最终发送点列对齐的触发绑定结果。
    /// </summary>
    public IReadOnlyList<FlyshotPreparedTriggerBinding> TriggerBindings { get; }

    /// <summary>
    /// 获取与最终发送点列一致的时间映射诊断行。
    /// </summary>
    public IReadOnlyList<IReadOnlyList<double>> TimingRows { get; }

    /// <summary>
    /// 获取与最终发送点列一致的跃度诊断行。
    /// </summary>
    public IReadOnlyList<IReadOnlyList<double>> JerkRows { get; }

    /// <summary>
    /// 获取请求的执行倍率。
    /// </summary>
    public double RequestSpeedRatio { get; }

    /// <summary>
    /// 获取最终发送队列采用的采样倍率；请求倍率保留在 <see cref="RequestSpeedRatio"/> 中用于诊断。
    /// </summary>
    public double FinalSpeedRatio { get; }

    /// <summary>
    /// 获取最终发送总时长，单位为秒。
    /// </summary>
    public double FinalDurationSeconds { get; }

    /// <summary>
    /// 获取历史诊断字段；当前执行侧不自动改写 speed_ratio，因此正常为 0。
    /// </summary>
    public int StretchIterationCount { get; }
}

/// <summary>
/// 表示飞拍最终发送队列中的一个 8ms 发送点。
/// </summary>
public sealed class FlyshotPreparedSample
{
    /// <summary>
    /// 初始化一条最终发送点。
    /// </summary>
    /// <param name="sampleIndex">发送周期序号。</param>
    /// <param name="sendTime">物理发送时间，单位为秒。</param>
    /// <param name="trajectoryTime">回映射到规划轨迹的采样时间，单位为秒。</param>
    /// <param name="speedRatio">生成该发送点时采用的队列采样倍率，已规划调速路径通常为 1。</param>
    /// <param name="jointsDegrees">J519 下发使用的角度制关节目标。</param>
    public FlyshotPreparedSample(
        long sampleIndex,
        double sendTime,
        double trajectoryTime,
        double speedRatio,
        IReadOnlyList<double> jointsDegrees)
    {
        ArgumentNullException.ThrowIfNull(jointsDegrees);
        SampleIndex = sampleIndex;
        SendTime = sendTime;
        TrajectoryTime = trajectoryTime;
        SpeedRatio = speedRatio;
        JointsDegrees = jointsDegrees.ToArray();
    }

    /// <summary>
    /// 获取发送周期序号。
    /// </summary>
    public long SampleIndex { get; }

    /// <summary>
    /// 获取物理发送时间，单位为秒。
    /// </summary>
    public double SendTime { get; }

    /// <summary>
    /// 获取回映射到规划轨迹的采样时间，单位为秒。
    /// </summary>
    public double TrajectoryTime { get; }

    /// <summary>
    /// 获取生成该发送点时采用的队列采样倍率。
    /// </summary>
    public double SpeedRatio { get; }

    /// <summary>
    /// 获取 J519 下发使用的角度制关节目标。
    /// </summary>
    public IReadOnlyList<double> JointsDegrees { get; }
}

/// <summary>
/// 表示理论触发事件最终绑定到哪个发送点的结果。
/// </summary>
public sealed class FlyshotPreparedTriggerBinding
{
    /// <summary>
    /// 初始化一条最终触发绑定结果。
    /// </summary>
    /// <param name="trigger">理论触发事件。</param>
    /// <param name="sample">最终绑定的发送点。</param>
    /// <param name="sampleIndex">最终绑定到的发送点索引。</param>
    /// <param name="foundInWindow">是否在理论搜索窗口内找到该发送点。</param>
    public FlyshotPreparedTriggerBinding(
        TrajectoryDoEvent trigger,
        FlyshotPreparedSample sample,
        int sampleIndex,
        bool foundInWindow)
    {
        Trigger = trigger ?? throw new ArgumentNullException(nameof(trigger));
        Sample = sample ?? throw new ArgumentNullException(nameof(sample));
        SampleIndex = sampleIndex;
        FoundInWindow = foundInWindow;
    }

    /// <summary>
    /// 获取理论触发事件。
    /// </summary>
    public TrajectoryDoEvent Trigger { get; }

    /// <summary>
    /// 获取最终绑定的发送点。
    /// </summary>
    public FlyshotPreparedSample Sample { get; }

    /// <summary>
    /// 获取最终绑定到的发送点索引。
    /// </summary>
    public int SampleIndex { get; }

    /// <summary>
    /// 获取是否在理论搜索窗口内完成绑定。
    /// </summary>
    public bool FoundInWindow { get; }
}
