/**
 * @file test_hardware_validation.c
 * @brief Hardware Validation Test Framework
 */

#include "mvgal/mvgal.h"
#include "mvgal/mvgal_log.h"
#include "mvgal/mvgal_shader_backend.h"
#include "mvgal/mvgal_cmd_dag.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

/* Forward declare Vulkan function for linking */
typedef struct VkInstance_T* VkInstance;

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)

static void test_gpu_detection(void)
{
    MVGAL_LOG_INFO("--- GPU Detection ---");

    int32_t gpu_count = mvgal_gpu_get_count();
    TEST_ASSERT(gpu_count >= 1, "At least one GPU detected");
    TEST_ASSERT(gpu_count >= 2, "Cross-vendor setup detected");

    MVGAL_LOG_INFO("Detected %d GPU(s)", gpu_count);
}

static void test_gpu_descriptors(void)
{
    MVGAL_LOG_INFO("--- GPU Descriptor Test ---");

    int32_t gpu_count = mvgal_gpu_get_count();
    int amd_count = 0, nvidia_count = 0, mtt_count = 0;

    for (int32_t i = 0; i < gpu_count; i++) {
        mvgal_gpu_descriptor_t desc;
        if (mvgal_gpu_get_descriptor((uint32_t)i, &desc) == MVGAL_SUCCESS) {
            TEST_ASSERT(true, "GPU descriptor query succeeds");
            
            switch (desc.vendor) {
            case MVGAL_VENDOR_AMD: amd_count++; break;
            case MVGAL_VENDOR_NVIDIA: nvidia_count++; break;
            case MVGAL_VENDOR_MOORE_THREADS: mtt_count++; break;
            case MVGAL_VENDOR_UNKNOWN:
            case MVGAL_VENDOR_INTEL:
            case MVGAL_VENDOR_QUALCOMM:
            case MVGAL_VENDOR_ARM:
            case MVGAL_VENDOR_BROADCOM:
                break;
            }
        }
    }

    MVGAL_LOG_INFO("AMD: %d, NVIDIA: %d, MTT: %d", amd_count, nvidia_count, mtt_count);
}

static void test_p2p_detection(void)
{
    MVGAL_LOG_INFO("--- P2P Capabilities Test ---");

    int32_t gpu_count = mvgal_gpu_get_count();

    for (int32_t i = 0; i < gpu_count; i++) {
        for (int32_t j = 0; j < gpu_count; j++) {
            if (i != j) {
                bool supported = mvgal_unified_heap_p2p_supported((uint32_t)i, (uint32_t)j);
                MVGAL_LOG_INFO("P2P %d -> %d: %s", i, j, supported ? "supported" : "not supported");
            }
        }
    }

    TEST_ASSERT(true, "P2P matrix computed");
}

static void test_vulkan_layers_available(void)
{
    MVGAL_LOG_INFO("--- Vulkan Layer Availability ---");

    int32_t mvgal_gpus = mvgal_gpu_get_count();
    TEST_ASSERT(mvgal_gpus >= 1, "MVGAL enumerates at least one GPU");

    /* Pass-through default: stock Vulkan may expose one device per process. */
    FILE *fp = popen("vulkaninfo --summary 2>/dev/null | grep -c 'deviceName'", "r");
    if (fp) {
        int vk_count = 0;
        char buf[32] = {0};
        if (fgets(buf, sizeof(buf), fp)) {
            vk_count = atoi(buf);
        }
        pclose(fp);
        MVGAL_LOG_INFO("Vulkan devices visible to vulkaninfo: %d (MVGAL GPUs: %d)",
                       vk_count, mvgal_gpus);
        TEST_ASSERT(vk_count >= 1, "At least one Vulkan device available");
    } else {
        MVGAL_LOG_INFO("vulkaninfo not available; skipping loader device count");
        TEST_ASSERT(true, "Vulkan probe skipped");
    }

    FILE *layer_fp = popen(
        "vulkaninfo --summary 2>/dev/null | grep -c 'VK_LAYER_MVGAL'", "r");
    if (layer_fp) {
        int layer_hits = 0;
        char lbuf[32] = {0};
        if (fgets(lbuf, sizeof(lbuf), layer_fp)) {
            layer_hits = atoi(lbuf);
        }
        pclose(layer_fp);
        if (layer_hits > 0) {
            TEST_ASSERT(true, "VK_LAYER_MVGAL registered with loader");
        } else {
            MVGAL_LOG_INFO("VK_LAYER_MVGAL not in vulkaninfo output (may be uninstalled)");
            TEST_ASSERT(true, "Vulkan layer check non-fatal");
        }
    }
}

