using System;
using System.Diagnostics;
using MultiWheelC.Control.Abstractions;
using MultiWheelC.Control.Allocation;
using MultiWheelC.StateEstimation;
using MultiWheelC.Trajectory;
using MyParking.Shared;

namespace MultiWheelC.Control.Execution
{
    // 单周期轨迹控制结果。
    public enum ParkingControlCycleResult
    {
        Inactive = 0,
        CommandSent = 1,
        Completed = 2,
        StateUnavailable = 3,
        Faulted = 4
    }

    // 单周期各阶段耗时及定位帧更新状态，用于区分控制计算与底层通信延迟。
    public readonly struct ParkingControlCycleTiming
    {
        public ParkingControlCycleTiming(
            long cycleIndex,
            double cycleIntervalMilliseconds,
            double stateReadMilliseconds,
            double projectionMilliseconds,
            double controllerComputeMilliseconds,
            double commandSendMilliseconds,
            double totalCycleMilliseconds,
            bool hasStateTimestamp,
            double stateTimestampSeconds,
            bool stateTimestampChanged,
            ParkingControlCycleResult result)
        {
            CycleIndex = cycleIndex;
            CycleIntervalMilliseconds =
                cycleIntervalMilliseconds;
            StateReadMilliseconds = stateReadMilliseconds;
            ProjectionMilliseconds = projectionMilliseconds;
            ControllerComputeMilliseconds =
                controllerComputeMilliseconds;
            CommandSendMilliseconds = commandSendMilliseconds;
            TotalCycleMilliseconds = totalCycleMilliseconds;
            HasStateTimestamp = hasStateTimestamp;
            StateTimestampSeconds = stateTimestampSeconds;
            StateTimestampChanged = stateTimestampChanged;
            Result = result;
        }

        public long CycleIndex { get; }
        public double CycleIntervalMilliseconds { get; }
        public double StateReadMilliseconds { get; }
        public double ProjectionMilliseconds { get; }
        public double ControllerComputeMilliseconds { get; }
        public double CommandSendMilliseconds { get; }
        public double TotalCycleMilliseconds { get; }
        public bool HasStateTimestamp { get; }
        public double StateTimestampSeconds { get; }
        public bool StateTimestampChanged { get; }
        public ParkingControlCycleResult Result { get; }
    }

    // 负责单车状态读取、公共轨迹计算和真实底盘命令发送。
    public sealed class ParkingGeometricController
    {
        private readonly IVehicleStateProvider _stateProvider;
        private readonly GcpCommandExecutor _commandExecutor;
        private readonly PathTrackingCore _trackingCore;

        private long _cycleIndex;
        private bool _hasPreviousStateTimestamp;
        private double _previousStateTimestampSeconds;

        // 创建具有终点判定、轨迹偏离保护和曲率前馈预瞄的单车轨迹控制器。
        public ParkingGeometricController(
            IVehicleStateProvider stateProvider,
            ILateralController lateralController,
            ILongitudinalController longitudinalController,
            GcpCommandAllocator gcpAllocator,
            GcpCommandExecutor commandExecutor,
            double finishDistanceMeters = 0.04,
            double finishSpeedMetersPerSecond = 0.02,
            double finishHeadingToleranceRadians =
                3.0 * Math.PI / 180.0,
            double maximumDistanceToTrajectoryMeters = 0.30,
            double terminalBrakingPreviewMeters = 0.02,
            double terminalApproachDistanceMeters = 0.10,
            double terminalApproachGainPerSecond = 0.8,
            double maximumTerminalApproachSpeedMetersPerSecond = 0.05,
            double curvaturePreviewSeconds = 0.20,
            double maximumCurvaturePreviewMeters = 0.12,
            double motionDirectionInBodyRadians = 0.0)
        {
            _stateProvider = stateProvider ??
                throw new ArgumentNullException(
                    nameof(stateProvider));
            _commandExecutor = commandExecutor ??
                throw new ArgumentNullException(
                    nameof(commandExecutor));
            _trackingCore = new PathTrackingCore(
                lateralController,
                longitudinalController,
                gcpAllocator,
                finishDistanceMeters,
                finishSpeedMetersPerSecond,
                finishHeadingToleranceRadians,
                maximumDistanceToTrajectoryMeters,
                terminalBrakingPreviewMeters,
                terminalApproachDistanceMeters,
                terminalApproachGainPerSecond,
                maximumTerminalApproachSpeedMetersPerSecond,
                curvaturePreviewSeconds,
                maximumCurvaturePreviewMeters,
                motionDirectionInBodyRadians);
        }

        // 以下控制参数由公共轨迹核心统一持有。
        public double FinishDistanceMeters =>
            _trackingCore.FinishDistanceMeters;

        public double FinishSpeedMetersPerSecond =>
            _trackingCore.FinishSpeedMetersPerSecond;

