using System;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Threading;
using ClumsyCore;
using ClumsyCore.Pilot;
using CommonUsage.Chassis;
using FundamentalLib;
using MultiWheelC.StateEstimation;
using MyParking.Shared;

namespace MultiWheelC
{
    /// <summary>
    /// 提供纵向开环辨识测试共用的舵轮准备、速度下发、采样和安全停车流程。
    /// </summary>
    public abstract class LongitudinalIdentificationTestBase
        : MovementTest
    {
        private const double MaximumTargetSpeedMetersPerSecond =
            0.70;
        private const double MinimumTargetSpeedMetersPerSecond =
            0.02;
        private const float MillimetersPerMeter = 1000f;

        private DriveTask _preparationTask;
        private MultiWheelChassis _chassis;
        private MultiWheelChassisAdapter _adapter;
        private WheelFeedbackVehicleStateProvider _stateProvider;
        private TrackingExperimentRecorder _recorder;
        private int _testRunning;
        private int _stopRequested;

        /// <summary>
        /// 获取或设置本次重复实验编号。
        /// </summary>
        public int TrialNumber = 1;

        /// <summary>
        /// 获取或设置速度阶跃前的静止记录时间，单位为s。
        /// </summary>
        public double BaselineSeconds = 1.0;

        /// <summary>
        /// 获取或设置目标速度保持时间，单位为s。
        /// </summary>
        public double CommandHoldSeconds = 5.0;

        /// <summary>
        /// 获取或设置停车后的继续记录时间，单位为s。
        /// </summary>
        public double PostStopSeconds = 2.0;

        /// <summary>
        /// 获取或设置速度命令循环周期，单位为ms。
        /// </summary>
        public int ControlIntervalMilliseconds = 50;

        /// <summary>
        /// 派生测试选择是否使用底盘DeAccPerSecond完成正常减速。
        /// </summary>
        protected abstract bool UseConfiguredDeceleration { get; }

        /// <summary>
        /// 获取用于CSV文件名区分停车方式的标识。
        /// </summary>
        protected abstract string StopModeName { get; }

        /// <summary>
        /// 读取目标速度，完成舵轮回正后执行纵向开环命令并保存CSV。
        /// </summary>
        public override void Test()
        {
            if (Interlocked.CompareExchange(
                    ref _testRunning,
                    1,
                    0) != 0)
            {
                Console.WriteLine(
                    "纵向辨识测试已经在运行，请先停止当前测试。");
                return;
            }

            Interlocked.Exchange(ref _stopRequested, 0);

            try
            {
                ValidateSettings();
                if (!TryReadTargetSpeed(
                        out var targetSpeedMetersPerSecond))
                {
                    return;
                }

                _chassis =
                    PilotDefinition.Chassis as MultiWheelChassis;
                if (_chassis == null)
                {
                    throw new InvalidOperationException(
                        "当前底盘不是MultiWheelChassis，无法执行纵向辨识。");
                }

                ValidateChassisAndTarget(
                    targetSpeedMetersPerSecond);

                Console.WriteLine(
                    "纵向辨识开始前请确认M层轮速诊断记录已经开启。");
                Hedingben.ToastText(
                    "请确认M层轮速诊断记录已开启。",
                    "LongitudinalIdentification");

                if (!MovementTestPreparation.AlignWheelsForward(
                        ref _preparationTask))
                {
                    throw new InvalidOperationException(
                        "四个舵轮未能稳定回正，纵向辨识已经取消。");
                }

                ThrowIfStopRequested();

                _adapter = new MultiWheelChassisAdapter(
                    _chassis,
                    PilotDefinition.Self.CarNum);
                _adapter.ResetToBodyFrame();
                _adapter.StopImmediately();

                _stateProvider =
                    ParkingVehicleStateProviderFactory.Create(
                        _chassis);
                if (!_stateProvider.TryGetState(
                        out var initialState))
                {
                    throw new InvalidOperationException(
                        "无法读取纵向辨识起点位姿：" +
                        _stateProvider.LastFailureReason);
                }

                _recorder = CreateRecorder(
                    targetSpeedMetersPerSecond,
                    initialState.PoseInWorld);
                _recorder.Start();

                Console.WriteLine(
                    $"纵向辨识参数：目标速度={targetSpeedMetersPerSecond:F3}m/s，" +
                    $"AccPerSecond={_chassis.AccPerSecond:F3}m/s²，" +
                    $"DeAccPerSecond={_chassis.DeAccPerSecond:F3}m/s²，" +
                    $"保持={CommandHoldSeconds:F1}s，停车方式={StopModeName}。");

                RunStationaryPhase(
                    BaselineSeconds,
                    "静止基线");
                RunTargetSpeedPhase(
                    targetSpeedMetersPerSecond);

                if (UseConfiguredDeceleration)
                {
                    RunConfiguredDecelerationPhase(
                        targetSpeedMetersPerSecond);
                }
                else
                {
                    _adapter.StopImmediately();
                    RunStationaryPhase(
                        PostStopSeconds,
                        "立即停车后记录");
                }

                Console.WriteLine(
                    "纵向辨识测试完成。请同时保存对应的M层轮速诊断CSV。");
                Hedingben.ToastText(
                    "纵向辨识完成，请停止并保存M层轮速诊断记录。",
                    "LongitudinalIdentification");
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("纵向辨识已由用户停止。");
            }
            catch (Exception exception)
            {
                Console.WriteLine(
                    "纵向辨识失败：" +
                    exception.Message);
                Hedingben.ToastText(
                    "纵向辨识失败：" +
                    exception.Message,
                    "LongitudinalIdentification");
            }
            finally
            {
                _adapter?.StopImmediately();
                _chassis?.PredefinedDriveStop();
                _recorder?.UpdateBodyCommand(0f, 0f, 0f);
                _recorder?.StopAndSave();

                _preparationTask?.Stop();
                _preparationTask = null;
                _recorder = null;
                _stateProvider = null;
                _adapter = null;
                _chassis = null;
                Interlocked.Exchange(ref _testRunning, 0);
            }
        }

        /// <summary>
        /// 请求停止当前辨识并立即清零底盘驱动速度。
        /// </summary>
        public override void TestStop()
        {
            Interlocked.Exchange(ref _stopRequested, 1);
            _preparationTask?.Stop();
            _adapter?.StopImmediately();
            _chassis?.PredefinedDriveStop();
        }

        /// <summary>
        /// 在固定周期内保持零命令并记录静止数据。
        /// </summary>
        private void RunStationaryPhase(
            double durationSeconds,
            string phaseName)
        {
            _recorder.UpdateBodyCommand(0f, 0f, 0f);
            RunPeriodicPhase(
                durationSeconds,
                phaseName,
                _ => UpdateWheelDiagnostics());
        }

        /// <summary>
        /// 通过标准车体速度适配器持续下发直线目标速度。
        /// </summary>
        private void RunTargetSpeedPhase(
            double targetSpeedMetersPerSecond)
        {
            _recorder.UpdateBodyCommand(
                (float)targetSpeedMetersPerSecond,
                0f,
                0f);

            RunPeriodicPhase(
                CommandHoldSeconds,
                "目标速度保持",
                interval =>
                {
                    var accepted = _adapter.SendBodyTwist(
                        new Twist2D(
                            targetSpeedMetersPerSecond,
                            0.0,
                            0.0),
                        interval);
                    if (!accepted)
                    {
                        throw new InvalidOperationException(
                            "底盘拒绝纵向速度命令：" +
                            _adapter.LastFailureReason);
                    }

                    UpdateWheelDiagnostics();
                });
        }

        /// <summary>
        /// 重复发送零速SendMotion，使底盘内部DeAccPerSecond斜坡真实参与减速。
        /// </summary>
        private void RunConfiguredDecelerationPhase(
            double targetSpeedMetersPerSecond)
        {
            _recorder.UpdateBodyCommand(0f, 0f, 0f);

            var rampDurationSeconds =
                Math.Abs(targetSpeedMetersPerSecond) /
                _chassis.DeAccPerSecond;
            var totalDurationSeconds =
                rampDurationSeconds +
                PostStopSeconds;

            RunPeriodicPhase(
                totalDurationSeconds,
                "配置化正常减速",
                interval =>
                {
                    // SendBodyTwist(Zero)会立即停车；辨识DeAccPerSecond时必须
                    // 直接保持SendMotion零目标，让底盘内部发送速度逐周期下降。
                    var accepted = _chassis.SendMotion(
                        0f,
                        0f,
                        0f,
                        interval);
                    if (!accepted)
                    {
                        throw new InvalidOperationException(
                            "底盘拒绝正常减速命令：" +
                            _chassis.LastMotionDecomposeFailureReason);
                    }

                    UpdateWheelDiagnostics();
                });
        }

        /// <summary>
        /// 按实际循环间隔运行一个阶段，并响应测试界面的停止请求。
        /// </summary>
        private void RunPeriodicPhase(
            double durationSeconds,
            string phaseName,
            Action<TimeSpan> cycleAction)
        {
            Console.WriteLine(
                $"纵向辨识阶段：{phaseName}，预计{durationSeconds:F2}s。");

            var periodSeconds =
                ControlIntervalMilliseconds / 1000.0;
            var phaseClock = Stopwatch.StartNew();
            var previousCycleSeconds = -periodSeconds;

            while (phaseClock.Elapsed.TotalSeconds <
                   durationSeconds)
            {
                ThrowIfStopRequested();

                var cycleStartSeconds =
                    phaseClock.Elapsed.TotalSeconds;
                var deltaTimeSeconds =
                    cycleStartSeconds -
                    previousCycleSeconds;
                previousCycleSeconds = cycleStartSeconds;

                cycleAction(
                    TimeSpan.FromSeconds(
                        deltaTimeSeconds));

                var elapsedMilliseconds =
                    (phaseClock.Elapsed.TotalSeconds -
                     cycleStartSeconds) * 1000.0;
                var remainingMilliseconds =
                    ControlIntervalMilliseconds -
                    elapsedMilliseconds;
                if (remainingMilliseconds > 1.0)
                {
                    Thread.Sleep(
                        (int)Math.Floor(
                            remainingMilliseconds));
                }
            }
        }

        /// <summary>
        /// 刷新轮组原始与滤波速度，供后台CSV记录器读取最新诊断值。
        /// </summary>
        private void UpdateWheelDiagnostics()
        {
            _stateProvider.TryGetWheelTwist(
                out _,
                out _);
        }

        /// <summary>
        /// 创建复用现有字段格式的纵向辨识CSV记录器。
        /// </summary>
        private TrackingExperimentRecorder CreateRecorder(
            double targetSpeedMetersPerSecond,
            Pose2D initialPoseInWorld)
        {
            var expectedTravelMeters =
                targetSpeedMetersPerSecond *
                CommandHoldSeconds;
            var referenceStart = new Vector2(
                (float)(
                    initialPoseInWorld.XMeters *
                    MillimetersPerMeter),
                (float)(
                    initialPoseInWorld.YMeters *
                    MillimetersPerMeter));
            var referenceEnd = new Vector2(
                referenceStart.X +
                (float)(
                    Math.Cos(initialPoseInWorld.YawRadians) *
                    expectedTravelMeters *
                    MillimetersPerMeter),
                referenceStart.Y +
                (float)(
                    Math.Sin(initialPoseInWorld.YawRadians) *
                    expectedTravelMeters *
                    MillimetersPerMeter));
            var speedMillimetersPerSecond =
                targetSpeedMetersPerSecond *
                MillimetersPerMeter;
            var trajectoryName =
                "LongitudinalStep_" +
                speedMillimetersPerSecond.ToString(
                    "+0;-0;0",
                    CultureInfo.InvariantCulture) +
                "mmps_" +
                StopModeName;

            return new TrackingExperimentRecorder(
                controllerName:
                    "OpenLoopLongitudinalIdentification",
                trajectoryName: trajectoryName,
                trialNumber: TrialNumber,
                referenceStart: referenceStart,
                referenceEnd: referenceEnd,
                referenceSpeed:
                    (float)targetSpeedMetersPerSecond,
                sampleIntervalMs:
                    ControlIntervalMilliseconds,
                referenceMotionFrameYawDegrees: 0f,
                referenceAccelerationMetersPerSecondSquared:
                    _chassis.AccPerSecond,
                referenceDecelerationMetersPerSecondSquared:
                    _chassis.DeAccPerSecond,
                diagnosticChassis: _chassis,
                diagnosticStateProvider: _stateProvider);
        }

        /// <summary>
        /// 从测试界面读取带方向的纵向目标速度，正值前进、负值倒车。
        /// </summary>
        private static bool TryReadTargetSpeed(
            out double targetSpeedMetersPerSecond)
        {
            targetSpeedMetersPerSecond = 0.0;
            var input = UI.GetInput(
                "输入纵向目标速度（m/s，正数前进、负数倒车，" +
                $"范围-{MaximumTargetSpeedMetersPerSecond:F1}～" +
                $"{MaximumTargetSpeedMetersPerSecond:F1}且不能为0）：");
            var parsed = double.TryParse(
                input,
                NumberStyles.Float,
                CultureInfo.CurrentCulture,
                out targetSpeedMetersPerSecond) ||
                double.TryParse(
                    input,
                    NumberStyles.Float,
                    CultureInfo.InvariantCulture,
                    out targetSpeedMetersPerSecond);

            if (!parsed ||
                double.IsNaN(targetSpeedMetersPerSecond) ||
                double.IsInfinity(targetSpeedMetersPerSecond) ||
                Math.Abs(targetSpeedMetersPerSecond) <
                    MinimumTargetSpeedMetersPerSecond ||
                Math.Abs(targetSpeedMetersPerSecond) >
                    MaximumTargetSpeedMetersPerSecond)
            {
                Console.WriteLine(
                    "目标速度必须是绝对值位于" +
                    $"{MinimumTargetSpeedMetersPerSecond:F2}～" +
                    $"{MaximumTargetSpeedMetersPerSecond:F2}m/s之间的有限数值。");
                return false;
            }

            return true;
        }

        /// <summary>
        /// 验证实验时间、循环周期和编号配置。
        /// </summary>
        private void ValidateSettings()
        {
            NumericGuard.EnsureFiniteNonNegative(
                BaselineSeconds,
                nameof(BaselineSeconds));
            NumericGuard.EnsureFinitePositive(
                CommandHoldSeconds,
                nameof(CommandHoldSeconds));
            NumericGuard.EnsureFiniteNonNegative(
                PostStopSeconds,
                nameof(PostStopSeconds));

            if (CommandHoldSeconds > 30.0 ||
                BaselineSeconds > 10.0 ||
                PostStopSeconds > 10.0)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(CommandHoldSeconds),
                    "辨识阶段时间超出测试允许范围。");
            }

