/**
 * @file test_mvgal_ai_features.c
 * @brief AI Features extraction & dispatch integration test
 *
 * Validates feature extraction from scheduler state, prediction
 * dispatch logic, and lifecycle helpers.
 */

#include "mvgal/mvgal_ai.h"
#include "mvgal/mvgal_scheduler.h"
#include "scheduler_internal.h"
#include "mvgal/mvgal_types.h"
#include "mvgal/mvgal_gpu.h"
#include "mvgal_ai_features.h"
#include "mvgal/mvgal_log.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

#define TEST_ASSERT(condition, message) \
    do { if (condition) { test_passed++; MVGAL_LOG_INFO("  \xe2\x9c\x93 %s", message); } \
         else { test_failed++; MVGAL_LOG_ERROR("  \xe2\x9c\x97 %s", message); } \
    } while (0)

/* ── Helpers ──────────────────────────────────────────────────────────── */

static void fill_mock_scheduler_state(mvgal_scheduler_state_t *state, int num_gpus)
{
    memset(state, 0, sizeof(*state));

    state->config.strategy = MVGAL_STRATEGY_AI_DRIVEN;
    state->gpu_count = (uint32_t)num_gpus;

    for (int i = 0; i < num_gpus && i < 8; i++) {
        state->gpus[i].utilization       = 0.3f + (float)i * 0.15f;
        state->gpus[i].temperature       = 55.0f + (float)i * 8.0f;
        state->gpus[i].power_usage_w     = 120.0f + (float)i * 30.0f;
        state->gpus[i].compute_score     = 1000.0f - (float)i * 100.0f;
        state->gpus[i].graphics_score    = 800.0f - (float)i * 50.0f;

        state->gpus[i].gpu_info.vram_total = (8192ULL - (uint64_t)i * 1024ULL) * 1024ULL * 1024ULL;
        state->gpus[i].gpu_info.vram_free  = (4096ULL - (uint64_t)i * 512ULL) * 1024ULL * 1024ULL;
        state->gpus[i].gpu_info.vram_used  = state->gpus[i].gpu_info.vram_total - state->gpus[i].gpu_info.vram_free;
    }
}

/* ── Tests ────────────────────────────────────────────────────────────── */

static void test_lifecycle_ready(void)
{
    MVGAL_LOG_INFO("--- Lifecycle: ready state ---");

    /* Without loading a model, should report not ready */
    bool ready = mvgal_ai_features_ready();
    TEST_ASSERT(ready == false, "AI features not ready without model");
}

static void test_feature_extraction(void)
{
    MVGAL_LOG_INFO("--- Feature extraction ---");

    mvgal_scheduler_state_t sched_state;
    fill_mock_scheduler_state(&sched_state, 4);

    mvgal_workload_descriptor_t workload;
    memset(&workload, 0, sizeof(workload));
    workload.type = MVGAL_WORKLOAD_COMPUTE;
    workload.estimated_duration_ns = 1000000;
    workload.memory_required = 512;

    int rc = mvgal_ai_features_dispatch(NULL, &workload);
    TEST_ASSERT(rc != 0, "Dispatch with NULL state fails");

    /* dispatch handles NULL workload gracefully (falls back to defaults) */
    rc = mvgal_ai_features_dispatch(&sched_state, NULL);
    TEST_ASSERT(rc == 0, "Dispatch with NULL workload is valid fallback");
}

static void test_feature_extraction_null_checks(void)
{
    MVGAL_LOG_INFO("--- Feature extraction NULL checks ---");

    int rc = mvgal_ai_features_extract(NULL, NULL, NULL);
    TEST_ASSERT(rc != 0, "Extract with all NULLs fails");

    mvgal_scheduler_state_t sched_state;
    fill_mock_scheduler_state(&sched_state, 2);

    mvgal_workload_descriptor_t workload;
    memset(&workload, 0, sizeof(workload));
    workload.type = MVGAL_WORKLOAD_COMPUTE;

    mvgal_ai_features_t features;
    rc = mvgal_ai_features_extract(NULL, &workload, &features);
    TEST_ASSERT(rc != 0, "Extract with NULL state fails");

    /* extract handles NULL workload gracefully (uses defaults) */
    rc = mvgal_ai_features_extract(&sched_state, NULL, &features);
    TEST_ASSERT(rc == 0, "Extract with NULL workload is valid fallback");

    rc = mvgal_ai_features_extract(&sched_state, &workload, NULL);
    TEST_ASSERT(rc != 0, "Extract with NULL features out fails");
}

