package com.starry.admin.modules.statistics.controller;

import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailCompositionVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailQueryVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceDetailResponseVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewQueryVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewResponseVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceOverviewSummaryVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceProfileVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceSnapshotVo;
import com.starry.admin.modules.statistics.module.vo.ClerkPerformanceTrendPointVo;
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collections;
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.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.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

/**
 * MockMvc tests for {@link PlayClerkPerformanceController} verifying new overview/detail endpoints.
 */
@ExtendWith(MockitoExtension.class)
class PlayClerkPerformanceControllerTest {

    @Mock
    private IPlayClerkPerformanceService playClerkPerformanceService;

    @InjectMocks
    private PlayClerkPerformanceController controller;

    private MockMvc mockMvc;
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        objectMapper = new ObjectMapper();
        objectMapper.findAndRegisterModules();
        mockMvc = MockMvcBuilders.standaloneSetup(controller)
                .setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper))
                .build();
    }

    @Test
    @DisplayName("POST /statistics/performance/overview should delegate and wrap service response")
    void overviewEndpointReturnsAggregatedData() throws Exception {
        ClerkPerformanceSnapshotVo snapshot = new ClerkPerformanceSnapshotVo();
        snapshot.setClerkId("c1");
        snapshot.setClerkNickname("Alice");
        snapshot.setGmv(new BigDecimal("300.00"));
        snapshot.setOrderCount(5);
        snapshot.setContinuedRate(new BigDecimal("60.00"));

        ClerkPerformanceOverviewSummaryVo summary = new ClerkPerformanceOverviewSummaryVo();
        summary.setTotalGmv(new BigDecimal("300.00"));
        summary.setTotalOrderCount(5);
        summary.setContinuedRate(new BigDecimal("60.00"));

        ClerkPerformanceOverviewResponseVo responseVo = new ClerkPerformanceOverviewResponseVo();
        responseVo.setSummary(summary);
        responseVo.setRankings(Collections.singletonList(snapshot));
        responseVo.setTotal(1);

        when(playClerkPerformanceService.queryOverview(any())).thenReturn(responseVo);

        String payload = "{\"endOrderTime\":[\"2024-08-01 00:00:00\",\"2024-08-07 23:59:59\"],\"limit\":3}";

        mockMvc.perform(MockMvcRequestBuilders.post("/statistics/performance/overview")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(payload))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.success").value(true))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.rankings", hasSize(1)))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.rankings[0].clerkId").value("c1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.summary.totalGmv").value(300.00))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.total").value(1));

        ArgumentCaptor<ClerkPerformanceOverviewQueryVo> captor = ArgumentCaptor
                .forClass(ClerkPerformanceOverviewQueryVo.class);
        verify(playClerkPerformanceService).queryOverview(captor.capture());
        ClerkPerformanceOverviewQueryVo captured = captor.getValue();
        assertEquals(Integer.valueOf(3), captured.getLimit());
        assertEquals(Arrays.asList("2024-08-01 00:00:00", "2024-08-07 23:59:59"), captured.getEndOrderTime());
    }

    @Test
    @DisplayName("POST /statistics/performance/detail should return snapshot and trend data")
    void detailEndpointReturnsSnapshot() throws Exception {
        ClerkPerformanceProfileVo profile = new ClerkPerformanceProfileVo();
        profile.setClerkId("c1");
        profile.setNickname("Alice");
        profile.setGroupName("一组");

        ClerkPerformanceSnapshotVo snapshotVo = new ClerkPerformanceSnapshotVo();
        snapshotVo.setClerkId("c1");
        snapshotVo.setGmv(new BigDecimal("260.00"));
        snapshotVo.setOrderCount(3);
        snapshotVo.setContinuedRate(new BigDecimal("66.67"));

        ClerkPerformanceDetailCompositionVo composition = new ClerkPerformanceDetailCompositionVo();
        ClerkPerformanceDetailCompositionVo.CompositionSlice slice = new ClerkPerformanceDetailCompositionVo.CompositionSlice();
        slice.setKey("FIRST_ORDER");
        slice.setLabel("首单");
        slice.setCount(1);
        slice.setCountRatio(new BigDecimal("33.33"));
        composition.setOrderComposition(Collections.singletonList(slice));

        ClerkPerformanceTrendPointVo trendPoint = new ClerkPerformanceTrendPointVo();
        trendPoint.setDate(LocalDate.of(2024, 8, 1));
        trendPoint.setGmv(new BigDecimal("120.00"));

        ClerkPerformanceDetailResponseVo detailResponse = new ClerkPerformanceDetailResponseVo();
        detailResponse.setProfile(profile);
        detailResponse.setSnapshot(snapshotVo);
        detailResponse.setComposition(composition);
        detailResponse.setTrend(Collections.singletonList(trendPoint));
        detailResponse.setTrendDimension("DAY");

        when(playClerkPerformanceService.queryDetail(any())).thenReturn(detailResponse);

        String payload = "{\"clerkId\":\"c1\",\"endOrderTime\":[\"2024-08-01 00:00:00\",\"2024-08-03 23:59:59\"],\"includeTrend\":true}";

        mockMvc.perform(MockMvcRequestBuilders.post("/statistics/performance/detail")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(payload))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.success").value(true))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.profile.clerkId").value("c1"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.snapshot.gmv").value(260.00))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.trend[0].gmv").value(120.00));

        ArgumentCaptor<ClerkPerformanceDetailQueryVo> captor = ArgumentCaptor
                .forClass(ClerkPerformanceDetailQueryVo.class);
        verify(playClerkPerformanceService).queryDetail(captor.capture());
        ClerkPerformanceDetailQueryVo captured = captor.getValue();
        assertTrue(captured.getIncludeTrend());
        assertEquals("c1", captured.getClerkId());
        assertEquals(Arrays.asList("2024-08-01 00:00:00", "2024-08-03 23:59:59"), captured.getEndOrderTime());
    }
}
