// Kyler Olsen
// YREA SLS
// String Tests
// November 2025

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

#include "sls/string.h"
#include "sls/lexer.h"
#include "sls/errors.h"
#include "tests/tests.h"

static const size_t NUM_STRING_TESTS = 10;

static TestResult pass_string_test(SlsStr test_name) {
    TestResult result = {.name = test_name, .status = TEST_NOT_IMPLEMENTED};
    result.status = TEST_PASS;
    return result;
}

static TestResult fail_string_test(SlsStr test_name, SlsStr message) {
    TestResult result = { .name = test_name, .status = TEST_NOT_IMPLEMENTED };
    result.status = TEST_ERROR;
    result.message = sls_str_cpy(message);

    if (result.message.str == NULL) {
        result.error = (SlsError){ SLS_STR("Out Of Memory Error."), 1 };
        result.status = TEST_ERROR;
    }
    return result;
}

// Test sls_str_malloc and sls_str_cpy
static TestResult test_malloc_and_copy() {
    const char* original = "Hello, SLS!";
    SlsStr s = sls_str_malloc(original, strlen(original));
    if (!s.str) return fail_string_test(SLS_STR("test_malloc_and_copy"), SLS_STR("Allocation failed"));

    if (strcmp(s.str, original) != 0) {
        sls_str_free(&s);
        return fail_string_test(SLS_STR("test_malloc_and_copy"), SLS_STR("Copied string mismatch"));
    }

    SlsStr copy = sls_str_cpy(s);
    if (!copy.str || strcmp(copy.str, original) != 0) {
        sls_str_free(&s);
        sls_str_free(&copy);
        return fail_string_test(SLS_STR("test_malloc_and_copy"), SLS_STR("sls_str_cpy failed"));
    }

    sls_str_free(&s);
    sls_str_free(&copy);
    return pass_string_test(SLS_STR("test_malloc_and_copy"));
}

// Test sls_str_cmp
static TestResult test_compare_strings() {
    SlsStr a = sls_str_malloc("abc", 3);
    SlsStr b = sls_str_malloc("abc", 3);
    SlsStr c = sls_str_malloc("abd", 3);

    if (sls_str_cmp(a, b) != 0) goto fail;
    if (sls_str_cmp(a, c) >= 0) goto fail;
    if (sls_str_cmp(c, a) <= 0) goto fail;

    sls_str_free(&a);
    sls_str_free(&b);
    sls_str_free(&c);
    return pass_string_test(SLS_STR("test_compare_strings"));

fail:
    sls_str_free(&a);
    sls_str_free(&b);
    sls_str_free(&c);
    return fail_string_test(SLS_STR("test_compare_strings"), SLS_STR("Comparison test failed"));
}

// Test sls_format for basic placeholders
static TestResult test_format_basic_placeholders() {
    SlsStr fmt = SLS_STR("Char: %c, Int: %d, Long: %l, Unsigned: %u, Size: %z, Float: %f, C-String: %y");
    const char *s = "Test";
    SlsStr result = sls_format(fmt, 'X', -42, (int64_t)1234567890123, (uint64_t)9876543210, (size_t)1024, 3.14, s);

    if (!result.str) return fail_string_test(SLS_STR("test_format_basic_placeholders"), SLS_STR("Formatting returned NULL"));

    const char* expected = "Char: X, Int: -42, Long: 1234567890123, Unsigned: 9876543210, Size: 1024, Float: 3.14, C-String: Test";
    if (strcmp(result.str, expected) != 0) {
        sls_str_free(&result);
        return fail_string_test(SLS_STR("test_format_basic_placeholders"), SLS_STR("Formatted string mismatch"));
    }

    sls_str_free(&result);
    return pass_string_test(SLS_STR("test_format_basic_placeholders"));
}

// Test sls_format for SLS types: %s, %t, %a, %i, %e, %b
static TestResult test_format_sls_types() {
    // Placeholder values
    SlsStr sls_str_val = SLS_STR("SLS_STRING");
    TokenType tok = 0;       // Assuming TOKEN_TYPES_NAMES[0] exists
    ArrayType arr = 0;       // Assuming ARRAY_TYPES_NAMES[0] exists
    IntegerBuiltInType ib = 0; // Assuming INTEGER_TYPES_NAMES[0] exists
    SlsError err = {SLS_STR("ErrorMessage"), 1};
    Boolean boolean_true = TRUE;

    SlsStr fmt = SLS_STR("Str:%s Tok:%t Arr:%a IntType:%i Err:%e Bool:%b");
    SlsStr result = sls_format(fmt, sls_str_val, tok, arr, ib, err, boolean_true);

    if (!result.str) return fail_string_test(SLS_STR("test_format_sls_types"), SLS_STR("Formatting returned NULL"));

    char expected[256];
    snprintf(expected, 256, "Str:%s Tok:%s Arr:%s IntType:%s Err:%s Bool:TRUE",
             sls_str_val.str,
             TOKEN_TYPES_NAMES[tok],
             ARRAY_TYPES_NAMES[arr],
             INTEGER_TYPES_NAMES[ib],
             err.message.str);

    if (strcmp(result.str, expected) != 0) {
        sls_str_free(&result);
        return fail_string_test(SLS_STR("test_format_sls_types"), SLS_STR("Formatted string mismatch for SLS types"));
    }

    sls_str_free(&result);
    return pass_string_test(SLS_STR("test_format_sls_types"));
}

