using System;
using System.IO;
using System.Text;
using MultiWheelC.TrajectoryPlanning.EMPlanner;
using MultiWheelC.TrajectoryPlanning.TrajectoryObservation;

namespace EMPlannerVerificationHost;

internal static class TrajectoryObservationSettingsChecks
{
    public static void Run()
    {
        VerifiesDefaults();
        VerifiesValidatedSnapshotFreezesVisualizationSettings();
        RejectsInvalidVisualizationSettings();
        VerifiesMovementTestMapsEveryFrozenSetting();
        VerifiesMovementTestFullDirectionScopeMapping();
    }

    private static void VerifiesDefaults()
    {
        var defaults = new TrajectoryObservationSettings();

        Verification.NearlyEqual(5d, defaults.SolverTimeoutSeconds, "observer solver timeout default");
        Verification.Equal(100000, defaults.MaximumOsqpIterations, "observer OSQP default");
        Verification.NearlyEqual(2d, defaults.TimeHorizonSeconds, "observer ST horizon default");
        Verification.Equal(EmPlanningScope.FullDirectionSegment, defaults.PlanningScope,
            "observer full-direction-segment scope default");
        Verification.True(defaults.EnableWebVisualization, "web defaults on");
        Verification.True(defaults.AutoOpenWebVisualization, "web auto-open defaults on");
        Verification.True(!defaults.EnableNativePainterVisualization, "Painter defaults off");
        Verification.Equal(0, defaults.WebVisualizationPort, "dynamic port default");
        Verification.NearlyEqual(10d, defaults.WebRefreshRateHz, "web refresh default");
        Verification.Equal(60, defaults.VisualizationHistoryCycleLimit, "history default");
        Verification.NearlyEqual(0.02d, defaults.DirectionConfirmationSpeedMetersPerSecond,
            "direction speed threshold");
        Verification.Equal(3, defaults.DirectionConfirmationSamples, "direction sample default");
        Verification.NearlyEqual(0.50d, defaults.GearSwitchProjectionToleranceMeters,
            "gear projection default");
        Verification.NearlyEqual(0.20d, defaults.GearSwitchStopHoldSeconds, "gear stop hold default");
    }

    private static void VerifiesValidatedSnapshotFreezesVisualizationSettings()
    {
        var source = new TrajectoryObservationSettings
        {
            PlanningScope = EmPlanningScope.FullDirectionSegment,
            EnableWebVisualization = true,
            AutoOpenWebVisualization = false,
            WebVisualizationPort = 2048,
            WebRefreshRateHz = 15d,
            VisualizationHistoryCycleLimit = 17,
            EnableNativePainterVisualization = true,
            DirectionConfirmationSpeedMetersPerSecond = 0.03d,
            DirectionConfirmationSamples = 4,
            GearSwitchProjectionToleranceMeters = 0.6d,
            GearSwitchStopHoldSeconds = 0.3d,
        };
        TrajectoryObservationSettings snapshot = source.CreateValidatedSnapshot();

        source.EnableWebVisualization = false;
        source.PlanningScope = EmPlanningScope.RollingHorizon;
        source.AutoOpenWebVisualization = true;
        source.WebVisualizationPort = 0;
        source.WebRefreshRateHz = 12d;
        source.VisualizationHistoryCycleLimit = 18;
        source.EnableNativePainterVisualization = false;
        source.DirectionConfirmationSpeedMetersPerSecond = 0.04d;
        source.DirectionConfirmationSamples = 5;
        source.GearSwitchProjectionToleranceMeters = 0.7d;
        source.GearSwitchStopHoldSeconds = 0.4d;

        Verification.Equal(EmPlanningScope.FullDirectionSegment, snapshot.PlanningScope,
            "snapshot freezes planning scope");
        Verification.True(snapshot.EnableWebVisualization, "snapshot freezes web switch");
        Verification.True(!snapshot.AutoOpenWebVisualization, "snapshot freezes web auto-open switch");
        Verification.Equal(2048, snapshot.WebVisualizationPort, "snapshot freezes web port");
        Verification.NearlyEqual(15d, snapshot.WebRefreshRateHz, "snapshot freezes web refresh");
        Verification.Equal(17, snapshot.VisualizationHistoryCycleLimit, "snapshot freezes history limit");
        Verification.True(snapshot.EnableNativePainterVisualization, "snapshot freezes Painter switch");
        Verification.NearlyEqual(0.03d, snapshot.DirectionConfirmationSpeedMetersPerSecond,
            "snapshot freezes direction speed");
        Verification.Equal(4, snapshot.DirectionConfirmationSamples, "snapshot freezes direction samples");
        Verification.NearlyEqual(0.6d, snapshot.GearSwitchProjectionToleranceMeters,
            "snapshot freezes gear projection tolerance");
        Verification.NearlyEqual(0.3d, snapshot.GearSwitchStopHoldSeconds, "snapshot freezes gear stop hold");
    }

