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

import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
import com.starry.admin.modules.clerk.enums.ClerkMediaReviewState;
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
import com.starry.admin.modules.clerk.module.entity.PlayClerkMediaAssetEntity;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService;
import com.starry.admin.modules.media.entity.PlayMediaEntity;
import com.starry.admin.modules.media.enums.MediaOwnerType;
import com.starry.admin.modules.media.service.IPlayMediaService;
import com.starry.admin.modules.weichat.entity.clerk.MediaOrderRequest;
import com.starry.admin.modules.weichat.entity.clerk.MediaVo;
import com.starry.admin.modules.weichat.service.MediaUploadService;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

@ExtendWith(MockitoExtension.class)
public class WxClerkMediaControllerTest {

    @Mock
    private MediaUploadService mediaUploadService;
    @Mock
    private IPlayMediaService mediaService;
    @Mock
    private IPlayClerkMediaAssetService clerkMediaAssetService;
    @InjectMocks
    private WxClerkMediaController controller;

    private MockMvc mockMvc;
    private final ObjectMapper objectMapper = new ObjectMapper();

    private PlayClerkUserInfoEntity clerk;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
        clerk = new PlayClerkUserInfoEntity();
        clerk.setId("clerk-1");
        clerk.setTenantId("tenant-1");
        ThreadLocalRequestDetail.setRequestDetail(clerk);
    }

    @AfterEach
    void tearDown() {
        ThreadLocalRequestDetail.remove();
    }

    @Test
    void uploadShouldReturnMediaVo() throws Exception {
        MediaVo vo = new MediaVo();
        vo.setId("media-1");
        vo.setUsage(ClerkMediaUsage.PROFILE.getCode());
        when(mediaUploadService.upload(any(), eq(clerk), eq(ClerkMediaUsage.PROFILE))).thenReturn(vo);

        MockMultipartFile file = new MockMultipartFile("file", "avatar.png", "image/png", new byte[] {1, 2});

        mockMvc.perform(multipart("/wx/clerk/media/upload")
                        .file(file)
                        .param("usage", ClerkMediaUsage.PROFILE.getCode()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.id").value("media-1"));
    }

    @Test
    void updateOrderShouldDelegateToService() throws Exception {
        MediaOrderRequest request = new MediaOrderRequest();
        request.setUsage(ClerkMediaUsage.PROFILE.getCode());
        request.setMediaIds(Arrays.asList("m1", "m2"));

        mockMvc.perform(put("/wx/clerk/media/order")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));

        verify(clerkMediaAssetService).submitWithOrder(eq("clerk-1"), eq(ClerkMediaUsage.PROFILE),
                eq(Arrays.asList("m1", "m2")));
    }

    @Test
    void listApprovedShouldReturnAssemblerOutput() throws Exception {
        PlayClerkMediaAssetEntity asset = new PlayClerkMediaAssetEntity();
        asset.setId("asset-1");
        asset.setClerkId("clerk-1");
        asset.setUsage(ClerkMediaUsage.PROFILE.getCode());
        asset.setMediaId("media-1");
        asset.setReviewState(ClerkMediaReviewState.APPROVED.getCode());
        asset.setCreatedTime(java.sql.Timestamp.valueOf(LocalDateTime.now()));
        when(clerkMediaAssetService.listByState(eq("clerk-1"), eq(ClerkMediaUsage.PROFILE),
                eq(Collections.singletonList(ClerkMediaReviewState.APPROVED)))).thenReturn(
                        Collections.singletonList(asset));

        PlayMediaEntity media = new PlayMediaEntity();
        media.setId("media-1");
        media.setUrl("https://oss/mock.png");
        when(mediaService.listByIds(Collections.singletonList("media-1")))
                .thenReturn(Collections.singletonList(media));

        mockMvc.perform(get("/wx/clerk/media/approved"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data", hasSize(1)))
                .andExpect(jsonPath("$.data[0].url").value("https://oss/mock.png"));
    }

    @Test
    void deleteShouldInvokeServices() throws Exception {
        mockMvc.perform(delete("/wx/clerk/media/{id}", "media-77"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));

        verify(clerkMediaAssetService).softDelete("clerk-1", "media-77");
        verify(mediaService).softDelete(MediaOwnerType.CLERK, "clerk-1", "media-77");
    }
}
