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

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

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.media.entity.PlayMediaEntity;
import com.starry.admin.modules.media.service.IPlayMediaService;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;

@ExtendWith(MockitoExtension.class)
class PlayClerkMediaAssetServiceImplTest {

    @Mock
    private IPlayMediaService mediaService;

    @Test
    void submitWithOrderShouldReindexAndFlagPending() {
        PlayClerkMediaAssetServiceImpl service = org.mockito.Mockito.spy(new PlayClerkMediaAssetServiceImpl());
        ReflectionTestUtils.setField(service, "mediaService", mediaService);
        List<PlayClerkMediaAssetEntity> assets = Arrays.asList(
                buildAsset("A", "media-a", 0),
                buildAsset("B", "media-b", 1),
                buildAsset("C", "media-c", 2));

        doReturn(assets).when(service).listActiveByUsage("clerk-1", ClerkMediaUsage.PROFILE);

        java.util.List<java.util.List<PlayClerkMediaAssetEntity>> batches = new java.util.ArrayList<>();
        org.mockito.Mockito.doAnswer(inv -> {
            java.util.Collection<PlayClerkMediaAssetEntity> collection = inv.getArgument(0);
            batches.add(new java.util.ArrayList<>(collection));
            return true;
        }).when(service).updateBatchById(any());

        service.submitWithOrder("clerk-1", ClerkMediaUsage.PROFILE, Arrays.asList("media-b", "media-a"));

        List<PlayClerkMediaAssetEntity> updates = flatten(batches);
        PlayClerkMediaAssetEntity b = find(updates, "B");
        PlayClerkMediaAssetEntity a = find(updates, "A");
        PlayClerkMediaAssetEntity c = find(updates, "C");

        assertThat(b.getOrderIndex()).isEqualTo(0);
        assertThat(b.getReviewState()).isEqualTo(ClerkMediaReviewState.PENDING.getCode());
        assertThat(a.getOrderIndex()).isEqualTo(1);
        assertThat(a.getReviewState()).isEqualTo(ClerkMediaReviewState.PENDING.getCode());
        assertThat(c.getReviewState()).isEqualTo(ClerkMediaReviewState.REJECTED.getCode());
    }

    @Test
    void applyReviewDecisionShouldApproveAndReject() {
        PlayClerkMediaAssetServiceImpl service = org.mockito.Mockito.spy(new PlayClerkMediaAssetServiceImpl());
        ReflectionTestUtils.setField(service, "mediaService", mediaService);
        List<PlayClerkMediaAssetEntity> assets = Arrays.asList(
                buildAsset("A", "media-1", 0),
                buildAsset("B", "media-2", 1));

        doReturn(assets).when(service).listActiveByUsage("clerk-1", ClerkMediaUsage.PROFILE);
        when(mediaService.listByIds(any())).thenReturn(Arrays.asList(
                buildMedia("media-1"),
                buildMedia("media-2")));

        java.util.List<java.util.List<PlayClerkMediaAssetEntity>> batches = new java.util.ArrayList<>();
        org.mockito.Mockito.doAnswer(inv -> {
            java.util.Collection<PlayClerkMediaAssetEntity> collection = inv.getArgument(0);
            batches.add(new java.util.ArrayList<>(collection));
            return true;
        }).when(service).updateBatchById(any());

        service.applyReviewDecision("clerk-1", ClerkMediaUsage.PROFILE, Arrays.asList("media-2"), "review-1", "ok");

        List<PlayClerkMediaAssetEntity> updates = flatten(batches);
        PlayClerkMediaAssetEntity approved = find(updates, "B");
        PlayClerkMediaAssetEntity rejected = find(updates, "A");

        assertThat(approved.getReviewState()).isEqualTo(ClerkMediaReviewState.APPROVED.getCode());
        assertThat(approved.getOrderIndex()).isEqualTo(0);
        assertThat(approved.getReviewRecordId()).isEqualTo("review-1");
        assertThat(rejected.getReviewState()).isEqualTo(ClerkMediaReviewState.REJECTED.getCode());
    }

    private static PlayClerkMediaAssetEntity buildAsset(String id, String mediaId, int order) {
        PlayClerkMediaAssetEntity entity = new PlayClerkMediaAssetEntity();
        entity.setId(id);
        entity.setClerkId("clerk-1");
        entity.setTenantId("tenant-1");
        entity.setMediaId(mediaId);
        entity.setUsage(ClerkMediaUsage.PROFILE.getCode());
        entity.setReviewState(ClerkMediaReviewState.DRAFT.getCode());
        entity.setOrderIndex(order);
        entity.setSubmittedTime(LocalDateTime.now());
        return entity;
    }

    private static PlayMediaEntity buildMedia(String id) {
        PlayMediaEntity media = new PlayMediaEntity();
        media.setId(id);
        media.setUrl("url-" + id);
        return media;
    }

    private static PlayClerkMediaAssetEntity find(List<PlayClerkMediaAssetEntity> updates, String id) {
        return updates.stream()
                .filter(asset -> id.equals(asset.getId()))
                .findFirst()
                .orElseThrow();
    }

    @SuppressWarnings("unchecked")
    private static List<PlayClerkMediaAssetEntity> flatten(List<? extends List<PlayClerkMediaAssetEntity>> captured) {
        return captured.stream()
                .flatMap(List::stream)
                .collect(Collectors.toCollection(ArrayList::new));
    }
}
