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

import com.infoepoch.pms.agent.platform.model.domain.AiModelProvider;
import com.infoepoch.pms.agent.platform.shared.adapter.out.crypto.AesEncryptConverter;
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.jdbc.core.RowMapper;
import org.springframework.test.util.ReflectionTestUtils;

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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class AiModelProviderRepositoryTest {

    @Mock
    private JdbcTemplate jdbcTemplate;

    @Mock
    private AesEncryptConverter aesEncryptConverter;

    private AiModelProviderRepository repository;

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

    @Test
    void shouldEncryptApiKeyBeforeInsert() {
        LocalDateTime now = LocalDateTime.now();
        AiModelProvider provider = AiModelProvider.restore(
                "provider-1",
                "DashScope",
                "DASHSCOPE",
                "https://example.com",
                "sk-plain",
                Map.of("X-Test", "1"),
                "provider",
                true,
                null,
                null,
                now,
                now
        );
        when(aesEncryptConverter.encrypt("sk-plain")).thenReturn("cipher-text");
        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(), any(), any(), any());

        repository.insert(provider);

        verify(aesEncryptConverter).encrypt("sk-plain");
        assertEquals("cipher-text", argsRef.get()[4]);
        assertNotEquals("sk-plain", argsRef.get()[4]);
    }

    @Test
    void shouldDecryptApiKeyWhenReadingProvider() {
        LocalDateTime now = LocalDateTime.now();
        when(aesEncryptConverter.decrypt("cipher-text")).thenReturn("sk-plain");
        when(jdbcTemplate.queryForObject(anyString(), any(RowMapper.class), anyString()))
                .thenAnswer(invocation -> {
                    @SuppressWarnings("unchecked")
                    RowMapper<AiModelProvider> mapper = invocation.getArgument(1);
                    ResultSet rs = mock(ResultSet.class);
                    when(rs.getString("ID")).thenReturn("provider-1");
                    when(rs.getString("NAME")).thenReturn("DashScope");
                    when(rs.getString("TYPE")).thenReturn("DASHSCOPE");
                    when(rs.getString("BASE_URL")).thenReturn("https://example.com");
                    when(rs.getString("API_KEY")).thenReturn("cipher-text");
                    when(rs.getString("CUSTOM_HEADERS")).thenReturn("{\"X-Test\":\"1\"}");
                    when(rs.getString("DESCRIPTION")).thenReturn("provider");
                    when(rs.getObject("IS_ACTIVE")).thenReturn(true);
                    when(rs.getString("CREATED_USER_ID")).thenReturn(null);
                    when(rs.getString("UPDATED_USER_ID")).thenReturn(null);
                    when(rs.getTimestamp("CREATED_AT")).thenReturn(Timestamp.valueOf(now));
                    when(rs.getTimestamp("UPDATED_AT")).thenReturn(Timestamp.valueOf(now));
                    return mapper.mapRow(rs, 0);
                });

        AiModelProvider provider = repository.selectById("provider-1");

        verify(aesEncryptConverter).decrypt("cipher-text");
        assertEquals("sk-plain", provider.getApiKey());
    }
}
