using System;
using System.Collections.Generic;
using MyParking.Shared;

namespace MultiWheelC.Fleet
{
    // 主车已经接收并接受的一辆成员车安全状态。
    public readonly struct FleetMemberSafetyStatus
    {
        public FleetMemberSafetyStatus(
            int vehicleId,
            long planId,
            bool isStateAvailable,
            bool isFaulted,
            int failureCode,
            double lastAcceptedReportTimeSeconds)
        {
            if (vehicleId <= 0)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(vehicleId),
                    "成员车编号必须大于零。");
            }

            if (planId < 0)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(planId),
                    "任务编号不能为负数。");
            }

            NumericGuard.EnsureFiniteNonNegative(
                lastAcceptedReportTimeSeconds,
                nameof(lastAcceptedReportTimeSeconds));

            VehicleId = vehicleId;
            PlanId = planId;
            IsStateAvailable = isStateAvailable;
            IsFaulted = isFaulted;
            FailureCode = failureCode;
            LastAcceptedReportTimeSeconds =
                lastAcceptedReportTimeSeconds;
        }

        public int VehicleId { get; }

        public long PlanId { get; }

        public bool IsStateAvailable { get; }

        public bool IsFaulted { get; }

        // 零表示成员车没有报告结构化故障。
        public int FailureCode { get; }

        // 使用主车本地单调时钟，不能直接填写从车上传的时间戳。
        public double LastAcceptedReportTimeSeconds { get; }
    }

    // 一次安全检查的结果；ShouldStop可直接作为是否停车的判断标识。
    public readonly struct FleetSafetyDecision
    {
        internal FleetSafetyDecision(
            bool shouldStop,
            int sourceVehicleId,
            string reason)
        {
            ShouldStop = shouldStop;
            SourceVehicleId = sourceVehicleId;
            Reason = reason ?? string.Empty;
        }

        public bool ShouldStop { get; }

        // 零表示原因属于整个车队，而不是某一辆成员车。
        public int SourceVehicleId { get; }

        public string Reason { get; }
    }

    // 检查成员通信和健康状态，并锁存需要整队停车的首个原因。
    public sealed class FleetSafetySupervisor
    {
        private readonly double _communicationTimeoutSeconds;

        private long _activePlanId;
        private FleetSafetyDecision _latchedDecision;

        public FleetSafetySupervisor(
            double communicationTimeoutSeconds)
        {
            NumericGuard.EnsureFinitePositive(
                communicationTimeoutSeconds,
                nameof(communicationTimeoutSeconds));

            _communicationTimeoutSeconds =
                communicationTimeoutSeconds;
            Reset();
        }

        public double CommunicationTimeoutSeconds =>
            _communicationTimeoutSeconds;

        public long ActivePlanId => _activePlanId;

        public bool IsActive => _activePlanId > 0;

        public bool IsStopLatched =>
            _latchedDecision.ShouldStop;

        public FleetSafetyDecision LastDecision =>
            _latchedDecision;

        // 开始一次新任务，同时清除上一任务留下的停车锁存。
        public void Start(long planId)
        {
            if (planId <= 0)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(planId),
                    "活动任务编号必须大于零。");
            }

            _activePlanId = planId;
            _latchedDecision = CreateContinueDecision();
        }

        // 返回ShouldStop；本类只负责判定，实际停车由后续运行入口执行。
        public FleetSafetyDecision Evaluate(
            FleetLayout layout,
            IReadOnlyList<FleetMemberSafetyStatus> memberStatuses,
            double currentTimeSeconds)
        {
            if (layout == null)
            {
                throw new ArgumentNullException(nameof(layout));
            }

            if (memberStatuses == null)
            {
                throw new ArgumentNullException(
                    nameof(memberStatuses));
            }

            NumericGuard.EnsureFiniteNonNegative(
                currentTimeSeconds,
                nameof(currentTimeSeconds));

            if (!IsActive)
            {
                return new FleetSafetyDecision(
                    true,
                    0,
                    "车队安全监督器尚未启动活动任务。");
            }

            if (IsStopLatched)
            {
                return _latchedDecision;
            }

            var statusesByVehicleId =
                new Dictionary<int, FleetMemberSafetyStatus>();

            for (var index = 0;
                 index < memberStatuses.Count;
                 index++)
            {
                var status = memberStatuses[index];

                // 非当前编队成员的状态不参与本次任务安全判定。
                if (!layout.TryGetVehicle(
                        status.VehicleId,
                        out _))
                {
                    continue;
                }

                if (statusesByVehicleId.ContainsKey(
                        status.VehicleId))
                {
                    return LatchStop(
                        status.VehicleId,
                        $"成员车{status.VehicleId}存在重复状态报告。");
                }

                statusesByVehicleId.Add(
                    status.VehicleId,
                    status);
            }

            for (var index = 0;
                 index < layout.Vehicles.Count;
                 index++)
            {
                var vehicleId =
                    layout.Vehicles[index].VehicleId;

                if (!statusesByVehicleId.TryGetValue(
                        vehicleId,
                        out var status))
                {
                    return LatchStop(
                        vehicleId,
                        $"未收到成员车{vehicleId}的状态报告。");
                }

                if (status.PlanId != _activePlanId)
                {
                    return LatchStop(
                        vehicleId,
                        $"成员车{vehicleId}报告的任务编号" +
                        $"{status.PlanId}与当前任务" +
                        $"{_activePlanId}不一致。");
                }

                if (status.LastAcceptedReportTimeSeconds >
                    currentTimeSeconds)
                {
                    return LatchStop(
                        vehicleId,
                        $"成员车{vehicleId}的主车接收时间晚于当前时间。");
                }

                var reportAgeSeconds =
                    currentTimeSeconds -
                    status.LastAcceptedReportTimeSeconds;
                if (reportAgeSeconds >
                    _communicationTimeoutSeconds)
                {
                    return LatchStop(
                        vehicleId,
                        $"成员车{vehicleId}通信超时，" +
                        $"最近有效报告距今" +
                        $"{reportAgeSeconds:F3}s。");
                }

                if (status.IsFaulted ||
                    status.FailureCode != 0)
                {
                    return LatchStop(
                        vehicleId,
                        $"成员车{vehicleId}报告故障，" +
                        $"故障码为{status.FailureCode}。");
                }

                if (!status.IsStateAvailable)
                {
                    return LatchStop(
                        vehicleId,
                        $"成员车{vehicleId}状态不可用。");
                }
            }

            _latchedDecision = CreateContinueDecision();
            return _latchedDecision;
        }

        // 结束当前任务并清除锁存；未开始新任务前Evaluate仍会要求停车。
        public void Reset()
        {
            _activePlanId = 0;
            _latchedDecision = CreateContinueDecision();
        }

        private FleetSafetyDecision LatchStop(
            int sourceVehicleId,
            string reason)
        {
            _latchedDecision = new FleetSafetyDecision(
                true,
                sourceVehicleId,
                reason);
            return _latchedDecision;
        }

        private static FleetSafetyDecision
            CreateContinueDecision()
        {
            return new FleetSafetyDecision(
                false,
                0,
                string.Empty);
        }
    }
}
