package com.starry.admin.modules.clerk.service.impl;

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.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
import com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity;
import com.starry.admin.modules.clerk.module.entity.PlayClerkMediaAssetEntity;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.module.enums.ClerkDataType;
import com.starry.admin.modules.clerk.module.vo.PlayClerkDataReviewReturnVo;
import com.starry.admin.modules.clerk.module.vo.PlayClerkDataReviewStateEditVo;
import com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.media.entity.PlayMediaEntity;
import com.starry.admin.modules.media.service.IPlayMediaService;
import com.starry.common.enums.ClerkReviewState;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
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.test.util.ReflectionTestUtils;

@ExtendWith(MockitoExtension.class)
class PlayClerkDataReviewInfoServiceImplTest {

    @Mock
    private IPlayClerkUserInfoService clerkUserInfoService;
    @Mock
    private IPlayClerkMediaAssetService clerkMediaAssetService;
    @Mock
    private IPlayMediaService mediaService;

    private PlayClerkDataReviewInfoServiceImpl service;

    @BeforeEach
    void setUp() {
        service = spy(new PlayClerkDataReviewInfoServiceImpl());
        ReflectionTestUtils.setField(service, "playClerkUserInfoService", clerkUserInfoService);
        ReflectionTestUtils.setField(service, "clerkMediaAssetService", clerkMediaAssetService);
        ReflectionTestUtils.setField(service, "mediaService", mediaService);
        org.mockito.Mockito.lenient()
                .doReturn(true)
                .when(service)
                .update(any(PlayClerkDataReviewInfoEntity.class));
    }

    @Test
    void updateDataReviewStateShouldSynchronizeAlbumOnApproval() {
        PlayClerkDataReviewInfoEntity review = buildReview("review-1", "clerk-1", "2",
                Arrays.asList("media-existing", "media-new"));
        doReturn(review).when(service).selectPlayClerkDataReviewInfoById("review-1");

        PlayClerkUserInfoEntity clerk = new PlayClerkUserInfoEntity();
        clerk.setId("clerk-1");
        clerk.setTenantId("tenant-1");
        when(clerkUserInfoService.getById("clerk-1")).thenReturn(clerk);

        PlayMediaEntity existingMedia = new PlayMediaEntity();
        existingMedia.setId("media-existing");
        when(mediaService.getById("media-existing")).thenReturn(existingMedia);
        PlayMediaEntity newMedia = new PlayMediaEntity();
        newMedia.setId("media-new");
        when(mediaService.getById("media-new")).thenReturn(newMedia);

        PlayClerkMediaAssetEntity assetStub = new PlayClerkMediaAssetEntity();
        assetStub.setId("asset-1");
        when(clerkMediaAssetService.linkDraftAsset(any(), any(), any(), any())).thenReturn(assetStub);

        PlayClerkDataReviewStateEditVo vo = new PlayClerkDataReviewStateEditVo();
        vo.setId("review-1");
        vo.setReviewState(ClerkReviewState.APPROVED);
        vo.setReviewCon("ok");

        service.updateDataReviewState(vo);

        verify(clerkMediaAssetService).linkDraftAsset(eq("tenant-1"), eq("clerk-1"), eq("media-existing"),
                eq(ClerkMediaUsage.PROFILE));
        verify(clerkMediaAssetService).linkDraftAsset(eq("tenant-1"), eq("clerk-1"), eq("media-new"), eq(ClerkMediaUsage.PROFILE));
        verify(clerkMediaAssetService).applyReviewDecision(eq("clerk-1"), eq(ClerkMediaUsage.PROFILE),
                eq(Arrays.asList("media-existing", "media-new")), eq("review-1"), eq("ok"));
        verify(clerkUserInfoService).update(any(PlayClerkUserInfoEntity.class));
    }

    @Test
    void updateDataReviewStateThrowsWhenClerkMissing() {
        PlayClerkDataReviewInfoEntity review = buildReview("review-2", "ghost", "2",
                Collections.singletonList("media"));
        doReturn(review).when(service).selectPlayClerkDataReviewInfoById("review-2");
        when(clerkUserInfoService.getById("ghost")).thenReturn(null);

        PlayClerkDataReviewStateEditVo vo = new PlayClerkDataReviewStateEditVo();
        vo.setId("review-2");
        vo.setReviewState(ClerkReviewState.APPROVED);

        assertThatThrownBy(() -> service.updateDataReviewState(vo))
                .isInstanceOf(CustomException.class)
                .hasMessageContaining("店员信息不存在");
    }