        public double FinishHeadingToleranceRadians =>
            _trackingCore.FinishHeadingToleranceRadians;

        public double MaximumDistanceToTrajectoryMeters =>
            _trackingCore.MaximumDistanceToTrajectoryMeters;

        public double TerminalBrakingPreviewMeters =>
            _trackingCore.TerminalBrakingPreviewMeters;

        public double TerminalApproachDistanceMeters =>
            _trackingCore.TerminalApproachDistanceMeters;

        public double TerminalApproachGainPerSecond =>
            _trackingCore.TerminalApproachGainPerSecond;

        public double MaximumTerminalApproachSpeedMetersPerSecond =>
            _trackingCore.MaximumTerminalApproachSpeedMetersPerSecond;

        public double CurvaturePreviewSeconds =>
            _trackingCore.CurvaturePreviewSeconds;

        public double MaximumCurvaturePreviewMeters =>
            _trackingCore.MaximumCurvaturePreviewMeters;

        public bool IsActive => _trackingCore.IsActive;

        public bool IsCompleted => _trackingCore.IsCompleted;

        public string LastFailureReason =>
            _trackingCore.LastFailureReason;

        public Exception LastException =>
            _trackingCore.LastException;

        // 最近一次有效车辆状态仍由单车外层保存。
        public VehicleState? LastVehicleState { get; private set; }

        public TrajectoryProjection? LastProjection =>
            _trackingCore.LastProjection;

        public GcpMotionCommand? LastRequestedCommand =>
            _trackingCore.LastRequestedCommand;

        // 经过GCP角速度限制后实际发送给底盘的最近一次命令。
        public GcpMotionCommand? LastCommand { get; private set; }

        public double? LastControlReferenceSpeedMetersPerSecond =>
            _trackingCore.LastControlReferenceSpeedMetersPerSecond;

        public double? LastCurvaturePreviewDistanceMeters =>
            _trackingCore.LastCurvaturePreviewDistanceMeters;

        public double? LastFeedforwardCurvaturePerMeter =>
            _trackingCore.LastFeedforwardCurvaturePerMeter;

        public ParkingControlCycleTiming? LastCycleTiming { get; private set; }

        // 先停车，再从轨迹起点重置公共控制核心和单车执行诊断。
        public void Start(Trajectory2D trajectory)
        {
            _commandExecutor.Stop();
            _trackingCore.Start(trajectory);
            ClearExecutionDiagnostics();
        }

