package com.infoepoch.pms.agent.platform.chat.adapter.out.persistence;

import com.infoepoch.pms.agent.platform.chat.domain.Session;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.util.ReflectionTestUtils;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;

@ExtendWith(MockitoExtension.class)
class SessionRepositoryTest {

    @Mock
    private JdbcTemplate jdbcTemplate;

    private SessionRepository repository;

    @BeforeEach
    void setUp() {
        repository = new SessionRepository();
        ReflectionTestUtils.setField(repository, "jdbcTemplate", jdbcTemplate);
    }

    @Test
    void shouldWriteTimestampsWhenInsertingSession() {
        LocalDateTime createdAt = LocalDateTime.of(2026, 4, 20, 10, 0, 0);
        LocalDateTime updatedAt = LocalDateTime.of(2026, 4, 20, 10, 5, 0);
        AtomicReference<Object[]> argsRef = new AtomicReference<>();
        doAnswer(invocation -> {
            argsRef.set(Arrays.copyOfRange(invocation.getArguments(), 1, invocation.getArguments().length));
            return 1;
        }).when(jdbcTemplate).update(anyString(), any(), any(), any(), any(), any(), any(), any(), any(), any());

        boolean inserted = repository.insert(new Session(
                "session-1",
                "user-1",
                "chatbot-1",
                Session.STATUS_ACTIVE,
                "title",
                "thread-1",
                false,
                createdAt,
                updatedAt
        ));

        assertTrue(inserted);
        assertInstanceOf(Timestamp.class, argsRef.get()[3]);
        assertInstanceOf(Timestamp.class, argsRef.get()[4]);
        assertEquals(Timestamp.valueOf(createdAt), argsRef.get()[3]);
        assertEquals(Timestamp.valueOf(updatedAt), argsRef.get()[4]);
    }

    @Test
    void shouldWriteTimestampsWhenUpdatingSession() {
        LocalDateTime createdAt = LocalDateTime.of(2026, 4, 20, 10, 0, 0);
        LocalDateTime updatedAt = LocalDateTime.of(2026, 4, 20, 10, 5, 0);
        AtomicReference<Object[]> argsRef = new AtomicReference<>();
        doAnswer(invocation -> {
            argsRef.set(Arrays.copyOfRange(invocation.getArguments(), 1, invocation.getArguments().length));
            return 1;
        }).when(jdbcTemplate).update(anyString(), any(), any(), any(), any(), any(), any(), any(), any(), any());

        boolean updated = repository.update(new Session(
                "session-1",
                "user-1",
                "chatbot-1",
                Session.STATUS_ACTIVE,
                "title",
                "thread-1",
                true,
                createdAt,
                updatedAt
        ));

        assertTrue(updated);
        assertInstanceOf(Timestamp.class, argsRef.get()[2]);
        assertInstanceOf(Timestamp.class, argsRef.get()[3]);
        assertEquals(Timestamp.valueOf(createdAt), argsRef.get()[2]);
        assertEquals(Timestamp.valueOf(updatedAt), argsRef.get()[3]);
    }

    @Test
    void shouldAllowNullTimestampsWhenWritingSession() {
        AtomicReference<Object[]> insertArgsRef = new AtomicReference<>();
        AtomicReference<Object[]> updateArgsRef = new AtomicReference<>();
        doAnswer(invocation -> {
            Object[] args = Arrays.copyOfRange(invocation.getArguments(), 1, invocation.getArguments().length);
            String sql = invocation.getArgument(0);
            if (sql.startsWith("INSERT")) {
                insertArgsRef.set(args);
            } else {
                updateArgsRef.set(args);
            }
            return 1;
        }).when(jdbcTemplate).update(anyString(), any(), any(), any(), any(), any(), any(), any(), any(), any());

        repository.insert(new Session(
                "session-1",
                "user-1",
                "chatbot-1",
                Session.STATUS_ACTIVE,
                "title",
                "thread-1",
                false,
                null,
                null
        ));
        repository.update(new Session(
                "session-1",
                "user-1",
                "chatbot-1",
                Session.STATUS_ACTIVE,
                "title",
                "thread-1",
                false,
                null,
                null
        ));

        assertNull(insertArgsRef.get()[3]);
        assertNull(insertArgsRef.get()[4]);
        assertNull(updateArgsRef.get()[2]);
        assertNull(updateArgsRef.get()[3]);
    }
}