    @Test
    void enrichDataContentWithMediaPreviewPopulatesCoverAndVideoUrls() {
        PlayClerkDataReviewInfoServiceImpl impl = service;

        PlayClerkDataReviewReturnVo videoRow = new PlayClerkDataReviewReturnVo();
        videoRow.setId("row-video");
        videoRow.setDataTypeEnum(ClerkDataType.PHOTO_ALBUM);
        videoRow.setDataContent(java.util.Collections.singletonList("media-video"));

        PlayClerkDataReviewReturnVo imageRow = new PlayClerkDataReviewReturnVo();
        imageRow.setId("row-image");
        imageRow.setDataTypeEnum(ClerkDataType.PHOTO_ALBUM);
        imageRow.setDataContent(java.util.Collections.singletonList("media-image"));

        IPage<PlayClerkDataReviewReturnVo> page = new Page<>(1, 10);
        page.setRecords(java.util.Arrays.asList(videoRow, imageRow));

        PlayMediaEntity videoMedia = new PlayMediaEntity();
        videoMedia.setId("media-video");
        videoMedia.setKind(com.starry.admin.modules.media.enums.MediaKind.VIDEO.getCode());
        videoMedia.setUrl("https://oss/video.mp4");
        videoMedia.setCoverUrl("https://oss/video-cover.jpg");

        PlayMediaEntity imageMedia = new PlayMediaEntity();
        imageMedia.setId("media-image");
        imageMedia.setKind(com.starry.admin.modules.media.enums.MediaKind.IMAGE.getCode());
        imageMedia.setUrl("https://oss/image.png");

        when(mediaService.getById("media-video")).thenReturn(videoMedia);
        when(mediaService.getById("media-image")).thenReturn(imageMedia);

        org.springframework.test.util.ReflectionTestUtils.invokeMethod(
                impl, "enrichDataContentWithMediaPreview", page);

        assertThat(videoRow.getDataContent()).containsExactly("https://oss/video-cover.jpg");
        assertThat(videoRow.getMediaVideoUrls()).containsExactly("https://oss/video.mp4");

        assertThat(imageRow.getDataContent()).containsExactly("https://oss/image.png");
        assertThat(imageRow.getMediaVideoUrls()).containsExactly((String) null);
    }

    @Test
    void enrichDataContentWithMediaPreviewFallsBackToLegacyUrlWhenMediaMissing() {
        PlayClerkDataReviewInfoServiceImpl impl = service;

        String legacyUrl = "https://oss/legacy-only.png";
        PlayClerkDataReviewReturnVo row = new PlayClerkDataReviewReturnVo();
        row.setId("row-legacy");
        row.setDataTypeEnum(ClerkDataType.PHOTO_ALBUM);
        row.setDataContent(java.util.Collections.singletonList(legacyUrl));

        IPage<PlayClerkDataReviewReturnVo> page = new Page<>(1, 10);
        page.setRecords(java.util.Collections.singletonList(row));

        // 当无法通过 mediaId 查询到记录时，应当保持 dataContent 为原始 URL，
        // 并且对应的 mediaVideoUrls 位置为 null。
        when(mediaService.getById(legacyUrl)).thenReturn(null);

        org.springframework.test.util.ReflectionTestUtils.invokeMethod(
                impl, "enrichDataContentWithMediaPreview", page);

        assertThat(row.getDataContent()).containsExactly(legacyUrl);
        assertThat(row.getMediaVideoUrls()).containsExactly((String) null);
    }

    @Test
    void updateDataReviewStateUpdatesAvatarOnApproval() {
        PlayClerkDataReviewInfoEntity review = buildReview("review-avatar", "clerk-1", "1",
                java.util.Collections.singletonList("https://oss/avatar.png"));
        // 审核服务内部通过 selectPlayClerkDataReviewInfoById 查询审核记录
        org.mockito.Mockito.doReturn(review).when(service).selectPlayClerkDataReviewInfoById("review-avatar");

        PlayClerkDataReviewStateEditVo vo = new PlayClerkDataReviewStateEditVo();
        vo.setId("review-avatar");
        vo.setReviewState(ClerkReviewState.APPROVED);

        service.updateDataReviewState(vo);

        ArgumentCaptor<PlayClerkUserInfoEntity> captor = ArgumentCaptor.forClass(PlayClerkUserInfoEntity.class);
        verify(clerkUserInfoService).update(captor.capture());
        PlayClerkUserInfoEntity updated = captor.getValue();
        assertThat(updated.getId()).isEqualTo("clerk-1");
        assertThat(updated.getAvatar()).isEqualTo("https://oss/avatar.png");
        // 其他字段不应被误改
        assertThat(updated.getNickname()).isNull();
        assertThat(updated.getAudio()).isNull();
    }

    @Test
    void updateDataReviewStateUpdatesAudioOnApproval() {
        PlayClerkDataReviewInfoEntity review = buildReview("review-audio", "clerk-2", "3",
                java.util.Collections.singletonList("https://oss/audio.m4a"));
        org.mockito.Mockito.doReturn(review).when(service).selectPlayClerkDataReviewInfoById("review-audio");

        PlayClerkDataReviewStateEditVo vo = new PlayClerkDataReviewStateEditVo();
        vo.setId("review-audio");
        vo.setReviewState(ClerkReviewState.APPROVED);

        service.updateDataReviewState(vo);

        ArgumentCaptor<PlayClerkUserInfoEntity> captor = ArgumentCaptor.forClass(PlayClerkUserInfoEntity.class);
        verify(clerkUserInfoService, org.mockito.Mockito.atLeastOnce()).update(captor.capture());
        PlayClerkUserInfoEntity updated = captor.getValue();
        assertThat(updated.getId()).isEqualTo("clerk-2");
        assertThat(updated.getAudio()).isEqualTo("https://oss/audio.m4a");
        assertThat(updated.getNickname()).isNull();
        assertThat(updated.getAvatar()).isNull();
    }

    private PlayClerkDataReviewInfoEntity buildReview(String id, String clerkId, String dataType,
            java.util.List<String> payload) {
        PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
        entity.setId(id);
        entity.setClerkId(clerkId);
        entity.setDataType(dataType);
        entity.setDataContent(payload);
        entity.setAddTime(LocalDateTime.now());
        return entity;
    }
}
