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

namespace MultiWheelC.Tests
{
    internal static class InMemoryFleetTransportTests
    {
        public static void Run()
        {
            VerifyTargetedCommandRoutingAndFifoOrder();
            VerifyBroadcastReachesAllFollowersOnly();
            VerifyMemberReportReturnsToLeader();
            VerifyEndpointRolesAndVehicleIdentity();

            Console.WriteLine(
                "InMemoryFleetTransport测试通过，共4个场景。");
        }

        private static void VerifyTargetedCommandRoutingAndFifoOrder()
        {
            var network = CreateNetwork();
            var leader = network.CreateEndpoint(1);
            var member2 = network.CreateEndpoint(2);
            var member3 = network.CreateEndpoint(3);

            leader.SendCommand(CreateCommand(2, sequenceNumber: 1));
            leader.SendCommand(CreateCommand(2, sequenceNumber: 2));

            AssertTrue(
                member2.TryReceiveCommand(out var first) &&
                first.SequenceNumber == 1,
                "定向命令第一条应到达目标车辆");
            AssertTrue(
                member2.TryReceiveCommand(out var second) &&
                second.SequenceNumber == 2,
                "定向命令应保持FIFO顺序");
            AssertFalse(
                member2.TryReceiveCommand(out _),
                "目标车辆不应收到额外命令");
            AssertFalse(
                member3.TryReceiveCommand(out _),
                "其他成员不应收到定向命令");
        }

        private static void VerifyBroadcastReachesAllFollowersOnly()
        {
            var network = CreateNetwork();
            var leader = network.CreateEndpoint(1);
            var member2 = network.CreateEndpoint(2);
            var member3 = network.CreateEndpoint(3);

            leader.SendCommand(
                CreateCommand(
                    FleetProtocol.BroadcastVehicleId,
                    sequenceNumber: 3));

            AssertTrue(
                member2.TryReceiveCommand(out var command2) &&
                command2.SequenceNumber == 3,
                "广播命令应到达成员车2");
            AssertTrue(
                member3.TryReceiveCommand(out var command3) &&
                command3.SequenceNumber == 3,
                "广播命令应到达成员车3");
            AssertFalse(
                leader.TryReceiveCommand(out _),
                "主车本地命令不应通过通信层回环");
        }

        private static void VerifyMemberReportReturnsToLeader()
        {
            var network = CreateNetwork();
            var leader = network.CreateEndpoint(1);
            var member2 = network.CreateEndpoint(2);

            member2.SendReport(
                CreateReport(
                    vehicleId: 2,
                    sequenceNumber: 8));

            AssertTrue(
                leader.TryReceiveReport(out var report),
                "主车应收到成员报告");
            AssertTrue(
                report.VehicleId == 2 &&
                report.SequenceNumber == 8,
                "主车收到的成员报告内容不正确");
            AssertFalse(
                leader.TryReceiveReport(out _),
                "报告队列取空后应返回false");
        }

        private static void VerifyEndpointRolesAndVehicleIdentity()
        {
            var network = CreateNetwork();
            var leader = network.CreateEndpoint(1);
            var member2 = network.CreateEndpoint(2);

            AssertThrows<InvalidOperationException>(
                () => member2.SendCommand(
                    CreateCommand(1, sequenceNumber: 1)),
                "从车不能发送车队命令");
            AssertThrows<InvalidOperationException>(
                () => member2.TryReceiveReport(out _),
                "从车不能消费全队成员报告");
            AssertThrows<ArgumentException>(
                () => member2.SendReport(
                    CreateReport(
                        vehicleId: 1,
                        sequenceNumber: 1)),
                "端点不能冒用其他车辆身份");

            leader.SendReport(
                CreateReport(
                    vehicleId: 1,
                    sequenceNumber: 2));
            AssertTrue(
                leader.TryReceiveReport(out var leaderReport) &&
                leaderReport.VehicleId == 1,
                "主车作为成员时也应能上报本车状态");
        }

        private static InMemoryFleetTransportNetwork CreateNetwork()
        {
            return new InMemoryFleetTransportNetwork(
                new[] { 1, 2, 3 },
                leaderVehicleId: 1);
        }

        private static FleetCommand CreateCommand(
            int targetVehicleId,
            long sequenceNumber)
        {
            return new FleetCommand(
                planId: 5,
                sequenceNumber,
                targetVehicleId,
                FleetCommandKind.Stop,
                motionDirectionInBodyRadians: 0.0,
                twistInVehicleBody: Twist2D.Zero,
                validForSeconds: 0.5);
        }

        private static FleetMemberReport CreateReport(
            int vehicleId,
            long sequenceNumber)
        {
            return new FleetMemberReport(
                vehicleId,
                planId: 5,
                sequenceNumber,
                sampleTimestampSeconds: 1.0,
                poseInCommonWorld: Pose2D.Identity,
                twistAtVehicleOriginInCommonWorld:
                    Twist2D.Zero,
                isStateAvailable: true,
                hasValidVelocityEstimate: true,
                FleetMemberState.Active,
                lastAppliedCommandSequence: 1);
        }

        private static void AssertThrows<TException>(
            Action action,
            string scenario)
            where TException : Exception
        {
            try
            {
                action();
            }
            catch (TException)
            {
                return;
            }

            throw new InvalidOperationException(
                $"{scenario}时应抛出{typeof(TException).Name}。");
        }

        private static void AssertTrue(
            bool condition,
            string message)
        {
            if (!condition)
            {
                throw new InvalidOperationException(message);
            }
        }

        private static void AssertFalse(
            bool condition,
            string message)
        {
            AssertTrue(!condition, message);
        }
    }
}
