package incheon.product.geoview2d.flight.web;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import incheon.com.cmm.exception.RestApiExceptionHandler;
import incheon.product.geoview2d.flight.service.FlightPhotoService;
import incheon.product.geoview2d.flight.vo.FlightPhotoLayerVO;
import incheon.product.geoview2d.flight.vo.FlightPhotoSearchVO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import java.io.IOException;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * FlightPhotoApiController MockMvc 테스트.
 * pageIndex 클램핑, WMTS 오류 처리, @Valid 검증을 중점 검증한다.
 */
@ExtendWith(MockitoExtension.class)
class FlightPhotoApiControllerTest {

    @InjectMocks
    private FlightPhotoApiController controller;

    @Mock
    private FlightPhotoService flightPhotoService;

    private MockMvc mockMvc;
    private ObjectMapper objectMapper;

    @BeforeEach
    void setup() {
        objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        mockMvc = MockMvcBuilders.standaloneSetup(controller)
                .setControllerAdvice(new RestApiExceptionHandler())
                .setMessageConverters(
                        new StringHttpMessageConverter(),
                        new MappingJackson2HttpMessageConverter(objectMapper))
                .build();
    }

    @Nested
    @DisplayName("GET /api/v1/product/g2d/flight — 항공 사진 목록")
    class FlightPhotoList {

        @Test
        @DisplayName("정상 목록 조회")
        void normalList() throws Exception {
            when(flightPhotoService.getFlightPhotoLayerList(any(), eq(false)))
                    .thenReturn(List.of());

            mockMvc.perform(get("/api/v1/product/g2d/flight"))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.data.content").isArray());
        }

        @Test
        @DisplayName("pageIndex 0이면 1로 클램핑")
        void pageIndexZeroClamped() throws Exception {
            when(flightPhotoService.getFlightPhotoLayerList(any(), eq(false)))
                    .thenReturn(List.of());

            mockMvc.perform(get("/api/v1/product/g2d/flight")
                            .param("pageIndex", "0"))
                    .andExpect(status().isOk());

            ArgumentCaptor<FlightPhotoSearchVO> captor = ArgumentCaptor.forClass(FlightPhotoSearchVO.class);
            verify(flightPhotoService).getFlightPhotoLayerList(captor.capture(), eq(false));
            assertThat(captor.getValue().getPageIndex()).isEqualTo(1);
        }
    }

    @Nested
    @DisplayName("GET /api/v1/product/g2d/flight/{id} — 항공 사진 상세")
    class FlightPhotoDetail {

        @Test
        @DisplayName("정상 상세 조회")
        void normalDetail() throws Exception {
            FlightPhotoLayerVO layer = new FlightPhotoLayerVO();
            layer.setFlightPhotoLyrId(1);
            layer.setFlightPhotoLyrNm("2024 항공사진");
            when(flightPhotoService.getFlightPhotoLayerById(1)).thenReturn(layer);

            mockMvc.perform(get("/api/v1/product/g2d/flight/1"))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.data.flightPhotoLyrId").value(1));
        }
    }

    @Nested
    @DisplayName("POST /api/v1/product/g2d/flight/download — 다운로드")
    class Download {

        @Test
        @DisplayName("WMTS 연결 실패 시 500")
        void wmtsConnectionFail() throws Exception {
            when(flightPhotoService.createWebMapTileServer())
                    .thenThrow(new IOException("WMTS 연결 실패"));

            String body = "{\"layerIds\":[1],\"srid\":5186,\"minx\":126.5,\"miny\":37.4,\"maxx\":126.6,\"maxy\":37.5}";

            mockMvc.perform(post("/api/v1/product/g2d/flight/download")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(body))
                    .andExpect(status().isInternalServerError());
        }

        @Test
        @DisplayName("@Valid 검증 — 필수 필드 누락이면 400")
        void missingRequiredFields() throws Exception {
            mockMvc.perform(post("/api/v1/product/g2d/flight/download")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content("{}"))
                    .andExpect(status().isBadRequest());

            verify(flightPhotoService, never()).validateDownloadRequest(any());
        }
    }
}
