package dev.lions.unionflow.server.api.dto.common;

import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Tests unitaires pour PagedResponse
 */
class PagedResponseTest {

    @Test
    void testConstructeurVide() {
        PagedResponse<String> response = new PagedResponse<>();
        assertNull(response.getData());
        assertNull(response.getTotal());
        assertNull(response.getPage());
        assertNull(response.getSize());
        assertNull(response.getTotalPages());
    }

    @Test
    void testConstructeurAvecParametres() {
        List<String> data = Arrays.asList("A", "B", "C");
        PagedResponse<String> response = new PagedResponse<>(data, 100L, 2, 20);

        assertEquals(data, response.getData());
        assertEquals(100L, response.getTotal());
        assertEquals(2, response.getPage());
        assertEquals(20, response.getSize());
        assertEquals(5, response.getTotalPages()); // 100 / 20 = 5
    }

    @Test
    void testConstructeurComplet() {
        List<String> data = Arrays.asList("A", "B");
        PagedResponse<String> response = new PagedResponse<>(data, 50L, 1, 10, 5);

        assertEquals(data, response.getData());
        assertEquals(50L, response.getTotal());
        assertEquals(1, response.getPage());
        assertEquals(10, response.getSize());
        assertEquals(5, response.getTotalPages());
    }

    @Test
    void testCalculTotalPages() {
        // Cas normal : 100 éléments, 20 par page = 5 pages
        PagedResponse<String> response1 = new PagedResponse<>(Collections.emptyList(), 100L, 0, 20);
        assertEquals(5, response1.getTotalPages());

        // Cas avec reste : 101 éléments, 20 par page = 6 pages (arrondi supérieur)
        PagedResponse<String> response2 = new PagedResponse<>(Collections.emptyList(), 101L, 0, 20);
        assertEquals(6, response2.getTotalPages());

        // Cas avec 0 élément
        PagedResponse<String> response3 = new PagedResponse<>(Collections.emptyList(), 0L, 0, 20);
        assertEquals(0, response3.getTotalPages());

        // Cas avec size = 0 (éviter division par zéro)
        PagedResponse<String> response4 = new PagedResponse<>(Collections.emptyList(), 100L, 0, 0);
        assertEquals(0, response4.getTotalPages());

        // Cas avec total null
        PagedResponse<String> response5 = new PagedResponse<>(Collections.emptyList(), null, 0, 20);
        assertEquals(0, response5.getTotalPages());
    }

    @Test
    void testSetters() {
        PagedResponse<Integer> response = new PagedResponse<>();
        List<Integer> data = Arrays.asList(1, 2, 3);

        response.setData(data);
        response.setTotal(50L);
        response.setPage(2);
        response.setSize(10);
        response.setTotalPages(5);

        assertEquals(data, response.getData());
        assertEquals(50L, response.getTotal());
        assertEquals(2, response.getPage());
        assertEquals(10, response.getSize());
        assertEquals(5, response.getTotalPages());
    }

    @Test
    void testSetTotalRecalculeTotalPages() {
        PagedResponse<String> response = new PagedResponse<>();
        response.setSize(10);
        response.setTotal(100L);

        // Doit recalculer totalPages automatiquement
        assertEquals(10, response.getTotalPages());
    }

    @Test
    void testSetSizeRecalculeTotalPages() {
        PagedResponse<String> response = new PagedResponse<>();
        response.setTotal(100L);
        response.setSize(25);

        // Doit recalculer totalPages automatiquement
        assertEquals(4, response.getTotalPages());
    }

    @Test
    void testHasNext() {
        // Page 0 sur 5 pages totales → hasNext = true
        PagedResponse<String> response1 = new PagedResponse<>(Collections.emptyList(), 100L, 0, 20);
        assertTrue(response1.hasNext());

        // Page 4 (dernière) sur 5 pages → hasNext = false
        PagedResponse<String> response2 = new PagedResponse<>(Collections.emptyList(), 100L, 4, 20);
        assertFalse(response2.hasNext());

        // Page null → hasNext = false
        PagedResponse<String> response3 = new PagedResponse<>();
        response3.setTotal(100L);
        response3.setSize(20);
        assertFalse(response3.hasNext());
    }

    @Test
    void testHasPrevious() {
        // Page 0 → hasPrevious = false
        PagedResponse<String> response1 = new PagedResponse<>(Collections.emptyList(), 100L, 0, 20);
        assertFalse(response1.hasPrevious());

        // Page 1 → hasPrevious = true
        PagedResponse<String> response2 = new PagedResponse<>(Collections.emptyList(), 100L, 1, 20);
        assertTrue(response2.hasPrevious());

        // Page null → hasPrevious = false
        PagedResponse<String> response3 = new PagedResponse<>();
        assertFalse(response3.hasPrevious());
    }

    @Test
    void testIsEmpty() {
        // Liste vide → isEmpty = true
        PagedResponse<String> response1 = new PagedResponse<>(Collections.emptyList(), 0L, 0, 20);
        assertTrue(response1.isEmpty());

        // Liste null → isEmpty = true
        PagedResponse<String> response2 = new PagedResponse<>();
        assertTrue(response2.isEmpty());

        // Liste avec données → isEmpty = false
        PagedResponse<String> response3 = new PagedResponse<>(Arrays.asList("A", "B"), 2L, 0, 20);
        assertFalse(response3.isEmpty());
    }

    @Test
    void testToString() {
        List<String> data = Arrays.asList("A", "B", "C");
        PagedResponse<String> response = new PagedResponse<>(data, 100L, 2, 20);

        String result = response.toString();

        assertTrue(result.contains("total=100"));
        assertTrue(result.contains("page=2"));
        assertTrue(result.contains("size=20"));
        assertTrue(result.contains("totalPages=5"));
        assertTrue(result.contains("itemsCount=3"));
    }

    @Test
    void testToStringAvecDataNull() {
        PagedResponse<String> response = new PagedResponse<>();
        String result = response.toString();

        assertTrue(result.contains("itemsCount=0"));
    }

    @Test
    void testCasBordure_TotalPagesAvecValeursPetites() {
        // 1 élément, 10 par page = 1 page
        PagedResponse<String> response = new PagedResponse<>(Collections.emptyList(), 1L, 0, 10);
        assertEquals(1, response.getTotalPages());
    }

    @Test
    void testCasBordure_TotalPagesAvecValeursGrandes() {
        // 999999 éléments, 100 par page = 10000 pages
        PagedResponse<String> response = new PagedResponse<>(Collections.emptyList(), 999999L, 0, 100);
        assertEquals(10000, response.getTotalPages());
    }

    @Test
    void testGenerique() {
        // Test avec différents types génériques
        PagedResponse<Integer> intResponse = new PagedResponse<>(Arrays.asList(1, 2, 3), 3L, 0, 10);
        assertEquals(3, intResponse.getData().size());

        PagedResponse<Double> doubleResponse = new PagedResponse<>(Arrays.asList(1.5, 2.5), 2L, 0, 10);
        assertEquals(2, doubleResponse.getData().size());

        PagedResponse<CustomDTO> customResponse = new PagedResponse<>(
            Arrays.asList(new CustomDTO("test")), 1L, 0, 10
        );
        assertEquals(1, customResponse.getData().size());
    }

    // Classe DTO personnalisée pour tester le générique
    private static class CustomDTO {
        private final String value;

        public CustomDTO(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }
}
