package org.raddatz.familienarchiv.document.transcription;

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.raddatz.familienarchiv.audit.ActivityActorDTO;
import org.raddatz.familienarchiv.audit.AuditLogQueryService;
import org.raddatz.familienarchiv.document.DocumentService;
import org.raddatz.familienarchiv.document.transcription.TranscriptionQueueItemDTO;
import org.raddatz.familienarchiv.document.transcription.TranscriptionWeeklyStatsDTO;
import org.raddatz.familienarchiv.document.transcription.TranscriptionQueueProjection;
import org.raddatz.familienarchiv.document.transcription.TranscriptionWeeklyStatsProjection;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.UUID;

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

@ExtendWith(MockitoExtension.class)
class TranscriptionQueueServiceTest {

    @Mock DocumentService documentService;
    @Mock AuditLogQueryService auditLogQueryService;
    @InjectMocks TranscriptionQueueService service;

    @BeforeEach
    void stubContributors() {
        lenient().when(auditLogQueryService.findContributorsPerDocument(any())).thenReturn(Map.of());
    }

    // ─── getSegmentationQueue ─────────────────────────────────────────────────

    @Test
    void getSegmentationQueue_delegatesToRepositoryWithDefaultSize() {
        UUID id = UUID.randomUUID();
        TranscriptionQueueProjection proj = mockQueueProjection(id, "Brief von 1920", null, 0, 0, 0);
        when(documentService.findSegmentationQueue(5)).thenReturn(List.of(proj));

        List<TranscriptionQueueItemDTO> result = service.getSegmentationQueue();

        verify(documentService).findSegmentationQueue(5);
        assertThat(result).hasSize(1);
        assertThat(result.get(0).id()).isEqualTo(id);
        assertThat(result.get(0).title()).isEqualTo("Brief von 1920");
        assertThat(result.get(0).documentDate()).isNull();
        assertThat(result.get(0).annotationCount()).isEqualTo(0);
    }

    @Test
    void getSegmentationQueue_returnsEmptyList_whenQueueIsEmpty() {
        when(documentService.findSegmentationQueue(5)).thenReturn(List.of());

        List<TranscriptionQueueItemDTO> result = service.getSegmentationQueue();

        assertThat(result).isEmpty();
        verifyNoInteractions(auditLogQueryService);
    }

    @Test
    void getSegmentationQueue_returnsAllFive_andHasMoreFalse_whenExactlyFiveContributors() {
        UUID docId = UUID.randomUUID();
        TranscriptionQueueProjection proj = mockQueueProjection(docId, "Brief", null, 0, 0, 0);
        when(documentService.findSegmentationQueue(5)).thenReturn(List.of(proj));

        List<ActivityActorDTO> fiveActors = List.of(
            new ActivityActorDTO("A1", "#111", "Alice One"),
            new ActivityActorDTO("A2", "#222", "Alice Two"),
            new ActivityActorDTO("A3", "#333", "Alice Three"),
            new ActivityActorDTO("A4", "#444", "Alice Four"),
            new ActivityActorDTO("A5", "#555", "Alice Five")
        );
        when(auditLogQueryService.findContributorsPerDocument(List.of(docId)))
                .thenReturn(Map.of(docId, fiveActors));

        List<TranscriptionQueueItemDTO> result = service.getSegmentationQueue();

        assertThat(result.get(0).contributors()).hasSize(5);
        assertThat(result.get(0).hasMoreContributors()).isFalse();
    }

    @Test
    void getSegmentationQueue_mapsDocumentDateWhenPresent() {
        LocalDate date = LocalDate.of(1920, 6, 15);
        TranscriptionQueueProjection proj = mockQueueProjection(UUID.randomUUID(), "Brief", date, 0, 0, 0);
        when(documentService.findSegmentationQueue(5)).thenReturn(List.of(proj));

        List<TranscriptionQueueItemDTO> result = service.getSegmentationQueue();

        assertThat(result.get(0).documentDate()).isEqualTo(date);
    }

    // ─── getTranscriptionQueue ────────────────────────────────────────────────

    @Test
    void getTranscriptionQueue_delegatesToRepositoryWithDefaultSize() {
        UUID id = UUID.randomUUID();
        TranscriptionQueueProjection proj = mockQueueProjection(id, "Tagebuch", LocalDate.of(1943, 1, 1), 3, 1, 0);
        when(documentService.findTranscriptionQueue(5)).thenReturn(List.of(proj));

        List<TranscriptionQueueItemDTO> result = service.getTranscriptionQueue();

        verify(documentService).findTranscriptionQueue(5);
        assertThat(result).hasSize(1);
        assertThat(result.get(0).annotationCount()).isEqualTo(3);
        assertThat(result.get(0).textedBlockCount()).isEqualTo(1);
        assertThat(result.get(0).reviewedBlockCount()).isEqualTo(0);
    }

    // ─── getReadyToReadQueue ──────────────────────────────────────────────────

