package dev.lions.unionflow.server.entity;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;

@DisplayName("SystemLog entity")
class SystemLogTest {

    // -------------------------------------------------------------------------
    // No-args constructor
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("no-args constructor creates instance with all-null fields")
    void noArgsConstructor_createsInstanceWithNullFields() {
        SystemLog log = new SystemLog();

        assertThat(log).isNotNull();
        assertThat(log.getLevel()).isNull();
        assertThat(log.getSource()).isNull();
        assertThat(log.getMessage()).isNull();
        assertThat(log.getDetails()).isNull();
        assertThat(log.getTimestamp()).isNull();
        assertThat(log.getUserId()).isNull();
        assertThat(log.getIpAddress()).isNull();
        assertThat(log.getSessionId()).isNull();
        assertThat(log.getEndpoint()).isNull();
        assertThat(log.getHttpStatusCode()).isNull();
    }

    // -------------------------------------------------------------------------
    // Getters / Setters — every field
    // -------------------------------------------------------------------------
    @Nested
    @DisplayName("Getters and Setters")
    class GettersSettersTests {

        @Test
        @DisplayName("setLevel / getLevel round-trips")
        void level_roundTrips() {
            SystemLog log = new SystemLog();
            log.setLevel("ERROR");
            assertThat(log.getLevel()).isEqualTo("ERROR");
        }

        @Test
        @DisplayName("setSource / getSource round-trips")
        void source_roundTrips() {
            SystemLog log = new SystemLog();
            log.setSource("Database");
            assertThat(log.getSource()).isEqualTo("Database");
        }

        @Test
        @DisplayName("setMessage / getMessage round-trips")
        void message_roundTrips() {
            SystemLog log = new SystemLog();
            log.setMessage("Connection refused on port 5432");
            assertThat(log.getMessage()).isEqualTo("Connection refused on port 5432");
        }

        @Test
        @DisplayName("setDetails / getDetails round-trips")
        void details_roundTrips() {
            String stacktrace = "java.sql.SQLTransientConnectionException\n\tat com.example.Foo.bar(Foo.java:42)";
            SystemLog log = new SystemLog();
            log.setDetails(stacktrace);
            assertThat(log.getDetails()).isEqualTo(stacktrace);
        }

        @Test
        @DisplayName("setTimestamp / getTimestamp round-trips")
        void timestamp_roundTrips() {
            LocalDateTime ts = LocalDateTime.of(2026, 4, 20, 10, 30, 0);
            SystemLog log = new SystemLog();
            log.setTimestamp(ts);
            assertThat(log.getTimestamp()).isEqualTo(ts);
        }

        @Test
        @DisplayName("setUserId / getUserId round-trips")
        void userId_roundTrips() {
            SystemLog log = new SystemLog();
            log.setUserId("user-abc-123");
            assertThat(log.getUserId()).isEqualTo("user-abc-123");
        }

        @Test
        @DisplayName("setIpAddress / getIpAddress round-trips")
        void ipAddress_roundTrips() {
            SystemLog log = new SystemLog();
            log.setIpAddress("10.0.0.1");
            assertThat(log.getIpAddress()).isEqualTo("10.0.0.1");
        }

        @Test
        @DisplayName("setSessionId / getSessionId round-trips")
        void sessionId_roundTrips() {
            SystemLog log = new SystemLog();
            log.setSessionId("sess-xyz-789");
            assertThat(log.getSessionId()).isEqualTo("sess-xyz-789");
        }

        @Test
        @DisplayName("setEndpoint / getEndpoint round-trips")
        void endpoint_roundTrips() {
            SystemLog log = new SystemLog();
            log.setEndpoint("/api/membres/123/cotisations");
            assertThat(log.getEndpoint()).isEqualTo("/api/membres/123/cotisations");
        }

        @Test
        @DisplayName("setHttpStatusCode / getHttpStatusCode round-trips")
        void httpStatusCode_roundTrips() {
            SystemLog log = new SystemLog();
            log.setHttpStatusCode(500);
            assertThat(log.getHttpStatusCode()).isEqualTo(500);
        }

