/**
 * @file test_wow64.c
 * @brief WoW64 Thunk Layer Unit Tests
 *
 * Tests:
 *   - mvgal_wow64_init()/shutdown() lifecycle
 *   - Pointer conversion functions
 *   - Instance creation/destruction
 *   - Device creation/destruction
 *   - Buffer allocation/mapping
 *   - Vulkan thunk functions
 */

#include "mvgal/mvgal.h"
#include "mvgal/mvgal_wow64.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("  ✓ %s", message); } \
         else { test_failed++; MVGAL_LOG_ERROR("  ✗ %s", message); } \
    } while (0)

/* Forward declarations for internal access in tests */
extern mvgal_wow64_instance_t* mvgal_wow64_create_instance(void);
extern mvgal_wow64_device_t* mvgal_wow64_create_device(mvgal_wow64_instance_t* instance, uint32_t device_idx);
extern mvgal_wow64_buffer_t* mvgal_wow64_create_buffer(mvgal_wow64_device_t* device, uint64_t size, uint32_t flags);

/* ------------------------------------------------------------------ */
/*  Pointer Conversion Tests                                          */
/* ------------------------------------------------------------------ */

static void test_ptr_conversion_roundtrip(void)
{
    MVGAL_LOG_INFO("--- Pointer conversion roundtrip ---");

    /* Test 32-bit pointer conversions */
    void *original_ptr = (void *)0x12345678;
    uint32_t ptr32 = mvgal_wow64_ptr64_to_ptr32(original_ptr);
    void *converted_ptr = mvgal_wow64_ptr32_to_ptr64(ptr32);

    TEST_ASSERT(converted_ptr == original_ptr, "ptr64_to_ptr32 roundtrip preserves value");

    /* Test with NULL */
    TEST_ASSERT(mvgal_wow64_ptr64_to_ptr32(NULL) == 0, "ptr64_to_ptr32(NULL) == 0");
    TEST_ASSERT(mvgal_wow64_ptr32_to_ptr64(0) == NULL, "ptr32_to_ptr64(0) == NULL");
}

/* ------------------------------------------------------------------ */
/*  WoW64 State Tests                                                 */
/* ------------------------------------------------------------------ */

static void test_init_shutdown(void)
{
    MVGAL_LOG_INFO("--- Init / shutdown ---");

    /* Ensure clean state */
    mvgal_wow64_shutdown();

    bool ok = mvgal_wow64_init();
    TEST_ASSERT(ok, "mvgal_wow64_init returns true");

    /* Double init should be safe */
    ok = mvgal_wow64_init();
    TEST_ASSERT(ok, "mvgal_wow64_init is idempotent");

    mvgal_wow64_shutdown();
}

/* ------------------------------------------------------------------ */
/*  Instance Tests                                                    */
/* ------------------------------------------------------------------ */