static void test_prediction_no_model(void)
{
    MVGAL_LOG_INFO("--- Prediction with no model ---");

    mvgal_ai_prediction_t pred;
    memset(&pred, 0, sizeof(pred));
    /* Without a loaded model, prediction should fail gracefully */
    int rc = mvgal_ai_model_predict(0, NULL, NULL);
    TEST_ASSERT(rc != 0, "Predict with NULL model fails");

    mvgal_ai_features_t features;
    memset(&features, 0, sizeof(features));

    rc = mvgal_ai_model_predict(0, &features, &pred);
    TEST_ASSERT(rc != 0, "Predict with invalid model handle fails");
}

static void test_prediction_null_checks(void)
{
    MVGAL_LOG_INFO("--- Prediction NULL checks ---");

    mvgal_ai_features_t features;
    memset(&features, 0, sizeof(features));

    mvgal_ai_prediction_t pred;
    int rc = mvgal_ai_model_predict(0, NULL, &pred);
    TEST_ASSERT(rc != 0, "Predict with NULL features fails");

    rc = mvgal_ai_model_predict(0, &features, NULL);
    TEST_ASSERT(rc != 0, "Predict with NULL output fails");
}

static void test_dispatch_null_safety(void)
{
    MVGAL_LOG_INFO("--- Dispatch NULL safety ---");

    int rc = mvgal_ai_features_dispatch(NULL, NULL);
    TEST_ASSERT(rc != 0, "Dispatch with all NULLs fails");

    mvgal_workload_descriptor_t workload;
    memset(&workload, 0, sizeof(workload));
    workload.type = MVGAL_WORKLOAD_COMPUTE;

    rc = mvgal_ai_features_dispatch(NULL, &workload);
    TEST_ASSERT(rc != 0, "Dispatch with NULL state fails");
}

static void test_dispatch_fallback(void)
{
    MVGAL_LOG_INFO("--- Dispatch fallback ---");

    mvgal_scheduler_state_t state;
    fill_mock_scheduler_state(&state, 3);

    mvgal_workload_descriptor_t workload;
    memset(&workload, 0, sizeof(workload));
    workload.type       = MVGAL_WORKLOAD_COMPUTE;
    workload.memory_required     = 512;
    workload.estimated_duration_ns = 1000000;

    /* Without AI model loaded, dispatch should fall back to heuristic */
    int gpu = mvgal_ai_features_dispatch(&state, &workload);
    TEST_ASSERT(gpu >= 0, "Dispatch returned a valid GPU index");
    TEST_ASSERT(gpu < (int)state.gpu_count, "Dispatch returned GPU within range");
}

static void test_single_gpu_state(void)
{
    MVGAL_LOG_INFO("--- Single GPU state ---");

    mvgal_scheduler_state_t sched_state;
    fill_mock_scheduler_state(&sched_state, 1);

    mvgal_workload_descriptor_t workload;
    memset(&workload, 0, sizeof(workload));
    workload.type = MVGAL_WORKLOAD_GRAPHICS;
    workload.memory_required = 256;

    mvgal_ai_features_t features;
    int rc = mvgal_ai_features_extract(&sched_state, &workload, &features);
    TEST_ASSERT(rc == 0, "Single GPU extraction succeeds");
    TEST_ASSERT(features.num_gpus == 1, "Single GPU count");
    TEST_ASSERT(features.gpu_utilization[0] > 0.0f, "Single GPU utilization populated");
}

static void test_max_gpu_state(void)
{
    MVGAL_LOG_INFO("--- 8 GPU state ---");

    mvgal_scheduler_state_t sched_state;
    fill_mock_scheduler_state(&sched_state, 8);

    mvgal_workload_descriptor_t workload;
    memset(&workload, 0, sizeof(workload));
    workload.type = MVGAL_WORKLOAD_COMPUTE;

    mvgal_ai_features_t features;
    int rc = mvgal_ai_features_extract(&sched_state, &workload, &features);
    TEST_ASSERT(rc == 0, "8 GPU extraction succeeds");
    TEST_ASSERT(features.num_gpus == 8, "8 GPU count");

    /* Verify history encoding */
    bool history_populated = false;
    for (int i = 0; i < 16; i++) {
        if (features.workload_history[i] > 0 || features.workload_history[i] < 0) {
            history_populated = true;
            break;
        }
    }
    TEST_ASSERT(history_populated, "Workload history populated");
}

int main(void)
{
    printf("\n============================================================\n");
    printf("  MVGAL AI Features Integration Test\n");
    printf("============================================================\n\n");

    test_lifecycle_ready();
    test_feature_extraction();
    test_feature_extraction_null_checks();
    test_prediction_no_model();
    test_prediction_null_checks();
    test_dispatch_null_safety();
    test_dispatch_fallback();
    test_single_gpu_state();
    test_max_gpu_state();

    /* Summary */
    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");

    if (test_failed == 0) {
        printf("\n\xe2\x9c\x93 AI Features integration test PASSED\n\n");
    } else {
        printf("\n\xe2\x9c\x97 AI Features integration test FAILED\n\n");
    }

    return (test_failed > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
}
