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

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
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 com.starry.admin.common.exception.CustomException;
import com.starry.admin.common.oss.service.IOssFileService;
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.MediaVo;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
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.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

@ExtendWith(MockitoExtension.class)
class MediaUploadServiceTest {

    @Mock
    private IOssFileService ossFileService;
    @Mock
    private IPlayMediaService mediaService;
    @Mock
    private IPlayClerkMediaAssetService clerkMediaAssetService;

    private MediaUploadService service;

    @BeforeEach
    void setUp() {
        service = new MediaUploadService(ossFileService, mediaService, clerkMediaAssetService);
    }

    @Test
    @DisplayName("上传图片时应创建媒资与草稿资产，并返回完整的 MediaVo")
    void uploadImageCreatesMediaAndAsset() throws Exception {
        PlayClerkUserInfoEntity clerk = buildClerk();
        MultipartFile file = buildImageFile("avatar.png", "image/png");

        when(mediaService.normalizeAndSave(any())).thenAnswer(invocation -> invocation.getArgument(0));
        when(ossFileService.upload(any(), eq(clerk.getTenantId()), any()))
                .thenReturn("https://oss.mock/avatar.png");

        PlayClerkMediaAssetEntity asset = new PlayClerkMediaAssetEntity();
        asset.setId("asset-1");
        asset.setUsage(ClerkMediaUsage.PROFILE.getCode());
        asset.setReviewState("draft");
        when(clerkMediaAssetService.linkDraftAsset(any(), any(), any(), any())).thenAnswer(invocation -> {
            asset.setMediaId(invocation.getArgument(2));
            return asset;
        });

        MediaVo result = service.upload(file, clerk, null);

        assertThat(result).isNotNull();
        assertThat(result.getUrl()).isEqualTo("https://oss.mock/avatar.png");
        assertThat(result.getUsage()).isEqualTo(ClerkMediaUsage.PROFILE.getCode());
        assertThat(result.getAssetId()).isEqualTo("asset-1");

        ArgumentCaptor<PlayMediaEntity> mediaCaptor = ArgumentCaptor.forClass(PlayMediaEntity.class);
        verify(mediaService).normalizeAndSave(mediaCaptor.capture());
        PlayMediaEntity persisted = mediaCaptor.getValue();
        assertThat(persisted.getOwnerType()).isEqualTo(MediaOwnerType.CLERK);
        assertThat(persisted.getOwnerId()).isEqualTo(clerk.getId());
        assertThat(persisted.getMetadata()).containsKeys("originalFilename", "contentType", "uploadTraceId");

        verify(clerkMediaAssetService).linkDraftAsset(
                eq(clerk.getTenantId()),
                eq(clerk.getId()),
                eq(persisted.getId()),
                eq(ClerkMediaUsage.PROFILE));
    }

    @Test
    @DisplayName("不支援的檔案格式應回報錯誤，且不呼叫外部服務")
    void uploadRejectsUnsupportedFormat() {
        PlayClerkUserInfoEntity clerk = buildClerk();
        MultipartFile invalidFile = new MockMultipartFile("file", "note.txt", "text/plain", "oops".getBytes());

        assertThatThrownBy(() -> service.upload(invalidFile, clerk, ClerkMediaUsage.PROFILE))
                .isInstanceOf(CustomException.class)
                .hasMessage("不支持的文件格式");

        verify(mediaService, never()).normalizeAndSave(any());
        verify(ossFileService, never()).upload(any(), any(), any());
        verify(clerkMediaAssetService, never()).linkDraftAsset(any(), any(), any(), any());
    }

    @Test
    @DisplayName("缺少檔案或店員資訊時應回報錯誤")
    void uploadRejectsMissingInputs() {
        PlayClerkUserInfoEntity clerk = buildClerk();
        MultipartFile emptyFile = new MockMultipartFile("file", new byte[0]);

        assertThatThrownBy(() -> service.upload(emptyFile, clerk, ClerkMediaUsage.PROFILE))
                .isInstanceOf(CustomException.class)
                .hasMessage("请选择要上传的文件");

        MultipartFile file = new MockMultipartFile("file", "a.png", "image/png", new byte[] {1});
        assertThatThrownBy(() -> service.upload(file, null, ClerkMediaUsage.PROFILE))
                .isInstanceOf(CustomException.class)
                .hasMessage("店员信息不存在");
    }

    private MultipartFile buildImageFile(String filename, String contentType) throws IOException {
        BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB);
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            ImageIO.write(image, "png", baos);
            return new MockMultipartFile("file", filename, contentType, baos.toByteArray());
        }
    }

    private PlayClerkUserInfoEntity buildClerk() {
        PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity();
        entity.setId("clerk-1");
        entity.setTenantId("tenant-1");
        return entity;
    }
}