        // 读取车辆状态、调用公共核心并发送一次真实底盘命令。
        public ParkingControlCycleResult ExecuteCycle(
            double deltaTimeSeconds)
        {
            NumericGuard.EnsureFinitePositive(
                deltaTimeSeconds,
                nameof(deltaTimeSeconds));

            if (!_trackingCore.IsActive)
            {
                return ParkingControlCycleResult.Inactive;
            }

            var cycleIndex = ++_cycleIndex;
            var cycleStartTimestamp =
                Stopwatch.GetTimestamp();
            var stateReadMilliseconds = 0.0;
            var projectionMilliseconds = 0.0;
            var controllerComputeMilliseconds = 0.0;
            var commandSendMilliseconds = 0.0;
            var hasStateTimestamp = false;
            var stateTimestampSeconds = 0.0;
            var stateTimestampChanged = false;
            var cycleResult =
                ParkingControlCycleResult.Faulted;

            try
            {
                var stateReadStartTimestamp =
                    Stopwatch.GetTimestamp();
                bool stateAvailable;
                VehicleState vehicleState;
                try
                {
                    stateAvailable =
                        _stateProvider.TryGetState(
                            out vehicleState);
                }
                finally
                {
                    stateReadMilliseconds =
                        GetElapsedMilliseconds(
                            stateReadStartTimestamp);
                }

                if (!stateAvailable)
                {
                    var stopStartTimestamp =
                        Stopwatch.GetTimestamp();
                    try
                    {
                        StopForUnavailableState();
                    }
                    finally
                    {
                        commandSendMilliseconds =
                            GetElapsedMilliseconds(
                                stopStartTimestamp);
                    }

                    cycleResult = ParkingControlCycleResult
                        .StateUnavailable;
                    return cycleResult;
                }

                LastVehicleState = vehicleState;
                hasStateTimestamp = true;
                stateTimestampSeconds =
                    vehicleState.SampleTimestampSeconds;
                stateTimestampChanged =
                    !_hasPreviousStateTimestamp ||
                    stateTimestampSeconds !=
                    _previousStateTimestampSeconds;
                _previousStateTimestampSeconds =
                    stateTimestampSeconds;
                _hasPreviousStateTimestamp = true;

                var output = _trackingCore.Compute(
                    vehicleState.PoseInWorld,
                    vehicleState.TwistInBody,
                    vehicleState.HasValidVelocityEstimate,
                    deltaTimeSeconds);
                projectionMilliseconds =
                    output.ProjectionMilliseconds;
                controllerComputeMilliseconds =
                    output.ControllerComputeMilliseconds;

                if (output.Result ==
                    PathTrackingCycleResult.Completed)
                {
                    commandSendMilliseconds =
                        StopForCompletedTrajectory();
                    cycleResult =
                        ParkingControlCycleResult.Completed;
                    return cycleResult;
                }

                if (output.Result ==
                    PathTrackingCycleResult.Faulted)
                {
                    commandSendMilliseconds =
                        StopForTrackingFault();
                    cycleResult =
                        ParkingControlCycleResult.Faulted;
                    return cycleResult;
                }

                if (output.Result !=
                        PathTrackingCycleResult.CommandGenerated ||
                    !output.Command.HasValue)
                {
                    cycleResult =
                        ParkingControlCycleResult.Inactive;
                    return cycleResult;
                }

                var commandSendStartTimestamp =
                    Stopwatch.GetTimestamp();
                bool commandSucceeded;
                try
                {
                    commandSucceeded =
                        _commandExecutor.Execute(
                            output.Command.Value,
                            deltaTimeSeconds);
                }
                finally
                {
                    commandSendMilliseconds =
                        GetElapsedMilliseconds(
                            commandSendStartTimestamp);
                }

                if (!commandSucceeded)
                {
                    cycleResult = EnterFault(
                        string.IsNullOrWhiteSpace(
                            _commandExecutor.LastFailureReason)
                            ? "GCP底盘命令执行失败。"
                            : _commandExecutor.LastFailureReason);
                    return cycleResult;
                }

                LastCommand =
                    _commandExecutor.LastSentCommand;
                cycleResult =
                    ParkingControlCycleResult.CommandSent;
                return cycleResult;
            }
            catch (Exception exception)
            {
                cycleResult = EnterFault(
                    "停车机器人轨迹控制周期异常：" +
                    exception.Message,
                    exception);
                return cycleResult;
            }
            finally
            {
                LastCycleTiming =
                    new ParkingControlCycleTiming(
                        cycleIndex,
                        deltaTimeSeconds * 1000.0,
                        stateReadMilliseconds,
                        projectionMilliseconds,
                        controllerComputeMilliseconds,
                        commandSendMilliseconds,
                        GetElapsedMilliseconds(
                            cycleStartTimestamp),
                        hasStateTimestamp,
                        stateTimestampSeconds,
                        stateTimestampChanged,
                        cycleResult);
            }
        }

        // 主动取消当前轨迹、立即停车并清除全部控制状态。
        public void Cancel()
        {
            _commandExecutor.Stop();
            _trackingCore.Cancel();
            ClearExecutionDiagnostics();
        }

        // 状态不可用时停车并重置反馈历史，同时保留轨迹等待恢复。
        private void StopForUnavailableState()
        {
            _commandExecutor.Stop();
            _trackingCore.PauseForUnavailableState(
                "当前无法获得有效车辆状态，底盘已停车并等待定位恢复。");
            LastCommand = null;
        }

        // 公共核心完成轨迹后发送停车，并保留零命令供实验记录。
        private double StopForCompletedTrajectory()
        {
            var startTimestamp = Stopwatch.GetTimestamp();
            _commandExecutor.Stop();
            LastCommand = new GcpMotionCommand(
                0.0,
                0.0,
                0.0);
            return GetElapsedMilliseconds(startTimestamp);
        }

        // 公共核心故障后只负责真实底盘停车，不覆盖核心保存的失败原因。
        private double StopForTrackingFault()
        {
            var startTimestamp = Stopwatch.GetTimestamp();
            _commandExecutor.Stop();
            LastCommand = null;
            return GetElapsedMilliseconds(startTimestamp);
        }

        // 将底盘执行或外层异常同步到公共核心，并立即停车。
        private ParkingControlCycleResult EnterFault(
            string reason,
            Exception exception = null)
        {
            _commandExecutor.Stop();
            _trackingCore.Fail(reason, exception);
            LastCommand = null;
            return ParkingControlCycleResult.Faulted;
        }

        // 清除只属于单车状态读取、发送和周期计时的诊断。
        private void ClearExecutionDiagnostics()
        {
            _cycleIndex = 0;
            _hasPreviousStateTimestamp = false;
            _previousStateTimestampSeconds = 0.0;
            LastVehicleState = null;
            LastCommand = null;
            LastCycleTiming = null;
        }

        private static double GetElapsedMilliseconds(
            long startTimestamp)
        {
            return (Stopwatch.GetTimestamp() - startTimestamp) *
                   1000.0 /
                   Stopwatch.Frequency;
        }
    }
}
