package at.procon.dip.vectorization.camel;

import at.procon.dip.domain.document.EmbeddingStatus;
import at.procon.dip.domain.document.repository.DocumentEmbeddingRepository;
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
import at.procon.dip.runtime.config.RuntimeMode;
import at.procon.dip.vectorization.service.DocumentEmbeddingProcessingService;
import at.procon.ted.config.LegacyVectorizationProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.Exchange;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.dataformat.JsonLibrary;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

/**
 * Legacy generic vectorization route.
 * Uses DOC.doc_text_representation as the source text and DOC.doc_embedding as the write target
 * but belongs to the old runtime graph and is therefore activated only in LEGACY mode.
 */
@Component
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
@RequiredArgsConstructor
@Slf4j
public class GenericVectorizationRoute extends RouteBuilder {

    private static final String ROUTE_ID_TRIGGER = "generic-vectorization-trigger";
    private static final String ROUTE_ID_PROCESSOR = "generic-vectorization-processor";
    private static final String ROUTE_ID_SCHEDULER = "generic-vectorization-scheduler";

    private final LegacyVectorizationProperties properties;
    private final DocumentEmbeddingRepository embeddingRepository;
    private final DocumentEmbeddingProcessingService processingService;

    private java.util.concurrent.ExecutorService executorService() {
        return java.util.concurrent.Executors.newFixedThreadPool(
                1,
                r -> {
                    Thread thread = new Thread(r);
                    thread.setName("doc-vectorization-" + thread.getId());
                    thread.setDaemon(true);
                    thread.setPriority(Thread.MAX_PRIORITY);
                    return thread;
                }
        );
    }

    @Override
    public void configure() {
        if (!properties.isEnabled() || !properties.isGenericPipelineEnabled()) {
            log.info("Phase 2 generic vectorization route disabled");
            return;
        }

        log.info("Configuring generic vectorization routes (legacy mode, apiUrl={}, scheduler={}ms)",
                properties.getApiUrl(),
                properties.getGenericSchedulerPeriodMs());

        onException(Exception.class)
                .handled(true)
                .process(exchange -> {
                    UUID embeddingId = exchange.getIn().getHeader("embeddingId", UUID.class);
                    Exception exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
                    log.error("Generic vectorization failed for embedding {}: {}", embeddingId,
                            exception != null ? exception.getMessage() : "unknown error", exception);
                    if (embeddingId != null) {
                        processingService.markAsFailed(embeddingId,
                                exception != null ? exception.getMessage() : "Unknown vectorization error");
                    }
                })
                .log(LoggingLevel.WARN, "Generic vectorization exception handled for ${header.embeddingId}");

        from("direct:vectorize-embedding")
                .routeId(ROUTE_ID_TRIGGER)
                .setHeader("embeddingId", header("embeddingId"))
                .to("seda:vectorize-embedding-async");

        from("seda:vectorize-embedding-async?concurrentConsumers=1&blockWhenFull=true&size=1000")
                .routeId(ROUTE_ID_PROCESSOR)
                .threads().executorService(executorService())
                .process(exchange -> {
                    UUID embeddingId = exchange.getIn().getHeader("embeddingId", UUID.class);
                    if (embeddingId == null) {
                        exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE);
                        return;
                    }

                    DocumentEmbeddingProcessingService.EmbeddingPayload payload =
                            processingService.prepareEmbeddingForVectorization(embeddingId);
                    if (payload == null) {
                        exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE);
                        return;
                    }
                    exchange.getIn().setBody(payload);
                })
                .filter(exchangeProperty(Exchange.ROUTE_STOP).isNull())
                .process(exchange -> {
                    DocumentEmbeddingProcessingService.EmbeddingPayload payload =
                            exchange.getIn().getBody(DocumentEmbeddingProcessingService.EmbeddingPayload.class);
                    VectorizationRequest request = new VectorizationRequest(payload.textContent(), false);
                    exchange.getIn().setBody(request);
                })
                .marshal().json(JsonLibrary.Jackson)
                .removeHeaders("CamelHttp*")
                .setHeader(Exchange.HTTP_METHOD, constant("POST"))
                .setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
                .toD(properties.getApiUrl() + "/embed?bridgeEndpoint=true&throwExceptionOnFailure=true")
                .unmarshal().json(JsonLibrary.Jackson, VectorizationResponse.class)
                .process(exchange -> {
                    UUID embeddingId = exchange.getIn().getHeader("embeddingId", UUID.class);
                    VectorizationResponse response = exchange.getIn().getBody(VectorizationResponse.class);
                    if (response == null || response.embedding() == null) {
                        throw new IllegalStateException("Embedding service returned empty response");
                    }
                    processingService.saveEmbedding(embeddingId, response.embedding(), response.tokenCount());
                });

        from("timer:generic-vectorization-poller?period=" + properties.getGenericSchedulerPeriodMs())
                .routeId(ROUTE_ID_SCHEDULER)
                .process(exchange -> {
                    List<UUID> ids = embeddingRepository.findIdsByEmbeddingStatus(
                            EmbeddingStatus.PENDING, PageRequest.of(0, properties.getBatchSize()));
                    exchange.getIn().setBody(ids);
                })
                .split(body())
                .setHeader("embeddingId", body())
                .to("direct:vectorize-embedding");
    }

    public record VectorizationRequest(
            @JsonProperty("text") String text,
            @JsonProperty("isQuery") boolean isQuery
    ) {
    }

    public record VectorizationResponse(
            @JsonProperty("embedding") float[] embedding,
            @JsonProperty("token_count") Integer tokenCount
    ) {
    }
}