static void test_spirv_translation(void)
{
    MVGAL_LOG_INFO("--- SPIR-V Translation Test ---");

    /* Minimal valid SPIR-V header */
    uint32_t spirv_test[] = {
        0x07230203,  /* Magic number: SPIR-V 1.0 */
        0x00010000,  /* Version: 1.0 */
        0,           /* Generator (placeholder) */
        0,           /* ID bound */
    };

    char *source = NULL;
    size_t size = 0;

    /* Test AMD GLSL translation */
    int rc = mvgal_compile_shader(MVGAL_VENDOR_AMD, spirv_test, 4, &source, &size);
    TEST_ASSERT(rc == -1 || source != NULL, "AMD shader translation callable");

    /* Test NVIDIA GLSL translation */
    rc = mvgal_compile_shader(MVGAL_VENDOR_NVIDIA, spirv_test, 4, &source, &size);
    TEST_ASSERT(rc == -1 || source != NULL, "NVIDIA shader translation callable");

    /* Test Intel GLSL translation */
    rc = mvgal_compile_shader(MVGAL_VENDOR_INTEL, spirv_test, 4, &source, &size);
    TEST_ASSERT(rc == -1 || source != NULL, "Intel shader translation callable");
}

static void test_dag_operations(void)
{
    MVGAL_LOG_INFO("--- DAG Operations Test ---");

    mvgal_cmd_dag_t* dag = mvgal_dag_create();
    TEST_ASSERT(dag != NULL, "DAG creation in hardware context");

    mvgal_record_cmd_draw(dag, 1000, 1);
    TEST_ASSERT(dag->node_count >= 1, "Draw recorded in DAG");

    mvgal_record_cmd_dispatch(dag, 64, 1, 1);
    TEST_ASSERT(dag->node_count >= 2, "Dispatch recorded in DAG");

    mvgal_dag_destroy(dag);
    TEST_ASSERT(true, "DAG destruction succeeds");
}

static void test_cross_vendor_matrix(void)
{
    MVGAL_LOG_INFO("--- Cross-Vendor Validation Matrix ---");

    int32_t gpu_count = mvgal_gpu_get_count();
    int cross_vendor_found = 0;

    for (int32_t i = 0; i < gpu_count && i < 4; i++) {
        mvgal_gpu_descriptor_t desc_i, desc_j;
        if (mvgal_gpu_get_descriptor((uint32_t)i, &desc_i) == MVGAL_SUCCESS) {
            for (int32_t j = i + 1; j < gpu_count && j < 4; j++) {
                if (mvgal_gpu_get_descriptor((uint32_t)j, &desc_j) == MVGAL_SUCCESS) {
                    if (desc_i.vendor != desc_j.vendor) {
                        cross_vendor_found = 1;
                        MVGAL_LOG_INFO("Cross-vendor pair found: vendor 0x%04x and 0x%04x",
                            (unsigned)desc_i.vendor, (unsigned)desc_j.vendor);
                    }
                }
            }
        }
    }

    TEST_ASSERT(cross_vendor_found >= 1, "Cross-vendor GPU pair detected");
}

int main(void)
{
    printf("\n============================================================\n");
    printf("  MVGAL Hardware Validation Tests\n");
    printf("============================================================\n\n");

    printf("NOTE: These tests validate hardware capabilities.\n");
    printf("Run on systems with multiple GPUs for full coverage.\n\n");

    if (mvgal_init(0) != MVGAL_SUCCESS) {
        MVGAL_LOG_ERROR("Failed to initialize MVGAL");
        return EXIT_FAILURE;
    }

    test_gpu_detection();
    test_gpu_descriptors();
    test_p2p_detection();
    test_vulkan_layers_available();
    test_spirv_translation();
    test_dag_operations();
    test_cross_vendor_matrix();

    mvgal_shutdown();

    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  (Note: Some tests may be skipped if specific GPU types not present)\n");
    printf("============================================================\n\n");

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