/**
 * @file test_scheduler.c
 * @brief Scheduler Unit Tests
 */

#include "mvgal/mvgal.h"
#include "mvgal_scheduler.h"
#include "mvgal/mvgal_log.h"
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

static int test_passed = 0;
static int test_failed = 0;

#define TEST_ASSERT(condition, message) \
    do { if (condition) { test_passed++; } else { test_failed++; \
        MVGAL_LOG_ERROR("TEST FAILED: %s at %s:%d", message, __FILE__, __LINE__); \
    } } while (0)

static void test_scheduler_init(void)
{
    MVGAL_LOG_INFO("TEST: Scheduler Initialization");
    mvgal_error_t err = mvgal_init(0);
    TEST_ASSERT(err == MVGAL_SUCCESS, "Failed to initialize");
    
    // Scheduler is initialized as part of mvgal_init
    mvgal_shutdown();
    MVGAL_LOG_INFO("TEST: Scheduler Initialization - PASSED");
}

static void test_workload_creation(void)
{
    MVGAL_LOG_INFO("TEST: Workload Creation");
    mvgal_init(0);
    void *context = NULL; // Using void* as per scheduler API
    
    // Create and submit a simple workload
    mvgal_workload_submit_info_t info = {
        .type = MVGAL_WORKLOAD_COMPUTE,
        .priority = 50,
        .deadline = 0,
        .gpu_mask = 0xFFFFFFFF,
        .dependency_count = 0,
        .dependencies = NULL,
        .user_data = NULL
    };
    
    mvgal_workload_t workload;
    mvgal_error_t err = mvgal_workload_submit(context, &info, &workload);
    TEST_ASSERT(err == MVGAL_SUCCESS, "Failed to submit workload");
    TEST_ASSERT(workload != NULL, "Workload is NULL");
    
    // Wait for completion
    err = mvgal_workload_wait(workload, 1000);
    TEST_ASSERT(err == MVGAL_SUCCESS, "Failed to wait for workload");
    
    // Destroy workload
    mvgal_workload_destroy(workload);
    
    mvgal_shutdown();
    MVGAL_LOG_INFO("TEST: Workload Creation - PASSED");
}

static void test_strategy_selection(void)
{
    MVGAL_LOG_INFO("TEST: Strategy Selection");
    mvgal_init(0);
    mvgal_context_t context;
    mvgal_context_create(&context);
    
    // Test all strategies
    mvgal_distribution_strategy_t strategies[] = {
        MVGAL_STRATEGY_AFR,
        MVGAL_STRATEGY_SFR,
        MVGAL_STRATEGY_TASK,
        MVGAL_STRATEGY_COMPUTE_OFFLOAD,
        MVGAL_STRATEGY_HYBRID,
        MVGAL_STRATEGY_SINGLE_GPU,
        MVGAL_STRATEGY_AFR
    };
    
    for (size_t i = 0; i < sizeof(strategies)/sizeof(strategies[0]); i++) {
        mvgal_error_t err = mvgal_scheduler_set_strategy(context, strategies[i]);
        TEST_ASSERT(err == MVGAL_SUCCESS, "Failed to set strategy");
        
        mvgal_distribution_strategy_t current = mvgal_scheduler_get_strategy(context);
        TEST_ASSERT(current == strategies[i], "Strategy not set correctly");
    }
    
    mvgal_context_destroy(context);
    mvgal_shutdown();
    MVGAL_LOG_INFO("TEST: Strategy Selection - PASSED");
}

static void test_stats_tracking(void)
{
    MVGAL_LOG_INFO("TEST: Statistics Tracking");
    mvgal_init(0);
    mvgal_context_t context;
    mvgal_context_create(&context);
    
    // Get initial stats
    mvgal_stats_t stats;
    mvgal_error_t err = mvgal_get_stats(context, &stats);
    TEST_ASSERT(err == MVGAL_SUCCESS, "Failed to get stats");
    
    MVGAL_LOG_INFO("  Initial frames: %" PRIu64, stats.frames_submitted);
    
    mvgal_context_destroy(context);
    mvgal_shutdown();
    MVGAL_LOG_INFO("TEST: Statistics Tracking - PASSED");
}

static void test_gaming_strategy_auto_detect(void)
{
    MVGAL_LOG_INFO("TEST: Gaming Strategy Auto-Detect");
    mvgal_init(0);
    mvgal_context_t context;
    mvgal_context_create(&context);
    
    /* Test that gaming workloads get AFR strategy */
    mvgal_error_t err = mvgal_scheduler_set_strategy(context, MVGAL_STRATEGY_HYBRID);
    TEST_ASSERT(err == MVGAL_SUCCESS, "Failed to set hybrid strategy");
    
    /* Test compute workloads get compute offload */
    mvgal_workload_submit_info_t info = {
        .type = MVGAL_WORKLOAD_COMPUTE,
        .priority = 70,
        .gpu_mask = 0x3, /* GPU 0 and 1 */
    };
    
    mvgal_workload_t workload;
    err = mvgal_workload_submit(context, &info, &workload);
    TEST_ASSERT(err == MVGAL_SUCCESS, "Submit compute workload");
    
    mvgal_workload_destroy(workload);
    
    mvgal_context_destroy(context);
    mvgal_shutdown();
    MVGAL_LOG_INFO("TEST: Gaming Strategy Auto-Detect - PASSED");
}

static void test_sfr_for_vr_workloads(void)
{
    MVGAL_LOG_INFO("TEST: SFR Strategy for VR");
    
    if (mvgal_init(0) != MVGAL_SUCCESS) {
        MVGAL_LOG_WARN("  Skipping: MVGAL init failed");
        return;
    }
    
    int32_t gpu_count = mvgal_gpu_get_count();
    if (gpu_count < 2) {
        MVGAL_LOG_INFO("  Skipping: requires 2+ GPUs for SFR");
        mvgal_shutdown();
        return;
    }
    
    /* SFR is ideal for VR to minimize latency */
    mvgal_context_t context;
    mvgal_context_create(&context);
    
    mvgal_error_t err = mvgal_scheduler_set_strategy(context, MVGAL_STRATEGY_SFR);
    TEST_ASSERT(err == MVGAL_SUCCESS, "Set SFR strategy");
    
    mvgal_distribution_strategy_t strategy = mvgal_scheduler_get_strategy(context);
    TEST_ASSERT(strategy == MVGAL_STRATEGY_SFR, "SFR strategy confirmed");
    
    mvgal_context_destroy(context);
    mvgal_shutdown();
    MVGAL_LOG_INFO("TEST: SFR Strategy for VR - PASSED");
}

int main(void)
{
    printf("\n============================================================\n");
    printf("  MVGAL Scheduler Unit Tests\n");
    printf("============================================================\n\n");
    
    test_scheduler_init();
    test_workload_creation();
    test_strategy_selection();
    test_stats_tracking();
    test_gaming_strategy_auto_detect();
    test_sfr_for_vr_workloads();
    
    printf("\n============================================================\n");
    printf("  Test Summary\n");
    printf("============================================================\n");
    printf("  Passed: %d\n", test_passed);
    printf("  Failed: %d\n", test_failed);
    printf("  Total:  %d\n", test_passed + test_failed);
    printf("============================================================\n\n");
    
    return (test_failed > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
}
