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

import com.infoepoch.pms.agent.platform.agent.domain.irepository.IAgentRepository;
import com.infoepoch.pms.agent.platform.chatbot.domain.irepository.IChatbotRepository;
import com.infoepoch.pms.agent.platform.model.application.dto.CreateModelConfigRequest;
import com.infoepoch.pms.agent.platform.model.application.dto.ModelConfigResponse;
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.irepository.IAiModelConfigRepository;
import com.infoepoch.pms.agent.platform.model.domain.irepository.IAiModelProviderRepository;
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.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.LocalDateTime;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class ModelConfigServiceTest {

    @Mock
    private IAiModelConfigRepository configRepository;

    @Mock
    private IAiModelProviderRepository providerRepository;

    @Mock
    private ModelProvisionService modelProvisionService;

    @Mock
    private ModelQueryService modelQueryService;

    @Mock
    private IAgentRepository agentRepository;

    @Mock
    private IChatbotRepository chatbotRepository;

    @Mock
    private SystemSettingsService systemSettingsService;

    @Test
    void shouldReturnLlmFlagsWhenCreatingLlmConfig() {
        AiModelProvider provider = provider("provider-1");
        CreateModelConfigRequest req = new CreateModelConfigRequest();
        req.setModelName("qwen-max");
        req.setModelType("LLM");
        req.setDisplayName("Qwen Max");
        req.setMaxTokens(2048);
        req.setEnableThinking(Boolean.TRUE);
        req.setMultiModel(Boolean.FALSE);

        when(providerRepository.findById("provider-1")).thenReturn(Optional.of(provider));
        when(configRepository.existsByProviderIdAndModelNameAndModelType("provider-1", "qwen-max", "LLM"))
                .thenReturn(false);
        when(configRepository.save(any(AiModelConfig.class))).thenAnswer(invocation -> invocation.getArgument(0));

        ModelConfigService service = new ModelConfigService(
                configRepository,
                providerRepository,
                modelProvisionService,
                modelQueryService,
                systemSettingsService,
                agentRepository,
                chatbotRepository
        );

        ModelConfigResponse response = service.create("provider-1", req);

        assertEquals("LLM", response.getModelType());
        assertTrue(response.getEnableThinking());
        assertEquals(Boolean.FALSE, response.getMultiModel());
        assertNull(response.getVectorDimension());
    }

    @Test
    void shouldAllowCreatingRerankConfigButNormalizeTypeSpecificFields() {
        AiModelProvider provider = provider("provider-1");
        CreateModelConfigRequest req = new CreateModelConfigRequest();
        req.setModelName("gte-rerank");
        req.setModelType("RERANK");
        req.setVectorDimension(1024);
        req.setEnableThinking(Boolean.TRUE);
        req.setMultiModel(Boolean.TRUE);

        when(providerRepository.findById("provider-1")).thenReturn(Optional.of(provider));
        when(configRepository.existsByProviderIdAndModelNameAndModelType("provider-1", "gte-rerank", "RERANK"))
                .thenReturn(false);
        when(configRepository.save(any(AiModelConfig.class))).thenAnswer(invocation -> invocation.getArgument(0));

        ModelConfigService service = new ModelConfigService(
                configRepository,
                providerRepository,
                modelProvisionService,
                modelQueryService,
                systemSettingsService,
                agentRepository,
                chatbotRepository
        );

        ModelConfigResponse response = service.create("provider-1", req);

        assertEquals("RERANK", response.getModelType());
        assertNull(response.getVectorDimension());
        assertNull(response.getEnableThinking());
        assertNull(response.getMultiModel());
    }

    @Test
    void shouldRejectDeleteWhenConfigIsReferencedByAgent() {
        AiModelConfig config = modelConfig("config-1", "provider-1");
        when(modelQueryService.getModelConfigOrNull("config-1")).thenReturn(config);
        when(agentRepository.existsByModelConfigId("config-1")).thenReturn(true);
        ModelConfigService service = new ModelConfigService(
                configRepository,
                providerRepository,
                modelProvisionService,
                modelQueryService,
                systemSettingsService,
                agentRepository,
                chatbotRepository
        );

        BusinessException ex = assertThrows(BusinessException.class, () -> service.delete("provider-1", "config-1"));

        assertEquals(ResultCode.RESOURCE_IN_USE, ex.getResultCode());
        verify(configRepository, never()).delete("config-1");
    }

    @Test
    void shouldRejectDeleteWhenConfigIsReferencedByChatbot() {
        AiModelConfig config = modelConfig("config-2", "provider-1");
        when(modelQueryService.getModelConfigOrNull("config-2")).thenReturn(config);
        when(chatbotRepository.existsByModelConfigId("config-2")).thenReturn(true);
        ModelConfigService service = new ModelConfigService(
                configRepository,
                providerRepository,
                modelProvisionService,
                modelQueryService,
                systemSettingsService,
                agentRepository,
                chatbotRepository
        );

        BusinessException ex = assertThrows(BusinessException.class, () -> service.delete("provider-1", "config-2"));

        assertEquals(ResultCode.RESOURCE_IN_USE, ex.getResultCode());
        verify(configRepository, never()).delete("config-2");
    }

    @Test
    void shouldClearDefaultModelReferencesWhenDeletingConfig() {
        AiModelConfig config = modelConfig("config-3", "provider-1");
        when(modelQueryService.getModelConfigOrNull("config-3")).thenReturn(config);
        when(agentRepository.existsByModelConfigId("config-3")).thenReturn(false);
        when(chatbotRepository.existsByModelConfigId("config-3")).thenReturn(false);

        ModelConfigService service = new ModelConfigService(
                configRepository,
                providerRepository,
                modelProvisionService,
                modelQueryService,
                systemSettingsService,
                agentRepository,
                chatbotRepository
        );

        service.delete("provider-1", "config-3");

        verify(systemSettingsService).clearDefaultModelReferences(java.util.List.of("config-3"));
        verify(configRepository).delete("config-3");
    }

    private AiModelConfig modelConfig(String id, String providerId) {
        LocalDateTime now = LocalDateTime.now();
        return new AiModelConfig(
                id,
                providerId,
                "qwen-max",
                "LLM",
                "千问 Max",
                null,
                null,
                null,
                null,
                null,
                null,
                null,
                true,
                null,
                null,
                now,
                now
        );
    }

    private AiModelProvider provider(String id) {
        LocalDateTime now = LocalDateTime.now();
        return AiModelProvider.restore(
                id,
                "DashScope",
                "DASHSCOPE",
                "https://dashscope.aliyuncs.com/compatible-mode/v1",
                "sk-test",
                null,
                "provider",
                true,
                null,
                null,
                now,
                now
        );
    }
}
