package com.infoepoch.pms.agent.platform.model.application;

import com.infoepoch.pms.agent.platform.model.application.dto.DefaultModelSettingsResponse;
import com.infoepoch.pms.agent.platform.model.application.dto.UpdateDefaultModelSettingsRequest;
import com.infoepoch.pms.agent.platform.model.domain.AiModelConfig;
import com.infoepoch.pms.agent.platform.model.domain.AiModelProvider;
import com.infoepoch.pms.agent.platform.model.domain.SystemSettings;
import com.infoepoch.pms.agent.platform.model.domain.irepository.ISystemSettingsRepository;
import com.infoepoch.pms.agent.platform.shared.exception.BusinessException;
import com.infoepoch.pms.agent.platform.shared.response.ResultCode;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class SystemSettingsServiceTest {

    private static final String KEY_DEFAULT_MODEL = "default_model_config_id";
    private static final String KEY_DEFAULT_EMBEDDING_MODEL = "default_embedding_model_config_id";
    private static final String KEY_DEFAULT_RERANK_MODEL = "default_rerank_model_config_id";

    @Mock
    private ISystemSettingsRepository settingsRepository;

    @Mock
    private ModelQueryService modelQueryService;

    @Test
    void shouldRejectSettingRerankAsDefaultModel() {
        when(modelQueryService.getModelConfigOrNull("config-rerank")).thenReturn(rerankConfig());
        SystemSettingsService service = new SystemSettingsService(settingsRepository, modelQueryService);

        BusinessException ex = assertThrows(BusinessException.class, () -> service.setDefaultModel("config-rerank"));

        assertEquals(ResultCode.PARAM_ERROR, ex.getResultCode());
    }

    @Test
    void shouldHydrateProviderWhenGettingDefaultModelSettings() {
        when(settingsRepository.findById(KEY_DEFAULT_MODEL))
                .thenReturn(Optional.of(SystemSettings.restore(KEY_DEFAULT_MODEL, "config-llm", LocalDateTime.now())));
        when(settingsRepository.findById(KEY_DEFAULT_EMBEDDING_MODEL)).thenReturn(Optional.empty());
        when(settingsRepository.findById(KEY_DEFAULT_RERANK_MODEL)).thenReturn(Optional.empty());
        when(modelQueryService.findById("config-llm")).thenReturn(Optional.of(llmConfig()));

        SystemSettingsService service = new SystemSettingsService(settingsRepository, modelQueryService);

        DefaultModelSettingsResponse response = service.getDefaultModelSettings();

        assertNotNull(response.defaultModel());
        assertEquals("config-llm", response.defaultModel().modelConfigId());
        assertEquals("DashScope", response.defaultModel().providerName());
    }

    @Test
    void shouldHydrateProviderWhenResolvingTypedActiveModelConfig() {
        when(settingsRepository.findById(KEY_DEFAULT_EMBEDDING_MODEL))
                .thenReturn(Optional.of(SystemSettings.restore(
                        KEY_DEFAULT_EMBEDDING_MODEL, "config-embedding", LocalDateTime.now())));
        when(modelQueryService.findById("config-embedding")).thenReturn(Optional.of(embeddingConfig()));

        SystemSettingsService service = new SystemSettingsService(settingsRepository, modelQueryService);

        AiModelConfig resolved = service.resolveTypedActiveModelConfig(
                KEY_DEFAULT_EMBEDDING_MODEL,
                "EMBEDDING",
                "默认向量模型配置"
        );

        assertNotNull(resolved.getProvider());
        assertEquals("provider-1", resolved.getProvider().getId());
    }

    @Test
    void shouldUseHydratedLookupWhenUpdatingDefaultModelSettings() {
        UpdateDefaultModelSettingsRequest request = new UpdateDefaultModelSettingsRequest();
        request.setDefaultModelConfigId("config-llm");
        request.setDefaultEmbeddingModelConfigId(null);
        request.setDefaultRerankModelConfigId(null);

        when(modelQueryService.findById("config-llm")).thenReturn(Optional.of(llmConfig()));
        when(settingsRepository.findById(KEY_DEFAULT_MODEL)).thenReturn(Optional.empty());
        when(settingsRepository.findById(KEY_DEFAULT_EMBEDDING_MODEL)).thenReturn(Optional.empty());
        when(settingsRepository.findById(KEY_DEFAULT_RERANK_MODEL)).thenReturn(Optional.empty());

        SystemSettingsService service = new SystemSettingsService(settingsRepository, modelQueryService);

        service.updateDefaultModelSettings(request);

        verify(modelQueryService).findById("config-llm");
        verify(settingsRepository, times(3)).save(any(SystemSettings.class));
    }

    @Test
    void shouldClearOnlyMatchingDefaultModelReferences() {
        when(settingsRepository.findById(KEY_DEFAULT_MODEL))
                .thenReturn(Optional.of(SystemSettings.restore(KEY_DEFAULT_MODEL, "config-llm", LocalDateTime.now())));
        when(settingsRepository.findById(KEY_DEFAULT_EMBEDDING_MODEL))
                .thenReturn(Optional.of(SystemSettings.restore(KEY_DEFAULT_EMBEDDING_MODEL, "config-embedding", LocalDateTime.now())));
        when(settingsRepository.findById(KEY_DEFAULT_RERANK_MODEL))
                .thenReturn(Optional.of(SystemSettings.restore(KEY_DEFAULT_RERANK_MODEL, "config-rerank", LocalDateTime.now())));

        SystemSettingsService service = new SystemSettingsService(settingsRepository, modelQueryService);

        service.clearDefaultModelReferences(List.of("config-llm", "config-rerank"));

        ArgumentCaptor<SystemSettings> captor = ArgumentCaptor.forClass(SystemSettings.class);
        verify(settingsRepository, times(2)).save(captor.capture());
        List<SystemSettings> clearedSettings = captor.getAllValues();
        assertEquals(List.of(KEY_DEFAULT_MODEL, KEY_DEFAULT_RERANK_MODEL),
                clearedSettings.stream().map(SystemSettings::getKey).toList());
        assertEquals(2, clearedSettings.stream()
                .map(SystemSettings::getValue)
                .filter(value -> value == null)
                .count());
        verifyNoMoreInteractions(modelQueryService);
    }

    private AiModelConfig llmConfig() {
        return AiModelConfig.restore(
                "config-llm",
                provider(),
                "qwen-max",
                "LLM",
                "Qwen Max",
                8192,
                BigDecimal.ONE,
                32,
                null,
                Boolean.TRUE,
                Boolean.TRUE,
                null,
                true,
                null,
                null,
                LocalDateTime.now(),
                LocalDateTime.now()
        );
    }

    private AiModelConfig embeddingConfig() {
        return AiModelConfig.restore(
                "config-embedding",
                provider(),
                "text-embedding-v3",
                "EMBEDDING",
                "Embedding",
                null,
                null,
                null,
                1024,
                null,
                null,
                null,
                true,
                null,
                null,
                LocalDateTime.now(),
                LocalDateTime.now()
        );
    }

    private AiModelConfig rerankConfig() {
        return AiModelConfig.restore(
                "config-rerank",
                provider(),
                "gte-rerank",
                "RERANK",
                "Rerank",
                null,
                null,
                null,
                null,
                null,
                null,
                null,
                true,
                null,
                null,
                LocalDateTime.now(),
                LocalDateTime.now()
        );
    }

    private AiModelProvider provider() {
        return AiModelProvider.restore(
                "provider-1",
                "DashScope",
                "DASHSCOPE",
                null,
                "sk-test",
                null,
                "provider",
                true,
                null,
                null,
                LocalDateTime.now(),
                LocalDateTime.now()
        );
    }
}
