package org.raddatz.familienarchiv.document;

import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.raddatz.familienarchiv.PostgresContainerConfig;
import org.raddatz.familienarchiv.config.FlywayConfig;
import org.raddatz.familienarchiv.document.DocumentRepository;
import org.raddatz.familienarchiv.document.Document;
import org.raddatz.familienarchiv.document.DocumentStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;

import java.util.List;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;

/**
 * Repository-level integration tests for {@code findFtsPageRaw}: verifies that the
 * paginated FTS query returns exactly page-size rows and that the window-function
 * total reflects the full match count, not just the page count.
 *
 * <p>Uses real Postgres via Testcontainers so the GIN index, tsvector trigger, and
 * {@code websearch_to_tsquery} semantics are identical to production.
 *
 * <p>{@code AFTER_CLASS} dirty-context keeps the Spring context alive for all tests
 * in this class and rebuilds it once at the end, rather than after every test.
 */
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import({PostgresContainerConfig.class, FlywayConfig.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class DocumentFtsPagedIntegrationTest {

    @Autowired DocumentRepository documentRepository;
    @Autowired EntityManager em;

    // 60 docs match "Walter"; 10 docs with "Hans" do not.
    private static final int WALTER_COUNT = 60;
    private static final int PAGE_SIZE = 50;

    @BeforeEach
    void seed() {
        documentRepository.deleteAll();
        em.flush();
        for (int i = 0; i < WALTER_COUNT; i++) {
            documentRepository.saveAndFlush(doc("Brief von Walter Nr. " + i));
        }
        for (int i = 0; i < 10; i++) {
            documentRepository.saveAndFlush(doc("Brief von Hans Nr. " + i));
        }
        em.clear();
    }

    @Test
    void findFtsPageRaw_firstPage_returnsPageSizeRows() {
        List<Object[]> rows = documentRepository.findFtsPageRaw("Walter", 0, PAGE_SIZE);

        assertThat(rows).hasSize(PAGE_SIZE);
    }

    @Test
    void findFtsPageRaw_windowTotal_equalsFullMatchCount_notPageSize() {
        List<Object[]> rows = documentRepository.findFtsPageRaw("Walter", 0, PAGE_SIZE);

        long total = ((Number) rows.get(0)[2]).longValue();
        assertThat(total).isEqualTo(WALTER_COUNT);
    }

    @Test
    void findFtsPageRaw_lastPage_returnsRemainder() {
        int remainder = WALTER_COUNT % PAGE_SIZE; // 60 % 50 = 10
        List<Object[]> rows = documentRepository.findFtsPageRaw("Walter", PAGE_SIZE, PAGE_SIZE);

        assertThat(rows).hasSize(remainder);
        long total = ((Number) rows.get(0)[2]).longValue();
        assertThat(total).isEqualTo(WALTER_COUNT);
    }

    @Test
    void findFtsPageRaw_noMatches_returnsEmptyList() {
        List<Object[]> rows = documentRepository.findFtsPageRaw("XYZ_KEIN_TREFFER", 0, PAGE_SIZE);

        assertThat(rows).isEmpty();
    }

    @Test
    void findFtsPageRaw_stopwordOnlyQuery_returnsEmptyList_noException() {
        assertThatNoException().isThrownBy(() -> {
            List<Object[]> rows = documentRepository.findFtsPageRaw("der die das und", 0, PAGE_SIZE);
            assertThat(rows).isEmpty();
        });
    }

    // ─── Helper ───────────────────────────────────────────────────────────────

    private Document doc(String title) {
        return Document.builder()
                .title(title)
                .originalFilename(title.replace(" ", "_") + ".pdf")
                .status(DocumentStatus.UPLOADED)
                .build();
    }
}
