package at.procon.ted.camel;

import at.procon.ted.config.TedProcessorProperties;
import at.procon.ted.service.ExcelExportService;
import at.procon.ted.service.SimilaritySearchService;
import at.procon.ted.service.SimilaritySearchService.SimilaritySearchResponse;
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.io.File;
import java.nio.file.Paths;

/**
 * Apache Camel route for processing Solution Brief PDF files.
 *
 * Features:
 * - Scans input directory for PDF files
 * - Performs semantic similarity search against TED documents
 * - Generates Excel (XLSX) reports with hyperlinks to matching tenders
 * - Files are NOT moved (noop mode)
 * - Idempotent processing to avoid reprocessing same files
 *
 * @author Martin.Schweitzer@procon.co.at and claude.ai
 */
@Component
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
@RequiredArgsConstructor
@Slf4j
public class SolutionBriefRoute extends RouteBuilder {

    private static final String ROUTE_ID = "solution-brief-processor";

    private final TedProcessorProperties properties;
    private final SimilaritySearchService similaritySearchService;
    private final ExcelExportService excelExportService;

    @Override
    public void configure() throws Exception {
        TedProcessorProperties.SolutionBriefProperties config = properties.getSolutionBrief();

        if (!config.isEnabled()) {
            log.info("Solution Brief processing is disabled, skipping route configuration");
            return;
        }

        // Determine result directory (absolute or relative to input)
        String resultDir = config.getResultDirectory();
        if (!Paths.get(resultDir).isAbsolute()) {
            resultDir = Paths.get(config.getInputDirectory(), resultDir).toString();
        }

        // Ensure directories exist
        File inputDir = new File(config.getInputDirectory());
        File outputDir = new File(resultDir);
        if (!inputDir.exists()) {
            inputDir.mkdirs();
            log.info("Created Solution Brief input directory: {}", inputDir.getAbsolutePath());
        }
        if (!outputDir.exists()) {
            outputDir.mkdirs();
            log.info("Created Solution Brief result directory: {}", outputDir.getAbsolutePath());
        }

        final String finalResultDir = resultDir;

        String fileUri = buildFileUri(config);
        log.info("=== Solution Brief Route Configuration ===");
        log.info("Input Directory: {}", config.getInputDirectory());
        log.info("Input Directory exists: {}", inputDir.exists());
        log.info("Input Directory is directory: {}", inputDir.isDirectory());
        log.info("Result Directory: {}", resultDir);
        log.info("File Pattern: {}", config.getFilePattern());
        log.info("TopK: {}, Threshold: {}", config.getTopK(), config.getSimilarityThreshold());
        log.info("Poll Interval: {}ms", config.getPollInterval());
        log.info("Idempotent: {}", config.isIdempotent());
        log.info("File URI: {}", fileUri);
        log.info("===========================================");

        // List existing PDFs in directory
        File[] pdfFiles = inputDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".pdf"));
        if (pdfFiles != null && pdfFiles.length > 0) {
            log.info("Found {} PDF files in input directory:", pdfFiles.length);
            for (File pdf : pdfFiles) {
                log.info("  - {} ({} bytes)", pdf.getName(), pdf.length());
            }
        } else {
            log.warn("No PDF files found in input directory: {}", inputDir.getAbsolutePath());
        }

        // Error handler - handled(false) ensures file goes to .error directory
        onException(Exception.class)
                .routeId("solution-brief-error-handler")
                .handled(false)
                .log(LoggingLevel.ERROR, "Solution Brief processing failed for ${header.CamelFileName}: ${exception.message}")
                .process(exchange -> {
                    Exception exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
                    String filename = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
                    log.error("Error processing Solution Brief '{}': {}", filename,
                            exception != null ? exception.getMessage() : "Unknown error", exception);
                });

        // Main processing route
        from(fileUri)
                .routeId(ROUTE_ID)
                .log(LoggingLevel.INFO, "Processing Solution Brief PDF: ${header.CamelFileName}")
                .process(exchange -> {
                    String filename = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
                    byte[] pdfData = exchange.getIn().getBody(byte[].class);

                    if (pdfData == null || pdfData.length == 0) {
                        log.warn("Empty PDF file: {}", filename);
                        exchange.setProperty("skipProcessing", true);
                        return;
                    }

                    log.info("Searching similar documents for: {} ({} bytes)", filename, pdfData.length);

                    try {
                        // Perform similarity search
                        SimilaritySearchResponse response = similaritySearchService.searchByPdf(
                                pdfData,
                                filename,
                                config.getTopK(),
                                config.getSimilarityThreshold()
                        );

                        if (response.getResults().isEmpty()) {
                            log.info("No similar documents found for: {}", filename);
                            exchange.setProperty("noResults", true);
                            return;
                        }

                        log.info("Found {} similar documents for: {}", response.getResultCount(), filename);

                        // Export to Excel
                        String excelPath = excelExportService.exportToExcel(
                                response,
                                filename,
                                finalResultDir
                        );

                        exchange.getIn().setHeader("excelOutputPath", excelPath);
                        exchange.getIn().setHeader("resultCount", response.getResultCount());

                        log.info("Excel report generated: {} ({} results)", excelPath, response.getResultCount());

                    } catch (Exception e) {
                        log.error("Failed to process Solution Brief '{}': {}", filename, e.getMessage(), e);
                        throw e;
                    }
                })
                .choice()
                    .when(exchangeProperty("skipProcessing").isEqualTo(true))
                        .log(LoggingLevel.WARN, "Skipped empty PDF: ${header.CamelFileName}")
                    .when(exchangeProperty("noResults").isEqualTo(true))
                        .log(LoggingLevel.INFO, "No similar documents found for: ${header.CamelFileName}")
                    .otherwise()
                        .log(LoggingLevel.INFO, "Solution Brief completed: ${header.CamelFileName} -> ${header.excelOutputPath} (${header.resultCount} results)")
                .end();
    }

    /**
     * Build the file URI for the Solution Brief consumer.
     */
    private String buildFileUri(TedProcessorProperties.SolutionBriefProperties config) {
        String directory = config.getInputDirectory().replace("\\", "/");

        StringBuilder uri = new StringBuilder("file:");
        uri.append(directory);
        uri.append("?includeExt=pdf");
        uri.append("&delay=").append(config.getPollInterval());
        uri.append("&move=.done");
        uri.append("&moveFailed=.error");

        return uri.toString();
    }
}
