package org.raddatz.familienarchiv.document.transcription;

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.annotation.AnnotationRepository;
import org.raddatz.familienarchiv.document.annotation.DocumentAnnotation;
import org.raddatz.familienarchiv.document.DocumentRepository;
import org.raddatz.familienarchiv.document.transcription.TranscriptionBlock;
import org.raddatz.familienarchiv.document.transcription.TranscriptionBlockRepository;
import org.raddatz.familienarchiv.document.Document;
import org.raddatz.familienarchiv.document.DocumentStatus;
import org.raddatz.familienarchiv.document.BlockSource;
import org.raddatz.familienarchiv.person.Person;
import org.raddatz.familienarchiv.person.PersonRepository;

import org.raddatz.familienarchiv.ocr.TrainingLabel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
import org.springframework.context.annotation.Import;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

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

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import({PostgresContainerConfig.class, FlywayConfig.class})
class TrainingBlockQueryTest {

    @Autowired TranscriptionBlockRepository blockRepository;
    @Autowired DocumentRepository documentRepository;
    @Autowired AnnotationRepository annotationRepository;
    @Autowired PersonRepository personRepository;

    private UUID kurrentDocId;
    private UUID typewriterDocId;
    private UUID kurrentAnnotationId;
    private UUID typewriterAnnotationId;

    @BeforeEach
    void setUp() {
        Document kurrentDoc = documentRepository.save(Document.builder()
                .title("Kurrent Brief")
                .originalFilename("kurrent.pdf")
                .status(DocumentStatus.UPLOADED)
                .trainingLabels(kurrentLabels())
                .build());
        kurrentDocId = kurrentDoc.getId();

        Document typewriterDoc = documentRepository.save(Document.builder()
                .title("Getippter Brief")
                .originalFilename("typed.pdf")
                .status(DocumentStatus.UPLOADED)
                .build());
        typewriterDocId = typewriterDoc.getId();

        kurrentAnnotationId = annotationRepository.save(annotation(kurrentDocId)).getId();
        typewriterAnnotationId = annotationRepository.save(annotation(typewriterDocId)).getId();
    }

    @Test
    void findEligibleKurrentBlocks_includesReviewedManualBlock() {
        blockRepository.save(block(kurrentDocId, kurrentAnnotationId, BlockSource.MANUAL, true));

        List<TranscriptionBlock> result = blockRepository.findEligibleKurrentBlocks();

        assertThat(result).hasSize(1);
        assertThat(result.get(0).getSource()).isEqualTo(BlockSource.MANUAL);
    }

    @Test
    void findEligibleKurrentBlocks_excludesUnreviewedManualBlock() {
        // MANUAL blocks filled by guided OCR but not yet reviewed should not count as checked text
        blockRepository.save(block(kurrentDocId, kurrentAnnotationId, BlockSource.MANUAL, false));

        List<TranscriptionBlock> result = blockRepository.findEligibleKurrentBlocks();

        assertThat(result).isEmpty();
    }

    @Test
    void findEligibleKurrentBlocks_includesReviewedOcrBlock() {
        blockRepository.save(block(kurrentDocId, kurrentAnnotationId, BlockSource.OCR, true));

        List<TranscriptionBlock> result = blockRepository.findEligibleKurrentBlocks();

        assertThat(result).hasSize(1);
        assertThat(result.get(0).isReviewed()).isTrue();
    }

    @Test
    void findEligibleKurrentBlocks_excludesUnreviewedOcrBlock() {
        blockRepository.save(block(kurrentDocId, kurrentAnnotationId, BlockSource.OCR, false));

        List<TranscriptionBlock> result = blockRepository.findEligibleKurrentBlocks();

        assertThat(result).isEmpty();
    }

    @Test
    void findEligibleKurrentBlocks_excludesNonEnrolledDocument() {
        blockRepository.save(block(typewriterDocId, typewriterAnnotationId, BlockSource.MANUAL, false));

        List<TranscriptionBlock> result = blockRepository.findEligibleKurrentBlocks();

        assertThat(result).isEmpty();
    }

    @Test
    void findEligibleKurrentBlocks_returnsAllReviewedBlocksAcrossBothSources() {
        blockRepository.save(block(kurrentDocId, kurrentAnnotationId, BlockSource.MANUAL, true));
        blockRepository.save(block(kurrentDocId, kurrentAnnotationId, BlockSource.OCR, true));
        blockRepository.save(block(kurrentDocId, kurrentAnnotationId, BlockSource.MANUAL, false)); // excluded
        blockRepository.save(block(kurrentDocId, kurrentAnnotationId, BlockSource.OCR, false));   // excluded

        List<TranscriptionBlock> result = blockRepository.findEligibleKurrentBlocks();

        assertThat(result).hasSize(2);
    }

    // ─── sender-based queries ─────────────────────────────────────────────────

