package at.procon.ted.camel;

import at.procon.ted.config.TedProcessorProperties;
import at.procon.ted.service.DocumentProcessingService;
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
import at.procon.dip.runtime.config.RuntimeMode;
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.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.nio.file.Path;

/**
 * Apache Camel route for watching and processing TED XML documents.
 * 
 * Features:
 * - Recursive directory scanning for *.xml files
 * - File locking to prevent concurrent processing
 * - Move to .processed or .error directories after processing
 * - Error handling with retry and dead letter channel
 * 
 * @author Martin.Schweitzer@procon.co.at and claude.ai
 */
@Component
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
@RequiredArgsConstructor
@Slf4j
public class TedDocumentRoute extends RouteBuilder {

    private static final String ROUTE_ID = "ted-document-processor";
    private static final String ROUTE_ID_PROCESSOR = "ted-document-processor-handler";

    private final TedProcessorProperties properties;
    private final DocumentProcessingService documentProcessingService;

    @Override
    public void configure() throws Exception {
        // Error handler configuration
        errorHandler(deadLetterChannel("direct:error-handler")
                .maximumRedeliveries(3)
                .redeliveryDelay(1000)
                .retryAttemptedLogLevel(LoggingLevel.WARN)
                .logStackTrace(true)
                .logRetryAttempted(true)
                .logExhausted(true));

        // Error handler route
        from("direct:error-handler")
                .routeId("ted-error-handler")
                .process(exchange -> {
                    Exception exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
                    if (exception != null) {
                        log.debug("Document processing error", exception);
                    }
                })
                .to(buildErrorUri());

        // Main file processing route - DISABLED
        // File consumer disabled to prevent memory leak with package download route
        // The package download route processes XML files directly after extraction
        // from(buildFileUri())
        // from("file://{{ted.input.directory}}")
        //         .routeId(ROUTE_ID)
        //         .to("direct:process-document");

        // Document processing sub-route
        from("direct:process-document")
                .routeId(ROUTE_ID_PROCESSOR)
                .process(exchange -> {
                    // Extract file information
                    String filename = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
                    String absolutePath = exchange.getIn().getHeader(Exchange.FILE_PATH, String.class);
                    Long fileSize = exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class);
                    
                    // Read XML content
                    byte[] body = exchange.getIn().getBody(byte[].class);
                    String xmlContent = new String(body, StandardCharsets.UTF_8);
                    
                    log.debug("Processing file: {} ({} bytes)", filename, fileSize);
                    
                    // Process the document
                    DocumentProcessingService.ProcessingResult result = 
                            documentProcessingService.processDocument(xmlContent, filename, absolutePath, fileSize);
                    
                    // Set result in exchange for logging
                    exchange.setProperty("processingResult", result);
                    
                    if (result.isError()) {
                        throw new RuntimeException("Document processing failed: " + result.errorMessage());
                    }
                })
                .choice()
                    .when(exchange -> {
                        DocumentProcessingService.ProcessingResult result =
                                exchange.getProperty("processingResult", DocumentProcessingService.ProcessingResult.class);
                        return result != null && result.isDuplicate();
                    })
                        // Move duplicate to processed directory (it's not an error)
                        .to(buildProcessedUri())
                    .otherwise()
                        // Vectorization is already triggered in DocumentProcessingService after DB save
                        .to(buildProcessedUri())
                .end();
    }

    /**
     * Build the file component URI for watching the input directory.
     */
    private String buildFileUri() {
        TedProcessorProperties.InputProperties input = properties.getInput();

        // Normalize path for Camel (convert backslashes to forward slashes)
        String directory = input.getDirectory().replace("\\", "/");

        StringBuilder uri = new StringBuilder("file:");
        uri.append(directory);
        
        uri.append("?");
        // Recursive scanning only if pattern contains **
        boolean recursive = input.getPattern().contains("**");
        uri.append("recursive=").append(recursive);
        // File pattern (always use antInclude for Ant-style patterns like *.xml)
        uri.append("&antInclude=").append(input.getPattern());
        // Polling configuration
        uri.append("&delay=").append(input.getPollInterval());
        uri.append("&maxMessagesPerPoll=").append(input.getMaxMessagesPerPoll());
        // Read lock strategy to prevent processing incomplete files
        uri.append("&readLock=changed");
        uri.append("&readLockCheckInterval=1000");
        uri.append("&readLockTimeout=30000");
        // Move files after processing
        uri.append("&move=").append(input.getProcessedDirectory());
        uri.append("&moveFailed=").append(input.getErrorDirectory());
        // Sort by name for consistent ordering
        uri.append("&sortBy=file:name");
        // Don't process hidden files
        uri.append("&exclude=.*");
        // Max depth only for recursive scanning
        if (recursive) {
            uri.append("&maxDepth=10");
        }

        log.info("File consumer URI: {}", uri);
        return uri.toString();
    }

    /**
     * Build URI for successfully processed files.
     */
    private String buildProcessedUri() {
        // Files are automatically moved by the file component's 'move' option
        // This is a no-op endpoint used for explicit routing
        return "log:ted-processed?level=DEBUG";
    }

    /**
     * Build URI for failed files.
     */
    private String buildErrorUri() {
        // Files are automatically moved by the file component's 'moveFailed' option
        return "log:ted-error?level=ERROR";
    }
}
