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

namespace MultiWheelC.Tests
{
    // 为同一测试进程中的各车辆端点提供共享FIFO消息队列。
    internal sealed class InMemoryFleetTransportNetwork
    {
        private readonly SharedState _sharedState;
        private readonly HashSet<int> _createdEndpointIds =
            new HashSet<int>();

        public InMemoryFleetTransportNetwork(
            IReadOnlyList<int> vehicleIds,
            int leaderVehicleId)
        {
            _sharedState = new SharedState(
                vehicleIds,
                leaderVehicleId);
        }

        public InMemoryFleetTransport CreateEndpoint(
            int vehicleId)
        {
            lock (_sharedState.SyncRoot)
            {
                if (!_sharedState.CommandQueues.ContainsKey(
                        vehicleId))
                {
                    throw new ArgumentOutOfRangeException(
                        nameof(vehicleId),
                        $"车辆{vehicleId}不属于当前内存车队网络。");
                }

                if (!_createdEndpointIds.Add(vehicleId))
                {
                    throw new InvalidOperationException(
                        $"车辆{vehicleId}的内存通信端点已经创建。");
                }
            }

            return new InMemoryFleetTransport(
                _sharedState,
                vehicleId);
        }

        internal sealed class SharedState
        {
            public SharedState(
                IReadOnlyList<int> vehicleIds,
                int leaderVehicleId)
            {
                if (vehicleIds == null)
                {
                    throw new ArgumentNullException(
                        nameof(vehicleIds));
                }

                if (vehicleIds.Count == 0)
                {
                    throw new ArgumentException(
                        "内存车队网络至少需要一辆车。",
                        nameof(vehicleIds));
                }

                if (leaderVehicleId <= 0)
                {
                    throw new ArgumentOutOfRangeException(
                        nameof(leaderVehicleId),
                        "主车编号必须大于零。");
                }

                CommandQueues =
                    new Dictionary<int, Queue<FleetCommand>>();

                for (var index = 0;
                     index < vehicleIds.Count;
                     index++)
                {
                    var vehicleId = vehicleIds[index];
                    if (vehicleId <= 0)
                    {
                        throw new ArgumentOutOfRangeException(
                            nameof(vehicleIds),
                            $"第{index}辆车的编号必须大于零。");
                    }

                    if (CommandQueues.ContainsKey(vehicleId))
                    {
                        throw new ArgumentException(
                            $"内存车队网络包含重复车号{vehicleId}。",
                            nameof(vehicleIds));
                    }

                    CommandQueues.Add(
                        vehicleId,
                        new Queue<FleetCommand>());
                }

                if (!CommandQueues.ContainsKey(
                        leaderVehicleId))
                {
                    throw new ArgumentException(
                        $"主车{leaderVehicleId}不在车辆列表中。",
                        nameof(leaderVehicleId));
                }

                LeaderVehicleId = leaderVehicleId;
            }

            public object SyncRoot { get; } = new object();

            public int LeaderVehicleId { get; }

            public Dictionary<int, Queue<FleetCommand>>
                CommandQueues { get; }

            public Queue<FleetMemberReport> ReportQueue { get; } =
                new Queue<FleetMemberReport>();
        }
    }

    // 单辆模拟车辆持有的通信端点；只负责消息路由，不解释控制语义。
    internal sealed class InMemoryFleetTransport : IFleetTransport
    {
        private readonly InMemoryFleetTransportNetwork.SharedState
            _sharedState;
        private readonly int _localVehicleId;

        internal InMemoryFleetTransport(
            InMemoryFleetTransportNetwork.SharedState sharedState,
            int localVehicleId)
        {
            _sharedState = sharedState ??
                throw new ArgumentNullException(
                    nameof(sharedState));
            _localVehicleId = localVehicleId;
        }

        public void SendCommand(FleetCommand command)
        {
            if (_localVehicleId !=
                _sharedState.LeaderVehicleId)
            {
                throw new InvalidOperationException(
                    "只有主车通信端点可以发送车队命令。");
            }

            lock (_sharedState.SyncRoot)
            {
                if (command.TargetVehicleId ==
                    FleetProtocol.BroadcastVehicleId)
                {
                    foreach (var pair in
                             _sharedState.CommandQueues)
                    {
                        // 主车本地命令由运行入口直接执行，不通过通信回环。
                        if (pair.Key != _localVehicleId)
                        {
                            pair.Value.Enqueue(command);
                        }
                    }

                    return;
                }

                if (!_sharedState.CommandQueues.TryGetValue(
                        command.TargetVehicleId,
                        out var queue))
                {
                    throw new ArgumentOutOfRangeException(
                        nameof(command),
                        $"目标车辆{command.TargetVehicleId}不存在。");
                }

                queue.Enqueue(command);
            }
        }

        public void SendReport(FleetMemberReport report)
        {
            if (report.VehicleId != _localVehicleId)
            {
                throw new ArgumentException(
                    $"车辆{_localVehicleId}不能发送属于车辆" +
                    $"{report.VehicleId}的状态报告。",
                    nameof(report));
            }

            lock (_sharedState.SyncRoot)
            {
                _sharedState.ReportQueue.Enqueue(report);
            }
        }

        public bool TryReceiveCommand(
            out FleetCommand command)
        {
            lock (_sharedState.SyncRoot)
            {
                var queue =
                    _sharedState.CommandQueues[_localVehicleId];
                if (queue.Count == 0)
                {
                    command = default;
                    return false;
                }

                command = queue.Dequeue();
                return true;
            }
        }

        public bool TryReceiveReport(
            out FleetMemberReport report)
        {
            if (_localVehicleId !=
                _sharedState.LeaderVehicleId)
            {
                throw new InvalidOperationException(
                    "只有主车通信端点可以接收成员状态报告。");
            }

            lock (_sharedState.SyncRoot)
            {
                if (_sharedState.ReportQueue.Count == 0)
                {
                    report = default;
                    return false;
                }

                report =
                    _sharedState.ReportQueue.Dequeue();
                return true;
            }
        }
    }
}