            if (ControlIntervalMilliseconds < 20 ||
                ControlIntervalMilliseconds > 200)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(ControlIntervalMilliseconds),
                    "纵向辨识命令周期必须在20～200ms之间。");
            }

            if (TrialNumber <= 0)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(TrialNumber),
                    "测试编号必须大于零。");
            }
        }

        /// <summary>
        /// 验证底盘运行时加载的速度、加速度和减速度配置。
        /// </summary>
        private void ValidateChassisAndTarget(
            double targetSpeedMetersPerSecond)
        {
            NumericGuard.EnsureFinitePositive(
                _chassis.MaxSpeed,
                nameof(_chassis.MaxSpeed));
            NumericGuard.EnsureFinitePositive(
                _chassis.AccPerSecond,
                nameof(_chassis.AccPerSecond));
            NumericGuard.EnsureFinitePositive(
                _chassis.DeAccPerSecond,
                nameof(_chassis.DeAccPerSecond));

            if (Math.Abs(targetSpeedMetersPerSecond) >
                _chassis.MaxSpeed)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(targetSpeedMetersPerSecond),
                    "目标速度超过底盘当前MaxSpeed配置。");
            }
        }

        /// <summary>
        /// 在收到停止请求时中断当前阶段并转入finally安全停车。
        /// </summary>
        private void ThrowIfStopRequested()
        {
            if (Volatile.Read(ref _stopRequested) != 0)
            {
                throw new OperationCanceledException();
            }
        }
    }

    /// <summary>
    /// 测量速度阶跃、加速与稳态，并在保持结束后立即清零驱动速度。
    /// </summary>
    [MovementTest(name = "纵向辨识：速度阶跃与立即停车")]
    public sealed class LongitudinalStepImmediateStopTest
        : LongitudinalIdentificationTestBase
    {
        protected override bool UseConfiguredDeceleration =>
            false;

        protected override string StopModeName =>
            "ImmediateStop";
    }

    /// <summary>
    /// 测量速度阶跃、稳态以及由底盘DeAccPerSecond形成的正常减速过程。
    /// </summary>
    [MovementTest(name = "纵向辨识：速度阶跃与正常减速")]
    public sealed class LongitudinalStepConfiguredDecelerationTest
        : LongitudinalIdentificationTestBase
    {
        protected override bool UseConfiguredDeceleration =>
            true;

        protected override string StopModeName =>
            "ConfiguredDeceleration";
    }
}
