package org.raddatz.familienarchiv.timeline;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.Test;
import org.raddatz.familienarchiv.PostgresContainerConfig;
import org.raddatz.familienarchiv.document.DatePrecision;
import org.raddatz.familienarchiv.person.Person;
import org.raddatz.familienarchiv.person.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.transaction.annotation.Transactional;
import software.amazon.awssdk.services.s3.S3Client;

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

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

/**
 * Integration tests for {@link TimelineService} and {@link PersonRepository#findByGeneration}
 * against real Postgres. Verifies that assembled output reflects persisted curated events and
 * that the generation query handles null-generation rows correctly.
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@ActiveProfiles("test")
@Import(PostgresContainerConfig.class)
@Transactional
class TimelineServiceIntegrationTest {

    @MockitoBean S3Client s3Client;

    @Autowired TimelineService timelineService;
    @Autowired TimelineEventRepository timelineEventRepository;
    @Autowired PersonRepository personRepository;

    @PersistenceContext EntityManager em;

    // ─── PersonRepository.findByGeneration ────────────────────────────────────

    @Test
    void findByGeneration_returns_matching_persons() {
        personRepository.save(Person.builder().lastName("Gen2A").generation(2).build());
        personRepository.save(Person.builder().lastName("Gen2B").generation(2).build());
        personRepository.save(Person.builder().lastName("Gen3").generation(3).build());
        em.flush();

        List<Person> result = personRepository.findByGeneration(2);

        assertThat(result).extracting(Person::getLastName)
                .containsExactlyInAnyOrder("Gen2A", "Gen2B");
    }

    @Test
    void findByGeneration_returns_empty_list_not_npe_when_no_match() {
        personRepository.save(Person.builder().lastName("Gen1").generation(1).build());
        em.flush();

        List<Person> result = personRepository.findByGeneration(99);

        assertThat(result).isNotNull().isEmpty();
    }

    @Test
    void findByGeneration_does_not_return_null_generation_persons() {
        personRepository.save(Person.builder().lastName("NullGen").build()); // generation stays null
        em.flush();

        List<Person> result = personRepository.findByGeneration(1);

        assertThat(result).extracting(Person::getLastName).doesNotContain("NullGen");
    }

    // ─── TimelineService.assemble end-to-end ─────────────────────────────────

    @Test
    void assemble_includes_persisted_curated_event_in_correct_year_band() {
        UUID actorId = UUID.randomUUID();
        TimelineEvent event = timelineEventRepository.save(TimelineEvent.builder()
                .title("Sarajevo")
                .type(EventType.HISTORICAL)
                .eventDate(LocalDate.of(1914, 6, 28))
                .precision(DatePrecision.DAY)
                .createdBy(actorId)
                .updatedBy(actorId)
                .build());
        em.flush();
        em.clear();

        TimelineDTO result = timelineService.assemble(new TimelineFilter(null, null, null, null, null));

        assertThat(result.years()).anySatisfy(y -> {
            assertThat(y.year()).isEqualTo(1914);
            assertThat(y.entries()).anySatisfy(e -> {
                assertThat(e.title()).isEqualTo("Sarajevo");
                assertThat(e.kind()).isEqualTo(Kind.EVENT);
                assertThat(e.eventId()).isEqualTo(event.getId());
            });
        });
    }
}
