/*
 * test_header_handling.c - Unit tests for header handling (truncation and limits)
 */

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <string.h>
#include <stdio.h>
#include <apr_strings.h>
#include <apr_tables.h>
#include <apr_pools.h>
#include <apr_general.h>

/* Mock header truncation function */
static char *truncate_header_value(apr_pool_t *pool, const char *value, int max_len)
{
    if (value == NULL) {
        return NULL;
    }
    
    size_t len = strlen(value);
    if ((int)len > max_len) {
        return apr_pstrmemdup(pool, value, max_len);
    }
    return apr_pstrdup(pool, value);
}

/* Mock header matching function */
static int header_name_matches(const char *configured, const char *actual)
{
    return strcasecmp(configured, actual) == 0;
}

/* Test: Header value within limit */
static void test_header_truncation_within_limit(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    const char *value = "short value";
    char *result = truncate_header_value(pool, value, 256);
    
    assert_string_equal(result, "short value");
    
    apr_pool_destroy(pool);
}

/* Test: Header value exactly at limit */
static void test_header_truncation_exact_limit(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    const char *value = "exactly10c";
    char *result = truncate_header_value(pool, value, 10);
    
    assert_string_equal(result, "exactly10c");
    
    apr_pool_destroy(pool);
}

/* Test: Header value exceeds limit */
static void test_header_truncation_exceeds_limit(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    const char *value = "this is a very long header value that should be truncated";
    char *result = truncate_header_value(pool, value, 15);
    
    assert_string_equal(result, "this is a very ");
    assert_int_equal(strlen(result), 15);
    
    apr_pool_destroy(pool);
}

/* Test: Header value with limit of 1 */
static void test_header_truncation_limit_one(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    const char *value = "abc";
    char *result = truncate_header_value(pool, value, 1);
    
    assert_string_equal(result, "a");
    
    apr_pool_destroy(pool);
}

/* Test: NULL header value */
static void test_header_truncation_null(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    char *result = truncate_header_value(pool, NULL, 256);
    
    assert_null(result);
    
    apr_pool_destroy(pool);
}

/* Test: Empty header value */
static void test_header_truncation_empty(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    const char *value = "";
    char *result = truncate_header_value(pool, value, 256);
    
    assert_string_equal(result, "");
    
    apr_pool_destroy(pool);
}

/* Test: Header name matching (case-insensitive) */
static void test_header_name_matching_case_insensitive(void **state)
{
    assert_true(header_name_matches("X-Request-Id", "x-request-id"));
    assert_true(header_name_matches("user-agent", "User-Agent"));
    assert_true(header_name_matches("HOST", "host"));
}

/* Test: Header name matching (different headers) */
static void test_header_name_matching_different(void **state)
{
    assert_false(header_name_matches("X-Request-Id", "X-Trace-Id"));
    assert_false(header_name_matches("Host", "User-Agent"));
}

/* Test: Multiple headers with limit */
static void test_header_count_limit(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    /* Simulate configured headers */
    const char *configured[] = {"X-Request-Id", "X-Trace-Id", "User-Agent", "Referer"};
    int configured_count = 4;
    int max_headers = 2;
    
    /* Simulate present headers */
    const char *present[] = {"X-Request-Id", "User-Agent", "Referer"};
    int present_count = 3;
    
    int logged_count = 0;
    for (int i = 0; i < configured_count && logged_count < max_headers; i++) {
        for (int j = 0; j < present_count; j++) {
            if (header_name_matches(configured[i], present[j])) {
                logged_count++;
                break;
            }
        }
    }
    
    assert_int_equal(logged_count, 2);
    
    apr_pool_destroy(pool);
}

/* Test: Header value with special JSON characters */
static void test_header_value_json_special(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    const char *value = "test\"value\\with\tspecial";
    char *truncated = truncate_header_value(pool, value, 256);
    
    /* Truncation should preserve the value */
    assert_string_equal(truncated, "test\"value\\with\tspecial");
    
    apr_pool_destroy(pool);
}

/* Test: Unicode in header value (UTF-8) */
static void test_header_value_unicode(void **state)
{
    apr_pool_t *pool;
    apr_pool_create(&pool, NULL);
    
    const char *value = "Mozilla/5.0 (compatible; 日本語)";
    char *result = truncate_header_value(pool, value, 50);
    
    /* Should be truncated but valid */
    assert_non_null(result);
    assert_true(strlen(result) <= 50);
    
    apr_pool_destroy(pool);
}

static int group_setup(void **state)
{
    (void)state;
    return apr_initialize();
}

static int group_teardown(void **state)
{
    (void)state;
    apr_terminate();
    return 0;
}

int main(void)
{
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test_header_truncation_within_limit),
        cmocka_unit_test(test_header_truncation_exact_limit),
        cmocka_unit_test(test_header_truncation_exceeds_limit),
        cmocka_unit_test(test_header_truncation_limit_one),
        cmocka_unit_test(test_header_truncation_null),
        cmocka_unit_test(test_header_truncation_empty),
        cmocka_unit_test(test_header_name_matching_case_insensitive),
        cmocka_unit_test(test_header_name_matching_different),
        cmocka_unit_test(test_header_count_limit),
        cmocka_unit_test(test_header_value_json_special),
        cmocka_unit_test(test_header_value_unicode),
    };
    
    return cmocka_run_group_tests(tests, group_setup, group_teardown);
}
