package com.lingniu.ingest.eventhistory;

import com.lingniu.ingest.protocol.gb32960.codec.Gb32960MessageDecoder;
import com.lingniu.ingest.tdenginehistory.TdengineHistoryReader;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.http.HttpMethod;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.io.IOException;
import java.nio.file.Path;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;

class ApiExceptionHandlerTest {

    @TempDir
    Path archiveRoot;

    @Test
    void missingRequiredQueryParameterReturnsConcreteReason() throws Exception {
        MockMvc mvc = standaloneSetup(new Gb32960FrameController(service()))
                .setControllerAdvice(new ApiExceptionHandler())
                .build();

        mvc.perform(get("/api/event-history/gb32960/snapshots")
                        .param("dateFrom", "2026-06-23")
                        .param("dateTo", "2026-06-23")
                        .param("limit", "1"))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.message").value("missing required query parameter: vin"))
                .andExpect(jsonPath("$.timestamp").value(org.hamcrest.Matchers.endsWith("+08:00")))
                .andExpect(jsonPath("$.path").value("/api/event-history/gb32960/snapshots"));
    }

    @Test
    void responseStatusExceptionReturnsReason() throws Exception {
        MockMvc mvc = standaloneSetup(new Gb32960FrameController(service()))
                .setControllerAdvice(new ApiExceptionHandler())
                .build();

        mvc.perform(get("/api/event-history/gb32960/snapshots")
                        .param("dateFrom", "2026-06-23")
                        .param("dateTo", "2026-06-23")
                        .param("limit", "1")
                        .param("vin", ""))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.message").value("vin is required for gb32960 snapshots"));
    }

    @Test
    void ioExceptionReturnsServiceUnavailable() throws Exception {
        MockMvc mvc = standaloneSetup(new IoFailureController())
                .setControllerAdvice(new ApiExceptionHandler())
                .build();

        mvc.perform(get("/io"))
                .andExpect(status().isServiceUnavailable())
                .andExpect(jsonPath("$.message").value("tdengine history query failed"))
                .andExpect(jsonPath("$.path").value("/io"));
    }

    @Test
    void noResourceReturnsNotFound() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getRequestURI()).thenReturn("/missing");

        var response = new ApiExceptionHandler().noResource(
                new NoResourceFoundException(HttpMethod.GET, "/missing"),
                request);

        assertThat(response.getStatusCode().value()).isEqualTo(404);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().message()).isEqualTo("resource not found: /missing");
    }

    private Gb32960DecodedFrameService service() {
        return new Gb32960DecodedFrameService(
                (TdengineHistoryReader) null,
                mock(Gb32960MessageDecoder.class),
                archiveRoot,
                null);
    }

    @RestController
    private static final class IoFailureController {
        @GetMapping("/io")
        String io() throws IOException {
            throw new IOException("tdengine history query failed");
        }
    }
}
