namespace ElevatorDiag;

public enum ElevatorState
{
    Idle,
    MovingUp,
    MovingDown,
    DoorOpen,
    Faulted
}

public class Elevator
{
    public int CurrentFloor { get; private set; } = 1;
    public double CurrentPosition { get; private set; } = 1.0;
    public ElevatorState State { get; private set; } = ElevatorState.Idle;
    public List<int> TargetFloors { get; } = new();
    public string? LastFault { get; private set; }

    private const double Speed = 0.5; // floors per tick
    private const int DoorHoldTicks = 3;
    private int _doorTimer = 0;

    public void RequestFloor(int floor)
    {
        if (State == ElevatorState.Faulted) return;
        if (!TargetFloors.Contains(floor))
        {
            TargetFloors.Add(floor);
            // No automatic sort here, we'll handle direction in GetNextTarget
        }
    }

    public void Update()
    {
        if (State == ElevatorState.Faulted) return;

        if (State == ElevatorState.DoorOpen)
        {
            _doorTimer++;
            if (_doorTimer >= DoorHoldTicks)
            {
                _doorTimer = 0;
                State = ElevatorState.Idle;
            }
            return;
        }

        if (TargetFloors.Count == 0)
        {
            State = ElevatorState.Idle;
            return;
        }

        int target = GetNextTarget();
        
        if (CurrentPosition < target)
        {
            State = ElevatorState.MovingUp;
            CurrentPosition += Speed;
        }
        else if (CurrentPosition > target)
        {
            State = ElevatorState.MovingDown;
            CurrentPosition -= Speed;
        }

        CurrentFloor = (int)Math.Round(CurrentPosition);

        if (Math.Abs(CurrentPosition - target) < 0.01)
        {
            CurrentPosition = target;
            CurrentFloor = target;
            TargetFloors.Remove(target);
            State = ElevatorState.DoorOpen;
        }
    }

    private int GetNextTarget()
    {
        // Simple logic: if moving, keep moving in same direction if targets exist
        if (State == ElevatorState.MovingUp)
        {
            var up = TargetFloors.Where(f => f > CurrentPosition).OrderBy(f => f).FirstOrDefault();
            if (up != 0) return up;
        }
        if (State == ElevatorState.MovingDown)
        {
            var down = TargetFloors.Where(f => f < CurrentPosition).OrderByDescending(f => f).FirstOrDefault();
            if (down != 0) return down;
        }
        
        // Closest if idle or changed direction
        return TargetFloors.OrderBy(f => Math.Abs(f - CurrentPosition)).First();
    }

    public void TriggerFault(string reason)
    {
        State = ElevatorState.Faulted;
        LastFault = reason;
    }

    public void ResetFault()
    {
        State = ElevatorState.Idle;
        LastFault = null;
    }
}
