import pytest
import time
from src.scaling_policies.queue_length_policy import QueueLengthScalingPolicy
from src.simulators.message_queue import MessageQueue

@pytest.fixture
def message_queue():
    return MessageQueue()

@pytest.fixture
def scaling_policy(message_queue):
    return QueueLengthScalingPolicy(message_queue=message_queue, queue_threshold=5, queue_low_threshold=2, scale_up_amount=1, scale_down_amount=1, cooldown_seconds=1)


def test_scale_up(message_queue, scaling_policy):
    # Simulate queue length exceeding threshold
    for i in range(6):
        message_queue.enqueue(f"Message {i}")

    initial_size = scaling_policy.current_size
    scaling_policy.check_and_scale()
    time.sleep(1.5) # wait for cooldown

    assert scaling_policy.current_size == initial_size + 1
    assert len(scaling_policy.scaling_events) == 1
    assert scaling_policy.scaling_events[0]['action'] == 'scale_up'
    assert scaling_policy.scaling_events[0]['queue_length'] == 6


def test_scale_down(message_queue, scaling_policy):
    # First, scale up to create some instances
    for i in range(6):
        message_queue.enqueue(f"Message {i}")

    scaling_policy.check_and_scale()
    time.sleep(1.5) # wait for cooldown

    # Now, reduce the queue length below the low threshold
    message_queue.dequeue()
    message_queue.dequeue()
    message_queue.dequeue()
    message_queue.dequeue()
    message_queue.dequeue()
    message_queue.dequeue()

    scaling_policy.check_and_scale()
    time.sleep(1.5) # wait for cooldown

    assert scaling_policy.current_size == 0
    assert len(scaling_policy.scaling_events) == 2
    assert scaling_policy.scaling_events[1]['action'] == 'scale_down'
    assert scaling_policy.scaling_events[1]['queue_length'] == 0

def test_cooldown_period(message_queue, scaling_policy):
    # Simulate queue length exceeding threshold
    for i in range(6):
        message_queue.enqueue(f"Message {i}")

    initial_size = scaling_policy.current_size
    scaling_policy.check_and_scale()
    scaling_policy.check_and_scale()
    time.sleep(0.5)  # Before cooldown
    scaling_policy.check_and_scale()

    assert scaling_policy.current_size == initial_size + 1
    assert len(scaling_policy.scaling_events) == 1

    time.sleep(1) # After cooldown
    scaling_policy.check_and_scale()
    assert scaling_policy.current_size == initial_size + 1
    assert len(scaling_policy.scaling_events) == 1
