package org.raddatz.familienarchiv.document;

import io.swagger.v3.oas.annotations.media.Schema;
import org.junit.jupiter.api.Test;
import org.raddatz.familienarchiv.audit.ActivityActorDTO;
import org.springframework.data.domain.PageRequest;

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

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

class DocumentSearchResultTest {

    private DocumentListItem item(UUID docId) {
        return new DocumentListItem(
                docId, "Test", "test.pdf", null, null,
                DatePrecision.UNKNOWN, null, null,
                List.of(), List.of(), null, null, null, null,
                0, List.of(), SearchMatchData.empty(),
                LocalDateTime.of(2026, 1, 15, 10, 0), LocalDateTime.of(2026, 1, 15, 10, 0));
    }

    @Test
    void of_totalElements_equals_list_size_for_unpaged_shortcut() {
        DocumentSearchResult result = DocumentSearchResult.of(
                List.of(item(UUID.randomUUID()), item(UUID.randomUUID())));

        assertThat(result.totalElements()).isEqualTo(2L);
        assertThat(result.pageNumber()).isZero();
        assertThat(result.pageSize()).isEqualTo(2);
        assertThat(result.totalPages()).isEqualTo(1);
    }

    @Test
    void of_empty_shortcut_has_zero_totalPages() {
        DocumentSearchResult result = DocumentSearchResult.of(List.of());

        assertThat(result.totalElements()).isZero();
        assertThat(result.totalPages()).isZero();
    }

    @Test
    void paged_factory_populates_paging_fields_from_pageable_and_total() {
        List<DocumentListItem> slice = List.of(item(UUID.randomUUID()), item(UUID.randomUUID()));

        DocumentSearchResult result = DocumentSearchResult.paged(slice, PageRequest.of(1, 50), 120L);

        assertThat(result.items()).hasSize(2);
        assertThat(result.totalElements()).isEqualTo(120L);
        assertThat(result.pageNumber()).isEqualTo(1);
        assertThat(result.pageSize()).isEqualTo(50);
        assertThat(result.totalPages()).isEqualTo(3); // ceil(120 / 50)
    }

    @Test
    void paged_factory_totalPages_rounds_up_on_remainder() {
        DocumentSearchResult result =
                DocumentSearchResult.paged(List.of(), PageRequest.of(0, 7), 30L);

        assertThat(result.totalPages()).isEqualTo(5); // ceil(30 / 7)
    }

    @Test
    void of_exposes_items_with_completion_and_contributors() {
        UUID id = UUID.randomUUID();
        ActivityActorDTO actor = new ActivityActorDTO("AB", "#f00", "Anna Braun");
        DocumentListItem item = new DocumentListItem(
                id, "T", "t.pdf", null, null,
                DatePrecision.UNKNOWN, null, null,
                List.of(), List.of(), null, null, null, null,
                75, List.of(actor), SearchMatchData.empty(),
                LocalDateTime.of(2026, 1, 15, 10, 0), LocalDateTime.of(2026, 1, 15, 10, 0));

        DocumentSearchResult result = DocumentSearchResult.of(List.of(item));

        assertThat(result.items()).hasSize(1);
        assertThat(result.items().get(0).completionPercentage()).isEqualTo(75);
        assertThat(result.items().get(0).contributors()).containsExactly(actor);
    }

    @Test
    void items_component_is_annotated_as_required_in_openapi_schema() throws NoSuchFieldException {
        Schema schema = DocumentSearchResult.class.getDeclaredField("items").getAnnotation(Schema.class);
        assertThat(schema).isNotNull();
        assertThat(schema.requiredMode()).isEqualTo(Schema.RequiredMode.REQUIRED);
    }

    @Test
    void totalElements_component_is_annotated_as_required_in_openapi_schema() throws NoSuchFieldException {
        Schema schema = DocumentSearchResult.class.getDeclaredField("totalElements").getAnnotation(Schema.class);
        assertThat(schema).isNotNull();
        assertThat(schema.requiredMode()).isEqualTo(Schema.RequiredMode.REQUIRED);
    }

    @Test
    void paging_components_are_annotated_as_required_in_openapi_schema() throws NoSuchFieldException {
        for (String name : List.of("pageNumber", "pageSize", "totalPages")) {
            Schema schema = DocumentSearchResult.class.getDeclaredField(name).getAnnotation(Schema.class);
            assertThat(schema).as(name + " must have @Schema").isNotNull();
            assertThat(schema.requiredMode()).isEqualTo(Schema.RequiredMode.REQUIRED);
        }
    }

    @Test
    void undatedCount_component_is_annotated_as_required_in_openapi_schema() throws NoSuchFieldException {
        Schema schema = DocumentSearchResult.class.getDeclaredField("undatedCount").getAnnotation(Schema.class);
        assertThat(schema).isNotNull();
        assertThat(schema.requiredMode()).isEqualTo(Schema.RequiredMode.REQUIRED);
    }

    @Test
    void factories_default_undatedCount_to_zero() {
        assertThat(DocumentSearchResult.of(List.of()).undatedCount()).isZero();
        assertThat(DocumentSearchResult.paged(List.of(), PageRequest.of(0, 50), 0L).undatedCount()).isZero();
    }

    @Test
    void withUndatedCount_overlays_count_and_preserves_other_fields() {
        DocumentSearchResult base = DocumentSearchResult.paged(
                List.of(item(UUID.randomUUID())), PageRequest.of(1, 50), 120L);

        DocumentSearchResult withCount = base.withUndatedCount(7L);

        assertThat(withCount.undatedCount()).isEqualTo(7L);
        assertThat(withCount.items()).isEqualTo(base.items());
        assertThat(withCount.totalElements()).isEqualTo(120L);
        assertThat(withCount.pageNumber()).isEqualTo(1);
        assertThat(withCount.pageSize()).isEqualTo(50);
        assertThat(withCount.totalPages()).isEqualTo(3);
    }
}
