// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Doubly Linked List Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "doubly_linked_list.h"
#include <assert.h>  /* for assert */
#include <stdint.h>  /* for SIZE_MAX */

/**
 * Test basic list creation and destruction
 */
static void test_create_destroy(void)
{
    struct doubly_linked_list *list = NULL;
    
    printf("Testing list creation and destruction...\n");
    
    list = dll_create();
    assert(NULL != list);
    assert(true == dll_is_empty(list));
    assert(0 == dll_get_size(list));
    
    dll_destroy(list);
    printf("✓ Create/destroy test passed\n\n");
}

/**
 * Test front insertion operations
 */
static void test_insert_front(void)
{
    struct doubly_linked_list *list = NULL;
    
    printf("Testing front insertion...\n");
    
    list = dll_create();
    assert(NULL != list);
    
    /* insert single element */
    assert(true == dll_insert_front(list, 10));
    assert(1 == dll_get_size(list));
    assert(false == dll_is_empty(list));
    
    /* insert multiple elements */
    assert(true == dll_insert_front(list, 20));
    assert(true == dll_insert_front(list, 30));
    assert(3 == dll_get_size(list));
    
    dll_print_forward(list);   /* should print: 30 <-> 20 <-> 10 */
    dll_print_backward(list);  /* should print: 10 <-> 20 <-> 30 */
    
    dll_destroy(list);
    printf("✓ Front insertion test passed\n\n");
}

/**
 * Test back insertion operations
 */
static void test_insert_back(void)
{
    struct doubly_linked_list *list = NULL;
    
    printf("Testing back insertion...\n");
    
    list = dll_create();
    assert(NULL != list);
    
    /* insert single element */
    assert(true == dll_insert_back(list, 10));
    assert(1 == dll_get_size(list));
    
    /* insert multiple elements */
    assert(true == dll_insert_back(list, 20));
    assert(true == dll_insert_back(list, 30));
    assert(3 == dll_get_size(list));
    
    dll_print_forward(list);   /* should print: 10 <-> 20 <-> 30 */
    dll_print_backward(list);  /* should print: 30 <-> 20 <-> 10 */
    
    dll_destroy(list);
    printf("✓ Back insertion test passed\n\n");
}

/**
 * Test insertion at specific positions
 */
static void test_insert_at(void)
{
    struct doubly_linked_list *list = NULL;
    
    printf("Testing insertion at specific positions...\n");
    
    list = dll_create();
    assert(NULL != list);
    
    /* insert at position 0 (empty list) */
    assert(true == dll_insert_at(list, 0, 10));
    assert(1 == dll_get_size(list));
    
    /* insert at end */
    assert(true == dll_insert_at(list, 1, 30));
    assert(2 == dll_get_size(list));
    
    /* insert in middle */
    assert(true == dll_insert_at(list, 1, 20));
    assert(3 == dll_get_size(list));
    
    /* test invalid position */
    assert(false == dll_insert_at(list, 10, 40));
    
    dll_print_forward(list);   /* should print: 10 <-> 20 <-> 30 */
    
    dll_destroy(list);
    printf("✓ Insert at position test passed\n\n");
}

/**
 * Test removal operations
 */
static void test_remove_operations(void)
{
    struct doubly_linked_list *list = NULL;
    
    printf("Testing removal operations...\n");
    
    list = dll_create();
    assert(NULL != list);
    
    /* populate list */
    dll_insert_back(list, 10);
    dll_insert_back(list, 20);
    dll_insert_back(list, 30);
    dll_insert_back(list, 40);
    dll_insert_back(list, 50);
    
    printf("Initial list: ");
    dll_print_forward(list);
    
    /* test remove front */
    assert(true == dll_remove_front(list));
    assert(4 == dll_get_size(list));
    printf("After remove front: ");
    dll_print_forward(list);
    
    /* test remove back */
    assert(true == dll_remove_back(list));
    assert(3 == dll_get_size(list));
    printf("After remove back: ");
    dll_print_forward(list);
    
    /* test remove at position */
    assert(true == dll_remove_at(list, 1));  /* remove middle element */
    assert(2 == dll_get_size(list));
    printf("After remove at position 1: ");
    dll_print_forward(list);
    
    /* test remove by data */
    assert(true == dll_remove_data(list, 20));
    assert(1 == dll_get_size(list));
    printf("After remove data 20: ");
    dll_print_forward(list);
    
    /* test remove from single element list */
    assert(true == dll_remove_front(list));
    assert(0 == dll_get_size(list));
    assert(true == dll_is_empty(list));
    
    dll_destroy(list);
    printf("✓ Removal operations test passed\n\n");
}

/**
 * Test search and get operations
 */
static void test_search_get(void)
{
    struct doubly_linked_list *list = NULL;
    int data = 0;
    size_t position = 0;
    
    printf("Testing search and get operations...\n");
    
    list = dll_create();
    assert(NULL != list);
    
    /* populate list */
    dll_insert_back(list, 100);
    dll_insert_back(list, 200);
    dll_insert_back(list, 300);
    
    /* test search */
    position = dll_search(list, 200);
    assert(1 == position);
    
    position = dll_search(list, 999);  /* not found */
    assert(SIZE_MAX == position);
    
    /* test get at position */
    assert(true == dll_get_at(list, 0, &data));
    assert(100 == data);
    
    assert(true == dll_get_at(list, 2, &data));
    assert(300 == data);
    
    assert(false == dll_get_at(list, 10, &data));  /* invalid position */
    
    dll_destroy(list);
    printf("✓ Search and get operations test passed\n\n");
}

/**
 * Test edge cases and error conditions
 */
static void test_edge_cases(void)
{
    struct doubly_linked_list *list = NULL;
    int data = 0;
    
    printf("Testing edge cases...\n");
    
    /* test operations on NULL list */
    assert(false == dll_insert_front(NULL, 10));
    assert(false == dll_remove_front(NULL));
    assert(0 == dll_get_size(NULL));
    assert(true == dll_is_empty(NULL));
    
    /* test operations on empty list */
    list = dll_create();
    assert(NULL != list);
    
    assert(false == dll_remove_front(list));
    assert(false == dll_remove_back(list));
    assert(false == dll_remove_at(list, 0));
    assert(false == dll_get_at(list, 0, &data));
    assert(SIZE_MAX == dll_search(list, 10));
    
    dll_print_forward(list);   /* should print "List is empty" */
    dll_print_backward(list);  /* should print "List is empty" */
    
    dll_destroy(list);
    printf("✓ Edge cases test passed\n\n");
}

/**
 * Test clear operation
 */
static void test_clear(void)
{
    struct doubly_linked_list *list = NULL;
    
    printf("Testing clear operation...\n");
    
    list = dll_create();
    assert(NULL != list);
    
    /* populate list */
    dll_insert_back(list, 1);
    dll_insert_back(list, 2);
    dll_insert_back(list, 3);
    assert(3 == dll_get_size(list));
    
    /* clear list */
    dll_clear(list);
    assert(0 == dll_get_size(list));
    assert(true == dll_is_empty(list));
    
    /* verify we can still use the list */
    assert(true == dll_insert_front(list, 99));
    assert(1 == dll_get_size(list));
    
    dll_destroy(list);
    printf("✓ Clear operation test passed\n\n");
}

/**
 * Main test function
 */
int main(void)
{
    printf("=== Doubly Linked List Test Suite ===\n\n");
    
    test_create_destroy();
    test_insert_front();
    test_insert_back();
    test_insert_at();
    test_remove_operations();
    test_search_get();
    test_edge_cases();
    test_clear();
    
    printf("=== All tests passed! ===\n");
    return 0;
}