using MainShell.Hardware;
using MainShell.Parameter;
using MainShell.Common;
using MwFramework.Device;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MainShell.Motion.Safety
{
    public sealed class MotionSafetyContext
    {
        public MotionSafetyContext(MotionRequestKind requestKind, IReadOnlyCollection<IAxis> axes, IReadOnlyCollection<MotionMoveRequest> requests, string source, int? timeoutMilliseconds)
        {
            RequestKind = requestKind;
            Axes = axes ?? Array.Empty<IAxis>();
            Requests = requests ?? Array.Empty<MotionMoveRequest>();
            Source = source;
            TimeoutMilliseconds = timeoutMilliseconds;
        }

        public MotionRequestKind RequestKind { get; }
        public IReadOnlyCollection<IAxis> Axes { get; }
        public IReadOnlyCollection<MotionMoveRequest> Requests { get; }
        public string Source { get; }
        public int? TimeoutMilliseconds { get; }
        public bool IsBatch => Requests.Count > 1;
    }

    public sealed class MotionSafetyCheckResult
    {
        public MotionSafetyCheckResult(bool isPassed, string ruleName, string message, string severity = "Block", int? alarmId = null, string recoveryHint = null, IEnumerable<string> affectedAxes = null, MessageKey messageKey = MessageKey.None, object[] messageArgs = null)
        {
            IsPassed = isPassed;
            RuleName = ruleName;
            Message = message;
            Severity = severity;
            AlarmId = alarmId;
            RecoveryHint = recoveryHint;
            AffectedAxes = (affectedAxes ?? Enumerable.Empty<string>()).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
            MessageKey = messageKey;
            MessageArgs = messageArgs ?? Array.Empty<object>();
        }

        public bool IsPassed { get; }
        public string RuleName { get; }
        public string Message { get; }
        public string Severity { get; }
        public int? AlarmId { get; }
        public string RecoveryHint { get; }
        public IReadOnlyCollection<string> AffectedAxes { get; }
        public MessageKey MessageKey { get; }
        public object[] MessageArgs { get; }

        public static MotionSafetyCheckResult Passed(string ruleName)
        {
            return new MotionSafetyCheckResult(true, ruleName, null);
        }

        public static MotionSafetyCheckResult Blocked(string ruleName, string message, int? alarmId = null, string recoveryHint = null, IEnumerable<string> affectedAxes = null, MessageKey messageKey = MessageKey.None, params object[] messageArgs)
        {
            return new MotionSafetyCheckResult(false, ruleName, message, "Block", alarmId, recoveryHint, affectedAxes, messageKey, messageArgs);
        }
    }

    public interface IMotionSafetyCheck
    {
        MotionSafetyCheckResult Check(MotionSafetyContext context);
    }

    public sealed class StagePlatformSafetyOptions
    {
        public StagePlatformSafetyOptions(double maxPlaneSpread, double maxTravelPerStep)
        {
            MaxPlaneSpread = maxPlaneSpread;
            MaxTravelPerStep = maxTravelPerStep;
        }

        public double MaxPlaneSpread { get; }
        public double MaxTravelPerStep { get; }
    }

    internal sealed class MotionRequestDuplicateAxisCheck : IMotionSafetyCheck
    {
        public MotionSafetyCheckResult Check(MotionSafetyContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var duplicateAxisNames = context.Requests
                .Select(x => x.AxisName)
                .Where(x => !string.IsNullOrWhiteSpace(x))
                .GroupBy(x => x, StringComparer.OrdinalIgnoreCase)
                .Where(x => x.Count() > 1)
                .Select(x => x.Key)
                .ToArray();

            if (duplicateAxisNames.Length == 0)
            {
                return MotionSafetyCheckResult.Passed(nameof(MotionRequestDuplicateAxisCheck));
            }

            return MotionSafetyCheckResult.Blocked(
                nameof(MotionRequestDuplicateAxisCheck),
                string.Format("Duplicate axis requests detected: {0}.", string.Join(", ", duplicateAxisNames)),
                recoveryHint: "请检查批量运动请求，确保同一批次中每根轴只出现一次。",
                affectedAxes: duplicateAxisNames,
                messageKey: MessageKey.ProcessFailedWithReason,
                messageArgs: new object[] { string.Format("重复轴请求: {0}", string.Join(", ", duplicateAxisNames)) });
        }
    }

    internal sealed class MotionBatchSameSourceCheck : IMotionSafetyCheck
    {
        public MotionSafetyCheckResult Check(MotionSafetyContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var nonEmptySources = context.Requests
                .Select(x => x.Source)
                .Where(x => !string.IsNullOrWhiteSpace(x))
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .ToArray();

            if (nonEmptySources.Length <= 1)
            {
                return MotionSafetyCheckResult.Passed(nameof(MotionBatchSameSourceCheck));
            }

            return MotionSafetyCheckResult.Blocked(
                nameof(MotionBatchSameSourceCheck),
                string.Format("Batch requests must share the same source. Sources: {0}.", string.Join(", ", nonEmptySources)),
                recoveryHint: "请确保一个批量运动由同一个机构动作统一发起。",
                messageKey: MessageKey.ProcessFailedWithReason,
                messageArgs: new object[] { string.Format("批量动作来源不一致: {0}", string.Join(", ", nonEmptySources)) });
        }
    }

    internal sealed class EmergencyStopReleasedCheck : IMotionSafetyCheck
    {
        private readonly MotionSafetyStateProvider _stateProvider;

        public EmergencyStopReleasedCheck(MotionSafetyStateProvider stateProvider)
        {
            _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider));
        }

        public MotionSafetyCheckResult Check(MotionSafetyContext context)
        {
            if (_stateProvider.IsEmergencyStopReleased())
            {
                return MotionSafetyCheckResult.Passed(nameof(EmergencyStopReleasedCheck));
            }

            return MotionSafetyCheckResult.Blocked(
                nameof(EmergencyStopReleasedCheck),
                "Emergency stop is active or safety circuit is not ready.",
                recoveryHint: "请释放急停并检查安全回路状态后再执行运动。",
                affectedAxes: context != null ? context.Axes.Select(x => x != null ? x.Name : null) : null,
                messageKey: MessageKey.ProcessFailedWithReason,
                messageArgs: new object[] { "急停未释放或安全回路未就绪" });
        }
    }

    internal sealed class SafetyDoorClosedCheck : IMotionSafetyCheck
    {
        private readonly MotionSafetyStateProvider _stateProvider;

        public SafetyDoorClosedCheck(MotionSafetyStateProvider stateProvider)
        {
            _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider));
        }

        public MotionSafetyCheckResult Check(MotionSafetyContext context)
        {
            if (_stateProvider.CurrentMachineMode != MachineMode.Auto)
            {
                return MotionSafetyCheckResult.Passed(nameof(SafetyDoorClosedCheck));
            }
            if (_stateProvider.AreSafetyDoorsClosed())
            {
                return MotionSafetyCheckResult.Passed(nameof(SafetyDoorClosedCheck));
            }

            return MotionSafetyCheckResult.Blocked(
                nameof(SafetyDoorClosedCheck),
                "Safety door is open and shield is not active.",
                recoveryHint: "请关闭安全门，或确认门屏蔽状态是否符合当前操作模式。",
                affectedAxes: context != null ? context.Axes.Select(x => x != null ? x.Name : null) : null,
                messageKey: MessageKey.ProcessFailedWithReason,
                messageArgs: new object[] { "安全门未关闭" });
        }
    }

    internal sealed class AutoFlowModeCheck : IMotionSafetyCheck
    {
        private readonly MotionSafetyStateProvider _stateProvider;

        public AutoFlowModeCheck(MotionSafetyStateProvider stateProvider)
        {
            _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider));
        }

        public MotionSafetyCheckResult Check(MotionSafetyContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!_stateProvider.IsAutoWorkflowSource(context.Source))
            {
                return MotionSafetyCheckResult.Passed(nameof(AutoFlowModeCheck));
            }

            if (_stateProvider.CurrentMachineMode == MainShell.Common.MachineMode.Auto)
            {
                return MotionSafetyCheckResult.Passed(nameof(AutoFlowModeCheck));
            }

            return MotionSafetyCheckResult.Blocked(
                nameof(AutoFlowModeCheck),
                string.Format("Motion source '{0}' requires Auto mode. Current mode: {1}.", context.Source, _stateProvider.CurrentMachineMode),
                recoveryHint: "请切换到自动模式，或改由手动安全入口执行该动作。",
                affectedAxes: context.Axes.Select(x => x != null ? x.Name : null),
                messageKey: MessageKey.ProcessFailedWithReason,
                messageArgs: new object[] { string.Format("动作来源 {0} 需要自动模式", context.Source) });
        }
    }

    internal sealed class StageVacuumReadyCheck : IMotionSafetyCheck
    {
        private readonly MotionSafetyStateProvider _stateProvider;
        private const string StagePlaneTag = "StagePlaneMove";

        public StageVacuumReadyCheck(MotionSafetyStateProvider stateProvider)
        {
            _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider));
        }

        public MotionSafetyCheckResult Check(MotionSafetyContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!context.Requests.Any(ContainsStagePlaneTag))
            {
                return MotionSafetyCheckResult.Passed(nameof(StageVacuumReadyCheck));
            }

            if (_stateProvider.AreStageVacuumsReady())
            {
                return MotionSafetyCheckResult.Passed(nameof(StageVacuumReadyCheck));
            }

            return MotionSafetyCheckResult.Blocked(
                nameof(StageVacuumReadyCheck),
                "Stage vacuum signals are not ready for plane movement.",
                recoveryHint: "请检查平台真空吸附状态，确认基板已吸稳后再移动平台。",
                affectedAxes: new[] { AxisName.Axis_Stage_Z7, AxisName.Axis_Stage_Z8, AxisName.Axis_Stage_Z9 },
                messageKey: MessageKey.ProcessFailedWithReason,
                messageArgs: new object[] { "平台真空未就绪" });
        }

        private static bool ContainsStagePlaneTag(MotionMoveRequest request)
        {
            return request != null && request.Tags != null && request.Tags.Contains(StagePlaneTag, StringComparer.OrdinalIgnoreCase);
        }
    }

    internal sealed class BondHeadSafePositionCheck : IMotionSafetyCheck
    {
        private readonly MotionSafetyStateProvider _stateProvider;
        private const string StagePlaneTag = "StagePlaneMove";

        public BondHeadSafePositionCheck(MotionSafetyStateProvider stateProvider)
        {
            _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider));
        }

        public MotionSafetyCheckResult Check(MotionSafetyContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!context.Requests.Any(ContainsStagePlaneTag))
            {
                return MotionSafetyCheckResult.Passed(nameof(BondHeadSafePositionCheck));
            }

            var unsafeSignals = _stateProvider.GetUnsafeBondHeadSignals();
            if (unsafeSignals == null || unsafeSignals.Count == 0)
            {
                return MotionSafetyCheckResult.Passed(nameof(BondHeadSafePositionCheck));
            }

            return MotionSafetyCheckResult.Blocked(
                nameof(BondHeadSafePositionCheck),
                string.Format("Bond head safe position signals are not ready: {0}.", string.Join(", ", unsafeSignals)),
                recoveryHint: "请确认 BondHead 已回到安全位，再执行平台联动。",
                affectedAxes: new[] { AxisName.Axis_Stage_Z7, AxisName.Axis_Stage_Z8, AxisName.Axis_Stage_Z9 },
                messageKey: MessageKey.ProcessFailedWithReason,
                messageArgs: new object[] { string.Format("BondHead 安全位信号未就绪: {0}", string.Join(", ", unsafeSignals)) });
        }

        private static bool ContainsStagePlaneTag(MotionMoveRequest request)
        {
            return request != null && request.Tags != null && request.Tags.Contains(StagePlaneTag, StringComparer.OrdinalIgnoreCase);
        }
    }

    internal sealed class StagePlatformPlaneMoveSafetyCheck : IMotionSafetyCheck
    {
        private const string StagePlaneTag = "StagePlaneMove";
        private readonly HardwareManager _hardware;
        private readonly StagePlatformSafetyOptions _options;

        public StagePlatformPlaneMoveSafetyCheck(HardwareManager hardware, StagePlatformSafetyOptions options)
        {
            _hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
            _options = options ?? throw new ArgumentNullException(nameof(options));
        }

        public MotionSafetyCheckResult Check(MotionSafetyContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var requests = context.Requests.Where(ContainsStagePlaneTag).ToArray();
            if (requests.Length == 0)
            {
                return MotionSafetyCheckResult.Passed(nameof(StagePlatformPlaneMoveSafetyCheck));
            }

            if (requests.Length != 3)
            {
                return MotionSafetyCheckResult.Blocked(
                    nameof(StagePlatformPlaneMoveSafetyCheck),
                    "Stage plane move requires exactly 3 axis requests.",
                    recoveryHint: "请确认平台联动请求完整包含 Z7、Z8、Z9 三根轴。",
                    affectedAxes: new[] { AxisName.Axis_Stage_Z7, AxisName.Axis_Stage_Z8, AxisName.Axis_Stage_Z9 },
                    messageKey: MessageKey.ProcessFailedWithReason,
                    messageArgs: new object[] { "平台联动请求数量不正确" });
            }

            var expectedAxes = new[]
            {
                AxisName.Axis_Stage_Z7,
                AxisName.Axis_Stage_Z8,
                AxisName.Axis_Stage_Z9
            };

            var requestMap = requests.ToDictionary(x => x.AxisName, x => x, StringComparer.OrdinalIgnoreCase);
            if (expectedAxes.Any(x => !requestMap.ContainsKey(x)))
            {
                return MotionSafetyCheckResult.Blocked(
                    nameof(StagePlatformPlaneMoveSafetyCheck),
                    "Stage plane move must include Stage-Z7, Stage-Z8 and Stage-Z9.",
                    recoveryHint: "请检查平台三轴联动动作构建逻辑。",
                    affectedAxes: expectedAxes,
                    messageKey: MessageKey.ProcessFailedWithReason,
                    messageArgs: new object[] { "平台联动缺少必要轴" });
            }

            var currentPositions = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase)
            {
                { AxisName.Axis_Stage_Z7, GetAxisPosition(_hardware.Axis_Stage_Z7, nameof(_hardware.Axis_Stage_Z7)) },
                { AxisName.Axis_Stage_Z8, GetAxisPosition(_hardware.Axis_Stage_Z8, nameof(_hardware.Axis_Stage_Z8)) },
                { AxisName.Axis_Stage_Z9, GetAxisPosition(_hardware.Axis_Stage_Z9, nameof(_hardware.Axis_Stage_Z9)) }
            };

            var targetPositions = expectedAxes.Select(x => requestMap[x].TargetPosition).ToArray();
            var currentSpread = currentPositions.Values.Max() - currentPositions.Values.Min();
            var targetSpread = targetPositions.Max() - targetPositions.Min();
            var maxTravelDelta = expectedAxes.Max(x => Math.Abs(requestMap[x].TargetPosition - currentPositions[x]));

            if (targetSpread > _options.MaxPlaneSpread)
            {
                return MotionSafetyCheckResult.Blocked(
                    nameof(StagePlatformPlaneMoveSafetyCheck),
                    string.Format("Stage plane target spread {0:F3} exceeds limit {1:F3}.", targetSpread, _options.MaxPlaneSpread),
                    recoveryHint: "请缩小三轴目标高度差，或先执行中间整平动作。",
                    affectedAxes: expectedAxes,
                    messageKey: MessageKey.ProcessFailedWithReason,
                    messageArgs: new object[] { string.Format("平台目标姿态差超限: {0:F3}", targetSpread) });
            }

            if (currentSpread > _options.MaxPlaneSpread)
            {
                return MotionSafetyCheckResult.Blocked(
                    nameof(StagePlatformPlaneMoveSafetyCheck),
                    string.Format("Stage plane current spread {0:F3} exceeds limit {1:F3}.", currentSpread, _options.MaxPlaneSpread),
                    recoveryHint: "请先执行平台整平或回安全平面动作。",
                    affectedAxes: expectedAxes,
                    messageKey: MessageKey.ProcessFailedWithReason,
                    messageArgs: new object[] { string.Format("平台当前姿态差超限: {0:F3}", currentSpread) });
            }

            if (maxTravelDelta > _options.MaxTravelPerStep)
            {
                return MotionSafetyCheckResult.Blocked(
                    nameof(StagePlatformPlaneMoveSafetyCheck),
                    string.Format("Stage plane travel delta {0:F3} exceeds single-step limit {1:F3}. Consider a safe intermediate move.", maxTravelDelta, _options.MaxTravelPerStep),
                    recoveryHint: "请改为分步走位，先到安全中转位再到目标位。",
                    affectedAxes: expectedAxes,
                    messageKey: MessageKey.ProcessFailedWithReason,
                    messageArgs: new object[] { string.Format("平台单步移动量超限: {0:F3}", maxTravelDelta) });
            }

            return MotionSafetyCheckResult.Passed(nameof(StagePlatformPlaneMoveSafetyCheck));
        }

        private static bool ContainsStagePlaneTag(MotionMoveRequest request)
        {
            return request != null && request.Tags != null && request.Tags.Contains(StagePlaneTag, StringComparer.OrdinalIgnoreCase);
        }

        private static double GetAxisPosition(IAxis axis, string axisPropertyName)
        {
            if (axis == null)
            {
                throw new InvalidOperationException(string.Format("Axis '{0}' is not available.", axisPropertyName));
            }

            return axis.State != null ? axis.State.ActualPos : axis.GetPositionImmediate();
        }
    }
}