// Test sls_format with %% escape
static TestResult test_format_percent_escape() {
    SlsStr fmt = SLS_STR("Progress: 100%% complete");
    SlsStr result = sls_format(fmt);

    if (!result.str) return fail_string_test(SLS_STR("test_format_percent_escape"), SLS_STR("Formatting returned NULL"));
    if (strcmp(result.str, "Progress: 100% complete") != 0) {
        sls_str_free(&result);
        return fail_string_test(SLS_STR("test_format_percent_escape"), SLS_STR("Percent escape failed"));
    }

    sls_str_free(&result);
    return pass_string_test(SLS_STR("test_format_percent_escape"));
}

// Test sls_str_new allocation and zero-init
static TestResult test_new_string_allocation() {
    SlsStr s = sls_str_new(10);
    if (!s.str) return fail_string_test(SLS_STR("test_new_string_allocation"), SLS_STR("Allocation failed"));

    for (size_t i = 0; i < s.len; i++) {
        if (s.str[i] != 0) {
            sls_str_free(&s);
            return fail_string_test(SLS_STR("test_new_string_allocation"), SLS_STR("Memory not zero-initialized"));
        }
    }

    sls_str_free(&s);
    return pass_string_test(SLS_STR("test_new_string_allocation"));
}

// Test empty string
static TestResult test_empty_string() {
    SlsStr s = sls_str_malloc("", 0);
    if (!s.str || s.len != 0) return fail_string_test(SLS_STR("test_empty_string"), SLS_STR("Empty string allocation failed"));
    sls_str_free(&s);
    return pass_string_test(SLS_STR("test_empty_string"));
}

// Test long string
static TestResult test_long_string() {
    size_t len = 1024;
    char* long_str = malloc(len + 1);
    for (size_t i = 0; i < len; i++) long_str[i] = 'A';
    long_str[len] = '\0';

    SlsStr s = sls_str_malloc(long_str, len);
    if (!s.str || s.len != len) {
        free(long_str);
        return fail_string_test(SLS_STR("test_long_string"), SLS_STR("Long string allocation failed"));
    }

    sls_str_free(&s);
    free(long_str);
    return pass_string_test(SLS_STR("test_long_string"));
}

// Test NULL pointer handling in sls_str_free
static TestResult test_free_null() {
    SlsStr s = SLS_STR_NULL;
    sls_str_free(&s); // Should safely do nothing
    return pass_string_test(SLS_STR("test_free_null"));
}

// Test sls_str_nlen edge cases
static TestResult test_str_nlen_edge() {
    const char* str = "ABCDE";
    if (sls_str_nlen(str, 0) != 0) return fail_string_test(SLS_STR("test_str_nlen_edge"), SLS_STR("Maxlen=0 failed"));
    if (sls_str_nlen(str, 3) != 3) return fail_string_test(SLS_STR("test_str_nlen_edge"), SLS_STR("Maxlen=3 failed"));
    if (sls_str_nlen(str, 10) != 5) return fail_string_test(SLS_STR("test_str_nlen_edge"), SLS_STR("Maxlen>strlen failed"));
    return pass_string_test(SLS_STR("test_str_nlen_edge"));
}

// Run all string tests
TestsReport run_string_tests() {
    TestsReport report = {
        .section = SLS_STR("string_tests"),
        .count = NUM_STRING_TESTS,
        .tests = malloc(sizeof(TestResult) * NUM_STRING_TESTS)
    };

    size_t i = 0;
    report.tests[i++] = test_malloc_and_copy();
    report.tests[i++] = test_compare_strings();
    report.tests[i++] = test_format_basic_placeholders();
    report.tests[i++] = test_format_sls_types();
    report.tests[i++] = test_format_percent_escape();
    report.tests[i++] = test_new_string_allocation();
    report.tests[i++] = test_empty_string();
    report.tests[i++] = test_long_string();
    report.tests[i++] = test_free_null();
    report.tests[i++] = test_str_nlen_edge();
    return report;
}
