package org.raddatz.familienarchiv.document;

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.AuditLogQueryService;
import org.raddatz.familienarchiv.document.annotation.AnnotationService;
import org.raddatz.familienarchiv.document.transcription.TranscriptionBlockQueryService;
import org.raddatz.familienarchiv.document.DocumentSearchResult;
import org.raddatz.familienarchiv.document.DocumentSort;
import org.raddatz.familienarchiv.document.Document;
import org.raddatz.familienarchiv.document.DocumentStatus;
import org.raddatz.familienarchiv.document.DocumentRepository;
import org.raddatz.familienarchiv.filestorage.FileService;
import org.raddatz.familienarchiv.tag.TagService;
import org.raddatz.familienarchiv.person.PersonService;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;

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

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

@ExtendWith(MockitoExtension.class)
class DocumentServiceSortTest {

    private static final Pageable UNPAGED = org.springframework.data.domain.PageRequest.of(0, 10_000);

    @Mock DocumentRepository documentRepository;
    @Mock PersonService personService;
    @Mock FileService fileService;
    @Mock TagService tagService;
    @Mock DocumentVersionService documentVersionService;
    @Mock AnnotationService annotationService;
    @Mock AuditLogQueryService auditLogQueryService;
    @Mock TranscriptionBlockQueryService transcriptionBlockQueryService;
    @InjectMocks DocumentService documentService;

    // ─── searchDocuments — DATE sort ──────────────────────────────────────────

    @Test
    void searchDocuments_with_DATE_sort_and_text_sorts_chronologically_not_by_relevance() {
        UUID id1 = UUID.randomUUID();  // rank position 0 (higher relevance, older doc)
        UUID id2 = UUID.randomUUID();  // rank position 1 (lower relevance, newer doc)

        Document older = Document.builder().id(id1)
                .title("Brief").status(DocumentStatus.UPLOADED)
                .documentDate(LocalDate.of(1940, 1, 1)).build();
        Document newer = Document.builder().id(id2)
                .title("Brief").status(DocumentStatus.UPLOADED)
                .documentDate(LocalDate.of(1960, 1, 1)).build();

        // FTS returns id1 first (higher rank), id2 second
        when(documentRepository.findRankedIdsByFts("Brief")).thenReturn(List.of(id1, id2));
        // findAll(spec, pageable) — the correct date path — returns date-DESC order
        when(documentRepository.findAll(any(Specification.class), any(Pageable.class)))
                .thenReturn(new PageImpl<>(List.of(newer, older)));

        DocumentSearchResult result = documentService.searchDocuments(
                "Brief", null, null, null, null, null, null, null, DocumentSort.DATE, "DESC", null, UNPAGED);

        // Expect: date order (newer 1960 first), NOT rank order (older 1940 first)
        assertThat(result.items()).hasSize(2);
        assertThat(result.items().get(0).document().getId()).isEqualTo(id2);  // newer doc first
    }

    // ─── searchDocuments — RELEVANCE sort ─────────────────────────────────────

    @Test
    void searchDocuments_with_RELEVANCE_sort_and_text_preserves_fts_rank_order() {
        UUID id1 = UUID.randomUUID();  // rank position 0
        UUID id2 = UUID.randomUUID();  // rank position 1

        Document doc1 = Document.builder().id(id1).title("Brief").status(DocumentStatus.UPLOADED).build();
        Document doc2 = Document.builder().id(id2).title("Brief").status(DocumentStatus.UPLOADED).build();

        when(documentRepository.findRankedIdsByFts("Brief")).thenReturn(List.of(id1, id2));
        when(documentRepository.findAll(any(Specification.class)))
                .thenReturn(List.of(doc2, doc1));  // unordered from DB

        DocumentSearchResult result = documentService.searchDocuments(
                "Brief", null, null, null, null, null, null, null, DocumentSort.RELEVANCE, null, null, UNPAGED);

        // Expect: rank order restored (id1 first)
        assertThat(result.items().get(0).document().getId()).isEqualTo(id1);
    }

    @Test
    void searchDocuments_with_null_sort_and_text_defaults_to_fts_rank_order() {
        UUID id1 = UUID.randomUUID();
        UUID id2 = UUID.randomUUID();

        Document doc1 = Document.builder().id(id1).title("Brief").status(DocumentStatus.UPLOADED).build();
        Document doc2 = Document.builder().id(id2).title("Brief").status(DocumentStatus.UPLOADED).build();

        when(documentRepository.findRankedIdsByFts("Brief")).thenReturn(List.of(id1, id2));
        when(documentRepository.findAll(any(Specification.class)))
                .thenReturn(List.of(doc2, doc1));

        DocumentSearchResult result = documentService.searchDocuments(
                "Brief", null, null, null, null, null, null, null, null, null, null, UNPAGED);

        assertThat(result.items().get(0).document().getId()).isEqualTo(id1);
    }
}
