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

namespace MultiWheelC.Tests
{
    internal static class FleetSafetySupervisorTests
    {
        private const long PlanId = 7;
        private const double CurrentTimeSeconds = 10.0;
        private const double CommunicationTimeoutSeconds = 0.5;

        public static void Run()
        {
            VerifyHealthyFleetContinues();
            VerifyMissingMemberStopsFleet();
            VerifyCommunicationTimeoutStopsFleet();
            VerifyUnavailableStateStopsFleet();
            VerifyMemberFaultStopsFleet();
            VerifyFailureCodeStopsFleet();
            VerifyPlanMismatchStopsFleet();
            VerifyStopIsLatchedUntilNewPlanStarts();

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

        private static void VerifyHealthyFleetContinues()
        {
            var supervisor = CreateStartedSupervisor();

            var decision = supervisor.Evaluate(
                CreateLayout(),
                CreateHealthyStatuses(),
                CurrentTimeSeconds);

            AssertFalse(
                decision.ShouldStop,
                "成员状态健康时不应停车");
        }

        private static void VerifyMissingMemberStopsFleet()
        {
            var supervisor = CreateStartedSupervisor();

            var decision = supervisor.Evaluate(
                CreateLayout(),
                new[] { CreateHealthyStatus(1) },
                CurrentTimeSeconds);

            AssertStopFromVehicle(
                decision,
                2,
                "缺少成员报告");
        }

        private static void VerifyCommunicationTimeoutStopsFleet()
        {
            var supervisor = CreateStartedSupervisor();
            var statuses = new[]
            {
                CreateHealthyStatus(1),
                new FleetMemberSafetyStatus(
                    vehicleId: 2,
                    planId: PlanId,
                    isStateAvailable: true,
                    isFaulted: false,
                    failureCode: 0,
                    lastAcceptedReportTimeSeconds: 9.4)
            };

            var decision = supervisor.Evaluate(
                CreateLayout(),
                statuses,
                CurrentTimeSeconds);

            AssertStopFromVehicle(
                decision,
                2,
                "通信超时");
        }

        private static void VerifyUnavailableStateStopsFleet()
        {
            var supervisor = CreateStartedSupervisor();
            var statuses = new[]
            {
                CreateHealthyStatus(1),
                new FleetMemberSafetyStatus(
                    vehicleId: 2,
                    planId: PlanId,
                    isStateAvailable: false,
                    isFaulted: false,
                    failureCode: 0,
                    lastAcceptedReportTimeSeconds: 9.9)
            };

            var decision = supervisor.Evaluate(
                CreateLayout(),
                statuses,
                CurrentTimeSeconds);

            AssertStopFromVehicle(
                decision,
                2,
                "状态不可用");
        }

        private static void VerifyMemberFaultStopsFleet()
        {
            var supervisor = CreateStartedSupervisor();
            var statuses = new[]
            {
                CreateHealthyStatus(1),
                new FleetMemberSafetyStatus(
                    vehicleId: 2,
                    planId: PlanId,
                    isStateAvailable: true,
                    isFaulted: true,
                    failureCode: 0,
                    lastAcceptedReportTimeSeconds: 9.9)
            };

            var decision = supervisor.Evaluate(
                CreateLayout(),
                statuses,
                CurrentTimeSeconds);

            AssertStopFromVehicle(
                decision,
                2,
                "成员故障状态");
        }

        private static void VerifyFailureCodeStopsFleet()
        {
            var supervisor = CreateStartedSupervisor();
            var statuses = new[]
            {
                CreateHealthyStatus(1),
                new FleetMemberSafetyStatus(
                    vehicleId: 2,
                    planId: PlanId,
                    isStateAvailable: true,
                    isFaulted: false,
                    failureCode: 42,
                    lastAcceptedReportTimeSeconds: 9.9)
            };

            var decision = supervisor.Evaluate(
                CreateLayout(),
                statuses,
                CurrentTimeSeconds);

            AssertStopFromVehicle(
                decision,
                2,
                "成员故障码");
        }

        private static void VerifyPlanMismatchStopsFleet()
        {
            var supervisor = CreateStartedSupervisor();
            var statuses = new[]
            {
                CreateHealthyStatus(1),
                new FleetMemberSafetyStatus(
                    vehicleId: 2,
                    planId: PlanId - 1,
                    isStateAvailable: true,
                    isFaulted: false,
                    failureCode: 0,
                    lastAcceptedReportTimeSeconds: 9.9)
            };

            var decision = supervisor.Evaluate(
                CreateLayout(),
                statuses,
                CurrentTimeSeconds);

            AssertStopFromVehicle(
                decision,
                2,
                "任务编号不一致");
        }

        private static void VerifyStopIsLatchedUntilNewPlanStarts()
        {
            var supervisor = CreateStartedSupervisor();
            supervisor.Evaluate(
                CreateLayout(),
                new[] { CreateHealthyStatus(1) },
                CurrentTimeSeconds);

            var latchedDecision = supervisor.Evaluate(
                CreateLayout(),
                CreateHealthyStatuses(),
                CurrentTimeSeconds);
            AssertTrue(
                latchedDecision.ShouldStop,
                "故障恢复后停车决定仍应锁存");

            supervisor.Start(PlanId + 1);
            var recoveredDecision = supervisor.Evaluate(
                CreateLayout(),
                new[]
                {
                    CreateHealthyStatus(1, PlanId + 1),
                    CreateHealthyStatus(2, PlanId + 1)
                },
                CurrentTimeSeconds);
            AssertFalse(
                recoveredDecision.ShouldStop,
                "开始新任务后应清除旧任务停车锁存");
        }

        private static FleetSafetySupervisor
            CreateStartedSupervisor()
        {
            var supervisor = new FleetSafetySupervisor(
                CommunicationTimeoutSeconds);
            supervisor.Start(PlanId);
            return supervisor;
        }

        private static FleetLayout CreateLayout()
        {
            return new FleetLayout(
                new[]
                {
                    new VehicleLayout(
                        1,
                        new Pose2D(-1.0, 0.0, 0.0)),
                    new VehicleLayout(
                        2,
                        new Pose2D(1.0, 0.0, Math.PI))
                });
        }

        private static IReadOnlyList<FleetMemberSafetyStatus>
            CreateHealthyStatuses()
        {
            return new[]
            {
                CreateHealthyStatus(1),
                CreateHealthyStatus(2)
            };
        }

        private static FleetMemberSafetyStatus CreateHealthyStatus(
            int vehicleId,
            long planId = PlanId)
        {
            return new FleetMemberSafetyStatus(
                vehicleId,
                planId,
                isStateAvailable: true,
                isFaulted: false,
                failureCode: 0,
                lastAcceptedReportTimeSeconds: 9.9);
        }

        private static void AssertStopFromVehicle(
            FleetSafetyDecision decision,
            int expectedVehicleId,
            string scenario)
        {
            AssertTrue(
                decision.ShouldStop,
                $"{scenario}时应停车");
            AssertTrue(
                decision.SourceVehicleId == expectedVehicleId,
                $"{scenario}的来源车辆不正确");
            AssertTrue(
                !string.IsNullOrWhiteSpace(decision.Reason),
                $"{scenario}应提供停车原因");
        }

        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);
        }
    }
}
