package at.procon.dip.domain.time.service;

import at.procon.dip.domain.document.RepresentationType;
import at.procon.dip.domain.document.entity.Document;
import at.procon.dip.domain.document.entity.DocumentTextRepresentation;
import at.procon.dip.domain.document.repository.DocumentRepository;
import at.procon.dip.domain.document.repository.DocumentTextRepresentationRepository;
import at.procon.dip.domain.document.service.DocumentRepresentationService;
import at.procon.dip.domain.document.service.command.AddDocumentTextRepresentationCommand;
import at.procon.dip.domain.time.config.TimeDomainProperties;
import at.procon.dip.domain.time.entity.TimeEntrySearchProjection;
import at.procon.dip.embedding.config.EmbeddingProperties;
import at.procon.dip.embedding.registry.EmbeddingModelRegistry;
import at.procon.dip.embedding.service.RepresentationEmbeddingOrchestrator;
import at.procon.dip.search.service.DocumentLexicalIndexService;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j
public class TimeEntryRepresentationMaterializationService {

    public static final String BUILDER_KEY = "time-entry-structured-text";

    private final TimeDomainProperties timeDomainProperties;
    private final DocumentRepository documentRepository;
    private final DocumentTextRepresentationRepository representationRepository;
    private final DocumentRepresentationService documentRepresentationService;
    private final DocumentLexicalIndexService lexicalIndexService;
    private final RepresentationEmbeddingOrchestrator embeddingOrchestrator;
    private final EmbeddingProperties embeddingProperties;
    private final EmbeddingModelRegistry modelRegistry;

    //@Transactional
    public void upsertRepresentations(TimeEntrySearchProjection projection) {
        if (projection.getSemanticText() == null || projection.getSemanticText().isBlank()) {
            log.debug("Skipping TIME representation for document {} because semantic text is blank", projection.getDocument().getId());
            return;
        }

        Document document = projection.getDocument();
        document.setTitle(firstNonBlank(projection.getSummaryText(), projection.getTimeRecordingDesc(), projection.getPrimaryTaskName(), projection.getExternalId()));
        document.setSummary(projection.getSummaryText());
        document.setLanguageCode(firstNonBlank(projection.getLanguageCode(), document.getLanguageCode()));
        if (document.getMimeType() == null || document.getMimeType().isBlank()) {
            document.setMimeType("application/x-time-entry");
        }
        document = documentRepository.save(document);

        Optional<DocumentTextRepresentation> existing = representationRepository
                .findByDocument_IdAndRepresentationType(document.getId(), RepresentationType.SEMANTIC_TEXT)
                .stream()
                .filter(r -> BUILDER_KEY.equals(r.getBuilderKey()) || r.isPrimaryRepresentation())
                .findFirst();

        boolean changed = existing.isEmpty()
                || !projection.getSemanticText().equals(existing.get().getTextBody())
                || !equalsNullable(projection.getLanguageCode(), existing.get().getLanguageCode())
                || !BUILDER_KEY.equals(existing.get().getBuilderKey());

        Document finalDocument = document;
        DocumentTextRepresentation semantic = existing
                .map(found -> changed ? updateRepresentation(found, projection) : found)
                .orElseGet(() -> documentRepresentationService.addRepresentation(new AddDocumentTextRepresentationCommand(
                        finalDocument.getId(),
                        null,
                        RepresentationType.SEMANTIC_TEXT,
                        BUILDER_KEY,
                        projection.getLanguageCode(),
                        null,
                        null,
                        null,
                        null,
                        true,
                        projection.getSemanticText(),
                        false
                )));

        if (changed
                && embeddingProperties.isEnabled()
                && timeDomainProperties.getLeitstand().isQueueEmbeddings()
                && embeddingProperties.getDefaultDocumentModel() != null && !embeddingProperties.getDefaultDocumentModel().isBlank()) {
            String modelKey = modelRegistry.getRequiredDefaultDocumentModelKey();
            embeddingOrchestrator.enqueueRepresentation(document.getId(), semantic.getId(), modelKey);
        }
    }

    private DocumentTextRepresentation updateRepresentation(DocumentTextRepresentation existing, TimeEntrySearchProjection projection) {
        existing.setBuilderKey(BUILDER_KEY);
        existing.setLanguageCode(projection.getLanguageCode());
        existing.setPrimaryRepresentation(true);
        existing.setTextBody(projection.getSemanticText());
        existing.setCharCount(projection.getSemanticText().length());
        DocumentTextRepresentation saved = representationRepository.saveAndFlush(existing);
        lexicalIndexService.indexRepresentation(saved.getId());
        return saved;
    }

    private boolean equalsNullable(String left, String right) {
        return left == null ? right == null : left.equals(right);
    }

    private String firstNonBlank(String... values) {
        for (String value : values) {
            if (value != null && !value.trim().isEmpty()) {
                return value.trim();
            }
        }
        return null;
    }
}
