import unittest
import time
import aiva.config as config  # Assuming aiva.config is where the hysteresis logic lives

# Mock CPU utilization function (replace with actual system call)
def mock_cpu_utilization():
    # Simulate different workload patterns
    current_time = time.time()
    if current_time % 60 < 20:  # 20 seconds of stable load
        return 30 + (current_time % 2)  # Small fluctuations around 30%
    elif current_time % 60 < 40: # 20 seconds of bursty load
        return 10 + (current_time % 10) * 7 # Bursts from 10% to 70%
    else: # 20 seconds of fluctuating load
        return 50 + (current_time % 20) * 2 # Fluctuations from 50% to 90%

class TestAdaptiveHysteresis(unittest.TestCase):

    def setUp(self):
        # Store original config values
        self.original_hysteresis_threshold = config.HYSTERESIS_THRESHOLD
        self.original_hysteresis_cooldown = config.HYSTERESIS_COOLDOWN

        # Reset scaling state for each test
        config.scaling_state = False  # Initialize scaling state

    def tearDown(self):
        # Restore original config values
        config.HYSTERESIS_THRESHOLD = self.original_hysteresis_threshold
        config.HYSTERESIS_COOLDOWN = self.original_hysteresis_cooldown

    def test_stable_load(self):
        config.HYSTERESIS_THRESHOLD = 50
        config.HYSTERESIS_COOLDOWN = 5
        start_time = time.time()
        scale_count = 0
        for i in range(10):
            cpu_utilization = mock_cpu_utilization()
            if cpu_utilization > config.HYSTERESIS_THRESHOLD and not config.scaling_state:
                config.scaling_state = True
                scale_count += 1
                print(f"Scaling up at {time.time() - start_time:.2f}s, CPU: {cpu_utilization:.2f}% (Stable)")
                time.sleep(config.HYSTERESIS_COOLDOWN) # Simulate cooldown
            elif cpu_utilization < config.HYSTERESIS_THRESHOLD - 10 and config.scaling_state:
                config.scaling_state = False
                scale_count -= 1
                print(f"Scaling down at {time.time() - start_time:.2f}s, CPU: {cpu_utilization:.2f}% (Stable)")
                time.sleep(config.HYSTERESIS_COOLDOWN)
            time.sleep(0.5)
        print(f"Stable Load: Scale Count = {scale_count}")
        self.assertLessEqual(scale_count, 3) # Expect limited scaling

    def test_bursty_load(self):
        config.HYSTERESIS_THRESHOLD = 50
        config.HYSTERESIS_COOLDOWN = 2
        start_time = time.time()
        scale_count = 0
        for i in range(10):
            cpu_utilization = mock_cpu_utilization()
            if cpu_utilization > config.HYSTERESIS_THRESHOLD and not config.scaling_state:
                config.scaling_state = True
                scale_count += 1
                print(f"Scaling up at {time.time() - start_time:.2f}s, CPU: {cpu_utilization:.2f}% (Bursty)")
                time.sleep(config.HYSTERESIS_COOLDOWN) # Simulate cooldown
            elif cpu_utilization < config.HYSTERESIS_THRESHOLD - 10 and config.scaling_state:
                config.scaling_state = False
                scale_count -= 1
                print(f"Scaling down at {time.time() - start_time:.2f}s, CPU: {cpu_utilization:.2f}% (Bursty)")
                time.sleep(config.HYSTERESIS_COOLDOWN)
            time.sleep(0.5)
        print(f"Bursty Load: Scale Count = {scale_count}")
        self.assertGreaterEqual(scale_count, 1) # Expect some scaling
        self.assertLessEqual(scale_count, 5) # Expect not too much scaling

    def test_fluctuating_load(self):
        config.HYSTERESIS_THRESHOLD = 50
        config.HYSTERESIS_COOLDOWN = 1
        start_time = time.time()
        scale_count = 0
        for i in range(10):
            cpu_utilization = mock_cpu_utilization()
            if cpu_utilization > config.HYSTERESIS_THRESHOLD and not config.scaling_state:
                config.scaling_state = True
                scale_count += 1
                print(f"Scaling up at {time.time() - start_time:.2f}s, CPU: {cpu_utilization:.2f}% (Fluctuating)")
                time.sleep(config.HYSTERESIS_COOLDOWN) # Simulate cooldown
            elif cpu_utilization < config.HYSTERESIS_THRESHOLD - 10 and config.scaling_state:
                config.scaling_state = False
                scale_count -= 1
                print(f"Scaling down at {time.time() - start_time:.2f}s, CPU: {cpu_utilization:.2f}% (Fluctuating)")
                time.sleep(config.HYSTERESIS_COOLDOWN)
            time.sleep(0.5)
        print(f"Fluctuating Load: Scale Count = {scale_count}")
        self.assertGreaterEqual(scale_count, 2) # Expect more frequent scaling

if __name__ == '__main__':
    unittest.main()