        @Test
        @DisplayName("setHttpStatusCode null is allowed (optional field)")
        void httpStatusCode_nullIsAllowed() {
            SystemLog log = new SystemLog();
            log.setHttpStatusCode(null);
            assertThat(log.getHttpStatusCode()).isNull();
        }
    }

    // -------------------------------------------------------------------------
    // Typical log-level values
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("level accepts all expected log-level values")
    void level_acceptsAllExpectedValues() {
        String[] levels = {"CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"};
        SystemLog log = new SystemLog();
        for (String level : levels) {
            log.setLevel(level);
            assertThat(log.getLevel()).isEqualTo(level);
        }
    }

    // -------------------------------------------------------------------------
    // Typical source values
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("source accepts typical system sources")
    void source_acceptsTypicalSources() {
        String[] sources = {"Database", "API", "Auth", "System", "Cache"};
        SystemLog log = new SystemLog();
        for (String source : sources) {
            log.setSource(source);
            assertThat(log.getSource()).isEqualTo(source);
        }
    }

    // -------------------------------------------------------------------------
    // UUID id (inherited from BaseEntity)
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("UUID id field is settable and gettable")
    void uuidId_settableAndGettable() {
        UUID id = UUID.randomUUID();
        SystemLog log = new SystemLog();
        log.setId(id);
        assertThat(log.getId()).isEqualTo(id);
    }

    // -------------------------------------------------------------------------
    // HTTP status code — boundary values
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("httpStatusCode accepts various HTTP status codes")
    void httpStatusCode_acceptsVariousValues() {
        int[] codes = {200, 201, 400, 401, 403, 404, 422, 500, 503};
        SystemLog log = new SystemLog();
        for (int code : codes) {
            log.setHttpStatusCode(code);
            assertThat(log.getHttpStatusCode()).isEqualTo(code);
        }
    }

    // -------------------------------------------------------------------------
    // Full system-error scenario
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("full error log scenario sets all fields coherently")
    void fullErrorLogScenario() {
        LocalDateTime ts = LocalDateTime.of(2026, 4, 20, 14, 35, 22);

        SystemLog log = new SystemLog();
        log.setLevel("ERROR");
        log.setSource("API");
        log.setMessage("NullPointerException in MemberService.findById");
        log.setDetails("java.lang.NullPointerException\n\tat dev.lions.MemberService.findById(MemberService.java:88)");
        log.setTimestamp(ts);
        log.setUserId("user-42");
        log.setIpAddress("192.168.0.10");
        log.setSessionId("session-abc");
        log.setEndpoint("/api/membres/42");
        log.setHttpStatusCode(500);

        assertThat(log.getLevel()).isEqualTo("ERROR");
        assertThat(log.getSource()).isEqualTo("API");
        assertThat(log.getMessage()).contains("NullPointerException");
        assertThat(log.getDetails()).contains("MemberService.java:88");
        assertThat(log.getTimestamp()).isEqualTo(ts);
        assertThat(log.getUserId()).isEqualTo("user-42");
        assertThat(log.getIpAddress()).isEqualTo("192.168.0.10");
        assertThat(log.getSessionId()).isEqualTo("session-abc");
        assertThat(log.getEndpoint()).isEqualTo("/api/membres/42");
        assertThat(log.getHttpStatusCode()).isEqualTo(500);
    }

    // -------------------------------------------------------------------------
    // Nullable optional fields
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("optional fields can be null independently")
    void optionalFields_canBeNullIndependently() {
        SystemLog log = new SystemLog();
        log.setLevel("INFO");
        log.setSource("System");
        log.setMessage("Scheduled task completed");
        log.setTimestamp(LocalDateTime.now());
        // Leave all optional fields as null
        assertThat(log.getDetails()).isNull();
        assertThat(log.getUserId()).isNull();
        assertThat(log.getIpAddress()).isNull();
        assertThat(log.getSessionId()).isNull();
        assertThat(log.getEndpoint()).isNull();
        assertThat(log.getHttpStatusCode()).isNull();
    }
}