    @Test
    void findManualKurrentBlocksByPerson_includesBlockFromKurrentLabelledDocument() {
        Person sender = personRepository.save(Person.builder().firstName("Karl").lastName("Test").build());
        Document doc = documentRepository.save(Document.builder()
                .title("Brief von Karl")
                .originalFilename("karl.pdf")
                .status(DocumentStatus.UPLOADED)
                .sender(sender)
                .trainingLabels(kurrentLabels())
                .build());
        UUID annId = annotationRepository.save(annotation(doc.getId())).getId();
        blockRepository.save(block(doc.getId(), annId, BlockSource.MANUAL, false));

        List<TranscriptionBlock> result = blockRepository.findManualKurrentBlocksByPerson(sender.getId());

        assertThat(result).hasSize(1);
    }

    @Test
    void findManualKurrentBlocksByPerson_excludesDocumentWithoutKurrentLabel() {
        Person sender = personRepository.save(Person.builder().firstName("Karl").lastName("Test").build());
        Document doc = documentRepository.save(Document.builder()
                .title("Brief von Karl")
                .originalFilename("karl.pdf")
                .status(DocumentStatus.UPLOADED)
                .sender(sender)
                .build());
        UUID annId = annotationRepository.save(annotation(doc.getId())).getId();
        blockRepository.save(block(doc.getId(), annId, BlockSource.MANUAL, false));

        List<TranscriptionBlock> result = blockRepository.findManualKurrentBlocksByPerson(sender.getId());

        assertThat(result).isEmpty();
    }

    @Test
    void findManualKurrentBlocksByPerson_excludesOcrBlocks() {
        Person sender = personRepository.save(Person.builder().firstName("Karl").lastName("Test").build());
        Document doc = documentRepository.save(Document.builder()
                .title("Brief von Karl")
                .originalFilename("karl.pdf")
                .status(DocumentStatus.UPLOADED)
                .sender(sender)
                .trainingLabels(kurrentLabels())
                .build());
        UUID annId = annotationRepository.save(annotation(doc.getId())).getId();
        blockRepository.save(block(doc.getId(), annId, BlockSource.OCR, false));

        List<TranscriptionBlock> result = blockRepository.findManualKurrentBlocksByPerson(sender.getId());

        assertThat(result).isEmpty();
    }

    @Test
    void findManualKurrentBlocksByPerson_excludesOtherSender() {
        Person karl = personRepository.save(Person.builder().firstName("Karl").lastName("Test").build());
        Person anna = personRepository.save(Person.builder().firstName("Anna").lastName("Test").build());
        Document doc = documentRepository.save(Document.builder()
                .title("Brief von Karl")
                .originalFilename("karl.pdf")
                .status(DocumentStatus.UPLOADED)
                .sender(karl)
                .trainingLabels(kurrentLabels())
                .build());
        UUID annId = annotationRepository.save(annotation(doc.getId())).getId();
        blockRepository.save(block(doc.getId(), annId, BlockSource.MANUAL, false));

        List<TranscriptionBlock> result = blockRepository.findManualKurrentBlocksByPerson(anna.getId());

        assertThat(result).isEmpty();
    }

    @Test
    void countManualKurrentBlocksByPerson_matchesFindResult() {
        Person sender = personRepository.save(Person.builder().firstName("Karl").lastName("Test").build());
        Document doc = documentRepository.save(Document.builder()
                .title("Brief von Karl")
                .originalFilename("karl.pdf")
                .status(DocumentStatus.UPLOADED)
                .sender(sender)
                .trainingLabels(kurrentLabels())
                .build());
        UUID annId = annotationRepository.save(annotation(doc.getId())).getId();
        blockRepository.save(block(doc.getId(), annId, BlockSource.MANUAL, false));
        blockRepository.save(block(doc.getId(), annId, BlockSource.MANUAL, true));

        long count = blockRepository.countManualKurrentBlocksByPerson(sender.getId());
        List<TranscriptionBlock> found = blockRepository.findManualKurrentBlocksByPerson(sender.getId());

        assertThat(count).isEqualTo(found.size());
    }

    @Test
    void countManualKurrentBlocksByPerson_returnsZeroWhenNoBlocksMatch() {
        Person sender = personRepository.save(Person.builder().firstName("Karl").lastName("Test").build());

        long count = blockRepository.countManualKurrentBlocksByPerson(sender.getId());

        assertThat(count).isZero();
    }

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

    private static Set<TrainingLabel> kurrentLabels() {
        return new HashSet<>(Set.of(TrainingLabel.KURRENT_RECOGNITION));
    }

    private DocumentAnnotation annotation(UUID docId) {
        return DocumentAnnotation.builder()
                .documentId(docId)
                .pageNumber(1)
                .x(0.1).y(0.2).width(0.3).height(0.4)
                .color("#00C7B1")
                .build();
    }

    private TranscriptionBlock block(UUID docId, UUID annotId, BlockSource source, boolean reviewed) {
        return TranscriptionBlock.builder()
                .annotationId(annotId)
                .documentId(docId)
                .text("Liebe Tante")
                .sortOrder(0)
                .source(source)
                .reviewed(reviewed)
                .build();
    }
}
