package com.ruoyi.web.kb.service.impl;

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

import java.net.URI;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
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.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.kb.support.KbApiProperties;

@ExtendWith(MockitoExtension.class)
@DisplayName("KnowledgeBaseClientImpl 重试")
class KnowledgeBaseClientImplTest
{
    @Mock
    private RestTemplate restTemplate;

    private KbApiProperties properties;

    private KnowledgeBaseClientImpl client;

    @BeforeEach
    void setUp()
    {
        properties = new KbApiProperties();
        properties.setEnabled(true);
        properties.setApiKey("test-key");
        properties.setHost("localhost");
        properties.setPort(9107);
        properties.setMaxRetries(2);
        properties.setRetryIntervalMs(0);
        client = new KnowledgeBaseClientImpl(properties, restTemplate, new ObjectMapper());
    }

    @Test
    @DisplayName("上传超时后重试2次，第3次成功")
    void uploadFileByUrl_retriesOnTimeoutThenSucceeds()
    {
        when(restTemplate.exchange(any(URI.class), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class)))
                .thenThrow(new ResourceAccessException("Read timed out"))
                .thenThrow(new ResourceAccessException("Read timed out"))
                .thenReturn(ResponseEntity.ok("{\"code\":200,\"data\":{\"file_id\":\"DOC-1\"}}"));

        String fileId = client.uploadFileByUrl("http://localhost/a.pdf", "分类", "描述");

        assertEquals("DOC-1", fileId);
        verify(restTemplate, times(3)).exchange(any(URI.class), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class));
    }

    @Test
    @DisplayName("上传5xx后重试2次仍失败则抛出异常")
    void uploadFileByUrl_retriesExhausted()
    {
        when(restTemplate.exchange(any(URI.class), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class)))
                .thenThrow(HttpServerErrorException.create(HttpStatus.BAD_GATEWAY, "bad gateway", null, null, null));

        ServiceException ex = assertThrows(ServiceException.class,
                () -> client.uploadFileByUrl("http://localhost/a.pdf", "分类", "描述"));

        assertEquals(true, ex.getMessage().contains("502"));
        verify(restTemplate, times(3)).exchange(any(URI.class), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class));
    }

    @Test
    @DisplayName("删除超时后重试2次，第3次成功")
    void deleteFile_retriesOnTimeoutThenSucceeds()
    {
        when(restTemplate.exchange(any(URI.class), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(String.class)))
                .thenThrow(new ResourceAccessException("connect timed out"))
                .thenThrow(new ResourceAccessException("connect timed out"))
                .thenReturn(ResponseEntity.ok("{\"code\":200}"));

        client.deleteFile("DOC-9");

        verify(restTemplate, times(3)).exchange(any(URI.class), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(String.class));
    }

    @Test
    @DisplayName("删除404不重试")
    void deleteFile_notFoundNoRetry()
    {
        when(restTemplate.exchange(any(URI.class), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(String.class)))
                .thenThrow(HttpClientErrorException.create(HttpStatus.NOT_FOUND, "not found", null, null, null));

        client.deleteFile("DOC-MISSING");

        verify(restTemplate, times(1)).exchange(any(URI.class), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(String.class));
    }
}
