using System;
using System.Collections.Generic;
using System.IO;
using MultiWheelC.TrajectoryPlanning.CoarsePath;
using MultiWheelC.TrajectoryPlanning.EMPlanner;
using MultiWheelC.TrajectoryPlanning.PathSmoothing;
using MultiWheelC.TrajectoryPlanning.TrajectoryObservation;

namespace EMPlannerVerificationHost;

internal static class TrajectoryObservationSegmentChecks
{
    public static void Run()
    {
        RejectsHardcodedActiveSegmentIndex();
        AdvancesOnlyAfterContinuousStopAndStableNextDirection();
        RejectsNonTerminalOrMismatchedGearTrajectory();
        ResetsConfirmationForInvalidDirectionEvidence();
        CompletesOneSegmentWithoutIndexingPastTheEnd();
    }

    private static void RejectsHardcodedActiveSegmentIndex()
    {
        string pipelinePath = Path.Combine(Directory.GetCurrentDirectory(), "ClumsyPilot",
            "ParkrobTrajplanner", "tarjplanner_movementtest", "TrajectoryObservationPipeline.cs");
        string source = File.ReadAllText(pipelinePath);

        Verification.True(source.IndexOf("segmentIndex = 0", StringComparison.Ordinal) < 0,
            "active observation segment is not hardcoded to zero");
    }

    private static void AdvancesOnlyAfterContinuousStopAndStableNextDirection()
    {
        DateTimeOffset t0 = new DateTimeOffset(2026, 8, 6, 0, 0, 0, TimeSpan.Zero);
        var tracker = CreateTracker();
        EmTrajectory trajectory = GearTerminal(t0);

        tracker.Update(t0, StateAtSwitch(0d, t0, 1L), trajectory);
        Verification.Equal(TrajectoryObservationSegmentPhase.WaitingForStop, tracker.State.Phase,
            "first zero sample begins stop hold");
        tracker.Update(t0.AddSeconds(0.21d), StateAtSwitch(0d, t0.AddSeconds(0.21d), 2L), trajectory);
        Verification.Equal(TrajectoryObservationSegmentPhase.WaitingForDirection, tracker.State.Phase,
            "continuous stop arms next direction");
        tracker.Update(t0.AddSeconds(0.25d), StateAtSwitch(-0.03d, t0.AddSeconds(0.25d), 3L), trajectory);
        tracker.Update(t0.AddSeconds(0.30d), StateAtSwitch(-0.03d, t0.AddSeconds(0.30d), 4L), trajectory);
        TrajectoryObservationSegmentUpdate advanced = tracker.Update(
            t0.AddSeconds(0.35d), StateAtSwitch(-0.03d, t0.AddSeconds(0.35d), 5L), trajectory);

        Verification.True(advanced.Advanced, "three stable reverse samples advance exactly one segment");
        Verification.Equal(1, tracker.State.ActiveSegmentIndex, "tracker advances only from N to N+1");
        Verification.Equal(TrajectoryObservationSegmentPhase.Planning, tracker.State.Phase,
            "advanced tracker resumes planning on next segment");
    }

    private static void RejectsNonTerminalOrMismatchedGearTrajectory()
    {
        DateTimeOffset t0 = new DateTimeOffset(2026, 8, 6, 1, 0, 0, TimeSpan.Zero);
        var tracker = CreateTracker();

        tracker.Update(t0, StateAtSwitch(0d, t0, 1L), GearTerminal(t0, EmTerminalType.Goal));
        Verification.Equal(TrajectoryObservationSegmentPhase.Planning, tracker.State.Phase,
            "non-gear terminal cannot begin confirmation");

        tracker.Update(t0.AddSeconds(0.01d), StateAtSwitch(0d, t0.AddSeconds(0.01d), 2L),
            GearTerminal(t0, EmTerminalType.GearSwitch, TravelDirection.Reverse));
        Verification.Equal(TrajectoryObservationSegmentPhase.Planning, tracker.State.Phase,
            "wrong trajectory direction cannot begin confirmation");

        tracker.Update(t0.AddSeconds(0.02d), StateAtSwitch(0d, t0.AddSeconds(0.02d), 3L),
            GearTerminal(t0, EmTerminalType.GearSwitch, TravelDirection.Forward, 0.10d));
        Verification.Equal(TrajectoryObservationSegmentPhase.Planning, tracker.State.Phase,
            "trajectory before its absolute terminal cannot begin confirmation");
    }

    private static void ResetsConfirmationForInvalidDirectionEvidence()
    {
        DateTimeOffset t0 = new DateTimeOffset(2026, 8, 6, 2, 0, 0, TimeSpan.Zero);
        AssertConfirmationReset(t0, StateAtSwitch(0.03d, t0.AddSeconds(0.25d), 3L), GearTerminal(t0),
            "wrong signed direction resets confirmation");
        AssertConfirmationReset(t0, StateAtSwitch(0d, t0.AddSeconds(0.25d), 3L), GearTerminal(t0),
            "zero speed resets confirmation");
        AssertConfirmationReset(t0, StateAt(2d, 0d, -0.03d, t0.AddSeconds(0.25d), 3L), GearTerminal(t0),
            "excessive switch projection distance resets confirmation");
        AssertConfirmationReset(t0, StateAtSwitch(-0.03d, t0.AddSeconds(0.25d), 3L),
            GearTerminal(t0, EmTerminalType.Goal), "non-gear trajectory resets confirmation");

        var repeated = CreateTracker();
        ArmWaitingForDirection(repeated, t0, GearTerminal(t0));
        repeated.Update(t0.AddSeconds(0.25d), StateAtSwitch(-0.03d, t0.AddSeconds(0.25d), 2L), GearTerminal(t0));
        Verification.Equal(TrajectoryObservationSegmentPhase.Planning, repeated.State.Phase,
            "repeated sequence resets confirmation");

        var discontinuous = CreateTracker();
        ArmWaitingForDirection(discontinuous, t0, GearTerminal(t0));
        discontinuous.Update(t0.AddSeconds(0.20d), StateAtSwitch(-0.03d, t0.AddSeconds(0.20d), 3L), GearTerminal(t0));
        Verification.Equal(TrajectoryObservationSegmentPhase.Planning, discontinuous.State.Phase,
            "time discontinuity resets confirmation");
    }