static void test_instance_create_destroy(void)
{
    MVGAL_LOG_INFO("--- Instance create/destroy ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_wow64_instance_t *inst = mvgal_wow64_create_instance();
    TEST_ASSERT(inst != NULL, "mvgal_wow64_create_instance returns non-NULL");

    /* Destroy should not crash */
    mvgal_wow64_destroy_instance(inst);
    TEST_ASSERT(true, "mvgal_wow64_destroy_instance does not crash");
}

static void test_instance_multiple(void)
{
    MVGAL_LOG_INFO("--- Multiple instances ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_wow64_instance_t *inst1 = mvgal_wow64_create_instance();
    mvgal_wow64_instance_t *inst2 = mvgal_wow64_create_instance();

    TEST_ASSERT(inst1 != NULL && inst2 != NULL, "Two instances can be created");
    TEST_ASSERT(inst1 != inst2, "Instances are distinct objects");

    mvgal_wow64_destroy_instance(inst1);
    mvgal_wow64_destroy_instance(inst2);
}

/* ------------------------------------------------------------------ */
/*  Device Tests                                                      */
/* ------------------------------------------------------------------ */

static void test_device_create_destroy(void)
{
    MVGAL_LOG_INFO("--- Device create/destroy ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_wow64_instance_t *inst = mvgal_wow64_create_instance();
    TEST_ASSERT(inst != NULL, "Instance created for device test");

    mvgal_wow64_device_t *dev = mvgal_wow64_create_device(inst, 0);
    TEST_ASSERT(dev != NULL, "mvgal_wow64_create_device returns non-NULL");

    mvgal_wow64_destroy_device(dev);
    mvgal_wow64_destroy_instance(inst);
}

static void test_device_multiple_gpus(void)
{
    MVGAL_LOG_INFO("--- Multiple GPU devices ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_wow64_instance_t *inst = mvgal_wow64_create_instance();
    TEST_ASSERT(inst != NULL, "Instance created");

    mvgal_wow64_device_t *dev0 = mvgal_wow64_create_device(inst, 0);
    mvgal_wow64_device_t *dev1 = mvgal_wow64_create_device(inst, 1);

    TEST_ASSERT(dev0 != NULL && dev1 != NULL, "Devices for multiple GPUs created");
    TEST_ASSERT(dev0 != dev1, "Devices are distinct objects");

    mvgal_wow64_destroy_device(dev0);
    mvgal_wow64_destroy_device(dev1);
    mvgal_wow64_destroy_instance(inst);
}

/* ------------------------------------------------------------------ */
/*  Buffer Tests                                                      */
/* ------------------------------------------------------------------ */

static void test_buffer_create_destroy(void)
{
    MVGAL_LOG_INFO("--- Buffer create/destroy ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_wow64_instance_t *inst = mvgal_wow64_create_instance();
    TEST_ASSERT(inst != NULL, "Instance created for buffer test");

    mvgal_wow64_device_t *dev = mvgal_wow64_create_device(inst, 0);
    TEST_ASSERT(dev != NULL, "Device created for buffer test");

    mvgal_wow64_buffer_t *buf = mvgal_wow64_create_buffer(dev, 4096, 0);
    TEST_ASSERT(buf != NULL, "mvgal_wow64_create_buffer returns non-NULL");

    mvgal_wow64_destroy_buffer(buf);
    mvgal_wow64_destroy_device(dev);
    mvgal_wow64_destroy_instance(inst);
}

static void test_buffer_mapping(void)
{
    MVGAL_LOG_INFO("--- Buffer mapping ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_wow64_instance_t *inst = mvgal_wow64_create_instance();
    mvgal_wow64_device_t *dev = mvgal_wow64_create_device(inst, 0);
    mvgal_wow64_buffer_t *buf = mvgal_wow64_create_buffer(dev, 8192, 0);
    TEST_ASSERT(buf != NULL, "Buffer created for mapping test");

    uint32_t ptr32 = 0;
    int result = mvgal_wow64_map_buffer(buf, 0, 4096, &ptr32);
    TEST_ASSERT(result == 0, "mvgal_wow64_map_buffer succeeds");
    TEST_ASSERT(ptr32 != 0, "Mapped pointer is non-zero");

    /* Unmap through destroy */
    mvgal_wow64_destroy_buffer(buf);
    mvgal_wow64_destroy_device(dev);
    mvgal_wow64_destroy_instance(inst);
}

static void test_buffer_mapping_partial(void)
{
    MVGAL_LOG_INFO("--- Buffer partial mapping ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_wow64_instance_t *inst = mvgal_wow64_create_instance();
    mvgal_wow64_device_t *dev = mvgal_wow64_create_device(inst, 0);
    mvgal_wow64_buffer_t *buf = mvgal_wow64_create_buffer(dev, 16384, 0);
    TEST_ASSERT(buf != NULL, "Buffer created for partial mapping test");

    /* Map partial range (bytes 4096-8192) */
    uint32_t ptr32 = 0;
    int result = mvgal_wow64_map_buffer(buf, 4096, 4096, &ptr32);
    TEST_ASSERT(result == 0, "Partial mapping succeeds");

    mvgal_wow64_destroy_buffer(buf);
    mvgal_wow64_destroy_device(dev);
    mvgal_wow64_destroy_instance(inst);
}

/* ------------------------------------------------------------------ */
/*  Vulkan Thunk Tests                                                */
/* ------------------------------------------------------------------ */

static void test_vkCreateInstance_thunk(void)
{
    MVGAL_LOG_INFO("--- vkCreateInstance thunk ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    VkInstanceCreateInfo32 createInfo = {0};
    createInfo.sType = 1; /* VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO */
    createInfo.flags = 0;
    createInfo.applicationInfo = 0;

    uint32_t instance = 0;
    int result = mvgal_wow64_vkCreateInstance(&createInfo, NULL, &instance);
    /* Result may fail due to no Vulkan loader, but should not crash */
    /* In a real test, we'd have a mock Vulkan loader available */
    if (result == 0) {
        TEST_ASSERT(instance != 0, "vkCreateInstance returned non-zero handle");
        mvgal_wow64_vkDestroyInstance(instance, NULL);
    } else {
        TEST_ASSERT(true, "vkCreateInstance handled gracefully without Vulkan loader");
    }
}

/* ------------------------------------------------------------------ */
/*  Gaming-Specific Tests                                               */
/* ------------------------------------------------------------------ */

static void test_gaming_context_detection(void)
{
    MVGAL_LOG_INFO("--- Gaming context detection ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    /* Test Steam App ID detection */
    uint32_t app_id = mvgal_wow64_detect_steam_app_id();
    MVGAL_LOG_INFO("  Current Steam App ID: %u", app_id);
    
    /* Game profile lookup (will use default if no specific profile) */
    mvgal_workload_type_t workload = MVGAL_WORKLOAD_GRAPHICS;
    mvgal_distribution_strategy_t strategy = mvgal_wow64_get_game_profile(app_id, &workload);
    MVGAL_LOG_INFO("  Game profile strategy: %d", strategy);
    
    /* Check frame pacing capability - returns false if < 2 GPUs */
    bool can_pace = mvgal_wow64_can_frame_pace();
    MVGAL_LOG_INFO("  Frame pacing capability: %s", can_pace ? "true" : "false");
    /* In test environment with stubbed GPU count, this may be false */
    
    mvgal_wow64_shutdown();
}

static void test_frame_pacing_api(void)
{
    MVGAL_LOG_INFO("--- Frame pacing API ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_frame_pacing_config_t pacing_config = {0};
    pacing_config.enabled = true;
    pacing_config.max_latency_ns = 16000000ULL; /* 16ms */
    pacing_config.target_fps = 60;

    bool ok = mvgal_wow64_enable_frame_pacing_with_config(&pacing_config);
    TEST_ASSERT(ok, "Frame pacing enable returns true");

    /* Test simple enable function too */
    mvgal_wow64_enable_frame_pacing(true);

    /* Estimate frame timing */
    uint64_t frame_time_ns = mvgal_wow64_estimate_frame_time_ns(0, 1);
    MVGAL_LOG_INFO("  Estimated frame time: %lu ns", frame_time_ns);
    TEST_ASSERT(frame_time_ns > 0, "Frame time estimation produces positive value");
}

static void test_gaming_gpu_mask(void)
{
    MVGAL_LOG_INFO("--- Gaming GPU mask selection ---");

    if (!mvgal_wow64_init()) {
        TEST_ASSERT(false, "mvgal_wow64_init failed");
        return;
    }

    mvgal_wow64_config_t config = {0};
    config.context_type = MVGAL_WOW64_CONTEXT_GAMING;
    config.strategy = MVGAL_STRATEGY_AFR;
    config.gpu_mask = 0x3; /* Explicitly set to 2 GPUs */
    
    bool ok = mvgal_wow64_update_config(&config);
    TEST_ASSERT(ok, "Config update succeeds");
    
    uint64_t gpu_mask = mvgal_wow64_get_gpu_mask();
    MVGAL_LOG_INFO("  Gaming GPU mask: 0x%lx", gpu_mask);
    TEST_ASSERT(gpu_mask == 0x3, "GPU mask matches config");
    
    mvgal_wow64_shutdown();
}

/* ------------------------------------------------------------------ */
/*  main                                                              */
/* ------------------------------------------------------------------ */

int main(void)
{
    printf("\n============================================================\n");
    printf("  MVGAL WoW64 Thunk Layer Unit Tests\n");
    printf("============================================================\n\n");

    test_ptr_conversion_roundtrip();
    test_init_shutdown();
    test_instance_create_destroy();
    test_instance_multiple();
    test_device_create_destroy();
    test_device_multiple_gpus();
    test_buffer_create_destroy();
    test_buffer_mapping();
    test_buffer_mapping_partial();
    test_vkCreateInstance_thunk();
    test_gaming_context_detection();
    test_frame_pacing_api();
    test_gaming_gpu_mask();

    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;
}