    @Test
    void getReadyToReadQueue_delegatesToRepositoryWithDefaultSize() {
        TranscriptionQueueProjection proj = mockQueueProjection(UUID.randomUUID(), "Urkunde", null, 4, 4, 4);
        when(documentService.findReadyToReadQueue(5)).thenReturn(List.of(proj));

        List<TranscriptionQueueItemDTO> result = service.getReadyToReadQueue();

        verify(documentService).findReadyToReadQueue(5);
        assertThat(result).hasSize(1);
        assertThat(result.get(0).reviewedBlockCount()).isEqualTo(4);
    }

    // ─── getWeeklyStats ───────────────────────────────────────────────────────

    @Test
    void getWeeklyStats_mapsProjectionToDTO() {
        TranscriptionWeeklyStatsProjection proj = mockStatsProjection(3L, 7L);
        when(documentService.findWeeklyStats()).thenReturn(proj);

        TranscriptionWeeklyStatsDTO result = service.getWeeklyStats();

        assertThat(result.segmentationCount()).isEqualTo(3L);
        assertThat(result.transcriptionCount()).isEqualTo(7L);
    }

    @Test
    void getWeeklyStats_returnsZeros_whenAllCountsAreZero() {
        TranscriptionWeeklyStatsProjection proj = mockStatsProjection(0L, 0L);
        when(documentService.findWeeklyStats()).thenReturn(proj);

        TranscriptionWeeklyStatsDTO result = service.getWeeklyStats();

        assertThat(result.segmentationCount()).isEqualTo(0L);
        assertThat(result.transcriptionCount()).isEqualTo(0L);
    }

    // ─── contributors ────────────────────────────────────────────────────────

    @Test
    void getSegmentationQueue_includesContributors_whenAuditDataPresent() {
        UUID docId = UUID.randomUUID();
        TranscriptionQueueProjection proj = mockQueueProjection(docId, "Brief", null, 0, 0, 0);
        when(documentService.findSegmentationQueue(5)).thenReturn(List.of(proj));

        ActivityActorDTO actor = new ActivityActorDTO("MR", "#a6dad8", "Max Raddatz");
        when(auditLogQueryService.findContributorsPerDocument(List.of(docId)))
                .thenReturn(Map.of(docId, List.of(actor)));

        List<TranscriptionQueueItemDTO> result = service.getSegmentationQueue();

        assertThat(result.get(0).contributors()).containsExactly(actor);
        assertThat(result.get(0).hasMoreContributors()).isFalse();
    }

    @Test
    void getSegmentationQueue_capsContributorsAtFive_andSetsHasMoreFlag() {
        UUID docId = UUID.randomUUID();
        TranscriptionQueueProjection proj = mockQueueProjection(docId, "Brief", null, 0, 0, 0);
        when(documentService.findSegmentationQueue(5)).thenReturn(List.of(proj));

        List<ActivityActorDTO> sixActors = List.of(
            new ActivityActorDTO("A1", "#111", "Alice One"),
            new ActivityActorDTO("A2", "#222", "Alice Two"),
            new ActivityActorDTO("A3", "#333", "Alice Three"),
            new ActivityActorDTO("A4", "#444", "Alice Four"),
            new ActivityActorDTO("A5", "#555", "Alice Five"),
            new ActivityActorDTO("A6", "#666", "Alice Six")
        );
        when(auditLogQueryService.findContributorsPerDocument(List.of(docId)))
                .thenReturn(Map.of(docId, sixActors));

        List<TranscriptionQueueItemDTO> result = service.getSegmentationQueue();

        assertThat(result.get(0).contributors()).hasSize(5);
        assertThat(result.get(0).hasMoreContributors()).isTrue();
    }

    // ─── helpers ─────────────────────────────────────────────────────────────

    private TranscriptionQueueProjection mockQueueProjection(
            UUID id, String title, LocalDate documentDate,
            int annotationCount, int textedBlockCount, int reviewedBlockCount) {
        TranscriptionQueueProjection proj = mock(TranscriptionQueueProjection.class);
        when(proj.getId()).thenReturn(id);
        when(proj.getTitle()).thenReturn(title);
        when(proj.getDocumentDate()).thenReturn(documentDate);
        when(proj.getAnnotationCount()).thenReturn(annotationCount);
        when(proj.getTextedBlockCount()).thenReturn(textedBlockCount);
        when(proj.getReviewedBlockCount()).thenReturn(reviewedBlockCount);
        return proj;
    }

    private TranscriptionWeeklyStatsProjection mockStatsProjection(
            long segmentationCount, long transcriptionCount) {
        TranscriptionWeeklyStatsProjection proj = mock(TranscriptionWeeklyStatsProjection.class);
        when(proj.getSegmentationCount()).thenReturn(segmentationCount);
        when(proj.getTranscriptionCount()).thenReturn(transcriptionCount);
        return proj;
    }
}