    private static void CompletesOneSegmentWithoutIndexingPastTheEnd()
    {
        DateTimeOffset t0 = new DateTimeOffset(2026, 8, 6, 3, 0, 0, TimeSpan.Zero);
        var tracker = new TrajectoryObservationSegmentTracker(new[] { CreateSegments()[0] },
            CreateSettings(), 0.01d);

        TrajectoryObservationSegmentUpdate completed = tracker.Update(
            t0, StateAtSwitch(0d, t0, 1L), GearTerminal(t0));

        Verification.True(completed.Completed, "single segment update reports completion");
        Verification.Equal(TrajectoryObservationSegmentPhase.Completed, tracker.State.Phase,
            "single segment reaches completed instead of indexing past end");
        Verification.Equal(0, tracker.State.ActiveSegmentIndex, "completed tracker retains final segment index");
    }

    private static void AssertConfirmationReset(DateTimeOffset t0, VehicleMotionState invalidState,
        EmTrajectory invalidTrajectory, string name)
    {
        var tracker = CreateTracker();
        ArmWaitingForDirection(tracker, t0, GearTerminal(t0));
        tracker.Update(invalidState.CapturedAtUtc, invalidState, invalidTrajectory);
        Verification.Equal(TrajectoryObservationSegmentPhase.Planning, tracker.State.Phase, name);
    }

    private static void ArmWaitingForDirection(TrajectoryObservationSegmentTracker tracker, DateTimeOffset t0,
        EmTrajectory trajectory)
    {
        tracker.Update(t0, StateAtSwitch(0d, t0, 1L), trajectory);
        tracker.Update(t0.AddSeconds(0.21d), StateAtSwitch(0d, t0.AddSeconds(0.21d), 2L), trajectory);
        Verification.Equal(TrajectoryObservationSegmentPhase.WaitingForDirection, tracker.State.Phase,
            "fixture arms direction confirmation");
    }

    private static TrajectoryObservationSegmentTracker CreateTracker()
    {
        return new TrajectoryObservationSegmentTracker(CreateSegments(), CreateSettings(), 0.01d);
    }

    private static TrajectoryObservationSettings CreateSettings()
    {
        return new TrajectoryObservationSettings
        {
            DirectionConfirmationSpeedMetersPerSecond = 0.02d,
            DirectionConfirmationSamples = 3,
            GearSwitchProjectionToleranceMeters = 0.50d,
            GearSwitchStopHoldSeconds = 0.20d,
        }.CreateValidatedSnapshot();
    }

    private static IReadOnlyList<DirectionSegmentView> CreateSegments()
    {
        return new[]
        {
            CreateSegment(0, TravelDirection.Forward, 0d, 1d, false, 0d,
                EmBoundaryType.None, EmBoundaryType.GearSwitchApproach),
            CreateSegment(1, TravelDirection.Reverse, 1d, 0d, true, 1d,
                EmBoundaryType.GearSwitchDeparture, EmBoundaryType.Goal),
        };
    }

    private static DirectionSegmentView CreateSegment(int index, TravelDirection direction, double startX,
        double endX, bool startIsGearSwitch, double sourceStartS, EmBoundaryType startBoundary,
        EmBoundaryType endBoundary)
    {
        var points = new List<SmoothedPathPoint>
        {
            new SmoothedPathPoint(startX, 0d, 0d, 0d, 0d, direction, 0d, 0d, 1d,
                startIsGearSwitch, SmoothedPathPointSource.Anchor),
            new SmoothedPathPoint(endX, 0d, 0d, 0d, 1d, direction, 0d, 0d, 1d,
                false, SmoothedPathPointSource.Anchor),
        };
        return new DirectionSegmentView(index, direction, points,
            new ReferenceBoundary(index, 0d, startBoundary, sourceStartS),
            new ReferenceBoundary(index, 1d, endBoundary, sourceStartS + 1d), sourceStartS);
    }

    private static VehicleMotionState StateAtSwitch(double speed, DateTimeOffset time, long sequenceId)
    {
        return StateAt(1d, 0d, speed, time, sequenceId);
    }

    private static VehicleMotionState StateAt(double x, double y, double speed, DateTimeOffset time, long sequenceId)
    {
        return new VehicleMotionState(new Pose2D(x, y, 0d), speed, null, time, sequenceId);
    }

    private static EmTrajectory GearTerminal(DateTimeOffset effectiveAtUtc,
        EmTerminalType terminalType = EmTerminalType.GearSwitch, TravelDirection direction = TravelDirection.Forward,
        double finalTimeFromStart = 0d)
    {
        var metadata = new EmTrajectoryMetadata("gear-terminal-" + effectiveAtUtc.Ticks, effectiveAtUtc,
            effectiveAtUtc, 1L, "segment-check", 1L, string.Empty, 0, direction, terminalType,
            EmLongitudinalMode.ExactStopAtBoundary, EmPlanningScope.RollingHorizon);
        return new EmTrajectory(metadata, new[]
        {
            new EmTrajectoryPoint(1d, 0d, 0d, 0d, finalTimeFromStart, 0d, 0, 1d, 1d,
                direction, EmBoundaryType.GearSwitchApproach, 0d, 0d),
        });
    }
}
