using System.Text.Json.Serialization;

namespace Flyshot.Core.Domain;

/// <summary>
/// 描述规划与运行时编排共同使用的机器人模型契约。
/// </summary>
public sealed class RobotProfile
{
    /// <summary>
    /// 使用已校验的关节约束与耦合元数据初始化机器人画像。
    /// </summary>
    public RobotProfile(
        string name,
        string modelPath,
        int degreesOfFreedom,
        IEnumerable<JointLimit> jointLimits,
        IEnumerable<JointCoupling> jointCouplings,
        TimeSpan servoPeriod,
        TimeSpan triggerPeriod)
    {
        if (string.IsNullOrWhiteSpace(name))
        {
            throw new ArgumentException("Robot profile name is required.", nameof(name));
        }

        if (string.IsNullOrWhiteSpace(modelPath))
        {
            throw new ArgumentException("Robot profile model path is required.", nameof(modelPath));
        }

        if (degreesOfFreedom <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(degreesOfFreedom), "Degrees of freedom must be positive.");
        }

        if (servoPeriod <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(servoPeriod), "Servo period must be positive.");
        }

        if (triggerPeriod <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(triggerPeriod), "Trigger period must be positive.");
        }

        ArgumentNullException.ThrowIfNull(jointLimits);
        ArgumentNullException.ThrowIfNull(jointCouplings);

        // 先对集合做一次快照，避免下游直接原地修改领域状态。
        var copiedJointLimits = jointLimits.ToArray();
        var copiedJointCouplings = jointCouplings.ToArray();

        if (copiedJointLimits.Length != degreesOfFreedom)
        {
            throw new ArgumentException("Joint limit count must match degrees of freedom.", nameof(jointLimits));
        }

        Name = name;
        ModelPath = modelPath;
        DegreesOfFreedom = degreesOfFreedom;
        JointLimits = copiedJointLimits;
        JointCouplings = copiedJointCouplings;
        ServoPeriod = servoPeriod;
        TriggerPeriod = triggerPeriod;
    }

    /// <summary>
    /// 获取对运行时其余模块暴露的机器人画像名称。
    /// </summary>
    [JsonPropertyName("name")]
    public string Name { get; }

    /// <summary>
    /// 获取机器人模型文件的来源路径。
    /// </summary>
    [JsonPropertyName("modelPath")]
    public string ModelPath { get; }

    /// <summary>
    /// 获取当前生效的旋转关节自由度数量。
    /// </summary>
    [JsonPropertyName("degreesOfFreedom")]
    public int DegreesOfFreedom { get; }

    /// <summary>
    /// 获取按关节校验后的运动学约束。
    /// </summary>
    [JsonPropertyName("jointLimits")]
    public IReadOnlyList<JointLimit> JointLimits { get; }

    /// <summary>
    /// 获取从机器人模型解析出的可选关节耦合元数据。
    /// </summary>
    [JsonPropertyName("jointCouplings")]
    public IReadOnlyList<JointCoupling> JointCouplings { get; }

    /// <summary>
    /// 获取运行时使用的伺服调度周期。
    /// </summary>
    [JsonPropertyName("servoPeriod")]
    public TimeSpan ServoPeriod { get; }

    /// <summary>
    /// 获取飞拍事件对齐使用的触发调度周期。
    /// </summary>
    [JsonPropertyName("triggerPeriod")]
    public TimeSpan TriggerPeriod { get; }
}

/// <summary>
/// 描述规划器所需的单个旋转关节约束集合。
/// </summary>
public sealed class JointLimit
{
    /// <summary>
    /// 初始化一个已校验的关节约束记录。
    /// </summary>
    public JointLimit(string jointName, double velocityLimit, double accelerationLimit, double jerkLimit)
    {
        if (string.IsNullOrWhiteSpace(jointName))
        {
            throw new ArgumentException("Joint name is required.", nameof(jointName));
        }

        if (velocityLimit <= 0.0)
        {
            throw new ArgumentOutOfRangeException(nameof(velocityLimit), "Velocity limit must be positive.");
        }

        if (accelerationLimit <= 0.0)
        {
            throw new ArgumentOutOfRangeException(nameof(accelerationLimit), "Acceleration limit must be positive.");
        }

        if (jerkLimit <= 0.0)
        {
            throw new ArgumentOutOfRangeException(nameof(jerkLimit), "Jerk limit must be positive.");
        }

        JointName = jointName;
        VelocityLimit = velocityLimit;
        AccelerationLimit = accelerationLimit;
        JerkLimit = jerkLimit;
    }

    /// <summary>
    /// 获取该约束对应的关节名称。
    /// </summary>
    [JsonPropertyName("jointName")]
    public string JointName { get; }

    /// <summary>
    /// 获取关节空间单位下的速度上限。
    /// </summary>
    [JsonPropertyName("velocityLimit")]
    public double VelocityLimit { get; }

    /// <summary>
    /// 获取关节空间单位下的加速度上限。
    /// </summary>
    [JsonPropertyName("accelerationLimit")]
    public double AccelerationLimit { get; }

    /// <summary>
    /// 获取关节空间单位下的跃度上限。
    /// </summary>
    [JsonPropertyName("jerkLimit")]
    public double JerkLimit { get; }
}

/// <summary>
/// 描述在运动学计算或轨迹规划前必须应用的关节耦合规则。
/// </summary>
public sealed class JointCoupling
{
    /// <summary>
    /// 初始化一个已校验的关节耦合描述。
    /// </summary>
    public JointCoupling(string slaveJointName, string masterJointName, double multiplier, double offset)
    {
        if (string.IsNullOrWhiteSpace(slaveJointName))
        {
            throw new ArgumentException("Slave joint name is required.", nameof(slaveJointName));
        }

        if (string.IsNullOrWhiteSpace(masterJointName))
        {
            throw new ArgumentException("Master joint name is required.", nameof(masterJointName));
        }

        if (string.Equals(slaveJointName, masterJointName, StringComparison.Ordinal))
        {
            throw new ArgumentException("Slave and master joints must be different.");
        }

        SlaveJointName = slaveJointName;
        MasterJointName = masterJointName;
        Multiplier = multiplier;
        Offset = offset;
    }

    /// <summary>
    /// 获取从属（被驱动）关节名称。
    /// </summary>
    [JsonPropertyName("slaveJointName")]
    public string SlaveJointName { get; }

    /// <summary>
    /// 获取主导（驱动）关节名称。
    /// </summary>
    [JsonPropertyName("masterJointName")]
    public string MasterJointName { get; }

    /// <summary>
    /// 获取作用在主导关节角度上的耦合倍率。
    /// </summary>
    [JsonPropertyName("multiplier")]
    public double Multiplier { get; }

    /// <summary>
    /// 获取在耦合倍率之后叠加的偏移量。
    /// </summary>
    [JsonPropertyName("offset")]
    public double Offset { get; }
}