    private static void RejectsInvalidVisualizationSettings()
    {
        AssertInvalid(settings => settings.WebVisualizationPort = -1, "negative web port");
        AssertInvalid(settings => settings.WebVisualizationPort = 1, "low reserved web port");
        AssertInvalid(settings => settings.WebVisualizationPort = 1023, "last reserved web port");
        AssertInvalid(settings => settings.WebVisualizationPort = 65536, "overflow web port");
        AssertInvalid(settings => settings.PlanningScope = (EmPlanningScope)999, "undefined planning scope");
        AssertInvalid(settings => settings.WebRefreshRateHz = 0d, "zero web refresh");
        AssertInvalid(settings => settings.VisualizationHistoryCycleLimit = 0, "zero history limit");
        AssertInvalid(settings => settings.DirectionConfirmationSpeedMetersPerSecond = 0d,
            "zero direction speed threshold");
        AssertInvalid(settings => settings.DirectionConfirmationSamples = 0, "zero direction samples");
        AssertInvalid(settings => settings.GearSwitchProjectionToleranceMeters = 0d,
            "zero gear projection tolerance");
        AssertInvalid(settings => settings.GearSwitchStopHoldSeconds = 0d, "zero gear stop hold");
    }

    private static void VerifiesMovementTestMapsEveryFrozenSetting()
    {
        string path = Path.Combine(Directory.GetCurrentDirectory(), "ClumsyPilot", "ParkrobTrajplanner",
            "tarjplanner_movementtest", "MovementTest.TrajectoryObservationTest.cs");
        string source = new UTF8Encoding(false, true).GetString(File.ReadAllBytes(path));
        foreach (string field in new[]
        {
            "EnableWebVisualization", "AutoOpenWebVisualization", "WebVisualizationPort", "WebRefreshRateHz",
            "VisualizationHistoryCycleLimit", "EnableNativePainterVisualization",
            "DirectionConfirmationSpeedMetersPerSecond", "DirectionConfirmationSamples",
            "GearSwitchProjectionToleranceMeters", "GearSwitchStopHoldSeconds",
        })
        {
            Verification.True(source.Contains("public ") && source.Contains(" " + field + " ="),
                "MovementTest exposes " + field);
            Verification.True(source.Contains(field + " = " + field + ","),
                "MovementTest maps " + field + " into settings");
        }
    }

    private static void VerifiesMovementTestFullDirectionScopeMapping()
    {
        string path = Path.Combine(Directory.GetCurrentDirectory(), "ClumsyPilot", "ParkrobTrajplanner",
            "tarjplanner_movementtest", "MovementTest.TrajectoryObservationTest.cs");
        string source = new UTF8Encoding(false, true).GetString(File.ReadAllBytes(path));

        Verification.True(source.Contains("public bool UseFullDirectionSegmentPlanning = true;"),
            "MovementTest exposes the full-direction-segment switch on by default");
        Verification.True(source.Contains(
                "PlanningScope = UseFullDirectionSegmentPlanning ? EmPlanningScope.FullDirectionSegment : EmPlanningScope.RollingHorizon,"),
            "MovementTest maps the full-direction-segment switch into settings");
        Verification.True(source.Contains("滚动兼容"),
            "MovementTest labels TimeHorizonSeconds as rolling-compatible");
    }

    private static void AssertInvalid(Action<TrajectoryObservationSettings> mutate, string name)
    {
        var settings = new TrajectoryObservationSettings();
        mutate(settings);
        bool threw = false;
        try { settings.Validate(); }
        catch (ArgumentOutOfRangeException) { threw = true; }
        Verification.True(threw, "observer rejects invalid " + name);
    }
}
