/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.component.docling;

import org.apache.camel.RuntimeCamelException;
import org.apache.camel.spi.Configurer;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriParam;
import org.apache.camel.spi.UriParams;

/**
 * Configuration for the Docling component.
 */
@Configurer
@UriParams
public class DoclingConfiguration implements Cloneable {

    @UriParam
    @Metadata(required = true, defaultValue = "CONVERT_TO_MARKDOWN", description = "The operation to perform",
              enums = "CONVERT_TO_MARKDOWN,CONVERT_TO_HTML,CONVERT_TO_JSON,EXTRACT_TEXT,EXTRACT_STRUCTURED_DATA,SUBMIT_ASYNC_CONVERSION,CHECK_CONVERSION_STATUS,BATCH_CONVERT_TO_MARKDOWN,BATCH_CONVERT_TO_HTML,BATCH_CONVERT_TO_JSON,BATCH_EXTRACT_TEXT,BATCH_EXTRACT_STRUCTURED_DATA,EXTRACT_METADATA,CHUNK_HYBRID,CHUNK_HIERARCHICAL")
    private DoclingOperations operation = DoclingOperations.CONVERT_TO_MARKDOWN;

    @UriParam(label = "advanced")
    @Metadata(description = "Path to Docling Python executable or command")
    private String doclingCommand = "docling";

    @UriParam(label = "advanced")
    @Metadata(description = "Working directory for Docling execution")
    private String workingDirectory;

    @UriParam
    @Metadata(description = "Enable OCR processing for scanned documents", defaultValue = "true")
    private boolean enableOCR = true;

    @UriParam
    @Metadata(description = "Language code for OCR processing", defaultValue = "en",
              enums = "en,es,fr,de,it,pt,ru,zh,ja,ko,ar,hi,th,vi,tr,pl,nl,sv,da,no,fi,cs,hu,el,he,fa,ur,bn,ta,te,ml,kn,gu,pa,or,as,ne,si,my,lo,km,ka,hy,eu,mt,is,ga,cy,gd,br,oc,ca,gl,ast,an,lad,la,eo,ia,ie,vo,jbo,tlh")
    private String ocrLanguage = "en";

    @UriParam
    @Metadata(description = "Output format for document conversion", defaultValue = "markdown",
              enums = "md,json,html,html_split_page,text,doctags")
    private String outputFormat = "markdown";

    @UriParam(label = "advanced")
    @Metadata(description = "Timeout for Docling process execution in milliseconds", defaultValue = "30000")
    private long processTimeout = 30000;

    @UriParam
    @Metadata(description = "Show layout information with bounding boxes", defaultValue = "false")
    private boolean includeLayoutInfo = false;

    @UriParam(label = "security")
    @Metadata(description = "Maximum file size in bytes for processing", defaultValue = "52428800")
    private long maxFileSize = 50 * 1024 * 1024; // 50MB

    @UriParam
    @Metadata(description = "Include the content of the output file in the exchange body and delete the output file",
              defaultValue = "false")
    private boolean contentInBody = false;

    @UriParam
    @Metadata(description = "Use docling-serve API instead of CLI command", defaultValue = "false")
    private boolean useDoclingServe = false;

    @UriParam
    @Metadata(description = "Docling-serve API URL (e.g., http://localhost:5001)", defaultValue = "http://localhost:5001")
    private String doclingServeUrl = "http://localhost:5001";

    @UriParam(label = "security")
    @Metadata(description = "Authentication token for docling-serve API (Bearer token or API key)", security = "secret")
    private String authenticationToken;

    @UriParam(label = "security")
    @Metadata(description = "OAuth profile name for obtaining an access token via the OAuth 2.0 Client Credentials grant. "
                            + "When set, the token is acquired from the configured identity provider and used as authenticationToken. "
                            + "Requires camel-oauth on the classpath.")
    private String oauthProfile;

    @UriParam(label = "security")
    @Metadata(description = "Authentication scheme (BEARER, API_KEY, NONE)", defaultValue = "NONE",
              enums = "BEARER,API_KEY,NONE")
    private AuthenticationScheme authenticationScheme = AuthenticationScheme.NONE;

    @UriParam(label = "security")
    @Metadata(description = "Header name for API key authentication", defaultValue = "X-API-Key")
    private String apiKeyHeader = "X-API-Key";

    @UriParam(label = "advanced")
    @Metadata(description = "Use asynchronous conversion mode (docling-serve API only)", defaultValue = "false")
    private boolean useAsyncMode = false;

    @UriParam(label = "advanced")
    @Metadata(description = "Polling interval for async conversion status in milliseconds", defaultValue = "2000")
    private long asyncPollInterval = 2000;

    @UriParam(label = "advanced")
    @Metadata(description = "Maximum time to wait for async conversion completion in milliseconds", defaultValue = "300000")
    private long asyncTimeout = 300000; // 5 minutes

    @UriParam(label = "advanced")
    @Metadata(description = "Time-to-live for pending async conversion tasks in milliseconds. Tasks older than this will be evicted from memory to prevent leaks.",
              defaultValue = "86400000")
    private long asyncTaskTtl = 86400000; // 24 hours

    @UriParam(label = "batch")
    @Metadata(description = "Number of documents to submit per sub-batch. Documents are partitioned into sub-batches of this size"
                            + " and each sub-batch is processed before starting the next one. Within each sub-batch, up to"
                            + " batchParallelism threads are used concurrently. This controls memory usage and back-pressure"
                            + " when processing large document sets.",
              defaultValue = "10")
    private int batchSize = 10;

    @UriParam(label = "batch")
    @Metadata(description = "Maximum time to wait for batch completion in milliseconds", defaultValue = "300000")
    private long batchTimeout = 300000;

    @UriParam(label = "batch")
    @Metadata(description = "Number of parallel threads for batch processing", defaultValue = "4")
    private int batchParallelism = 4;

    @UriParam(label = "batch")
    @Metadata(description = "Fail entire batch on first error (true) or continue processing remaining documents (false)",
              defaultValue = "true")
    private boolean batchFailOnFirstError = true;

    @UriParam(label = "batch")
    @Metadata(description = "Split batch results into individual exchanges (one per document) instead of single BatchProcessingResults",
              defaultValue = "false")
    private boolean splitBatchResults = false;

    @UriParam(label = "metadata")
    @Metadata(description = "Include metadata in message headers when extracting metadata", defaultValue = "true")
    private boolean includeMetadataInHeaders = true;

    @UriParam(label = "metadata")
    @Metadata(description = "Include raw metadata as returned by the parser", defaultValue = "false")
    private boolean includeRawMetadata = false;

    @UriParam(label = "advanced")
    @Metadata(description = "Enable OCR processing in docling-serve API mode. When not set, the server uses its own defaults. Set enableOCR to false to explicitly disable OCR.")
    private Boolean doOcr;

    @UriParam(label = "advanced")
    @Metadata(description = "Force OCR processing even for digital documents")
    private Boolean forceOcr;

    @UriParam(label = "advanced")
    @Metadata(description = "OCR engine to use", enums = "AUTO,EASYOCR,OCRMAC,RAPIDOCR,TESSEROCR,TESSERACT")
    private String ocrEngine;

    @UriParam(label = "advanced")
    @Metadata(description = "PDF parsing backend", enums = "DLPARSE_V1,DLPARSE_V2,DLPARSE_V4,PYPDFIUM2")
    private String pdfBackend;

    @UriParam(label = "advanced")
    @Metadata(description = "Table structure recognition mode", enums = "ACCURATE,FAST")
    private String tableMode;

    @UriParam(label = "advanced")
    @Metadata(description = "Enable table cell matching post-processing")
    private Boolean tableCellMatching;

    @UriParam(label = "advanced")
    @Metadata(description = "Enable table structure recognition")
    private Boolean doTableStructure;

    @UriParam(label = "advanced")
    @Metadata(description = "Processing pipeline to use", enums = "ASR,STANDARD,VLM")
    private String pipeline;

    @UriParam(label = "advanced")
    @Metadata(description = "Enable code enrichment in document processing")
    private Boolean doCodeEnrichment;

    @UriParam(label = "advanced")
    @Metadata(description = "Enable formula enrichment in document processing")
    private Boolean doFormulaEnrichment;

    @UriParam(label = "advanced")
    @Metadata(description = "Enable picture classification in document processing")
    private Boolean doPictureClassification;

    @UriParam(label = "advanced")
    @Metadata(description = "Enable picture description generation in document processing")
    private Boolean doPictureDescription;

    @UriParam(label = "advanced")
    @Metadata(description = "Include images in the conversion output")
    private Boolean includeImages;

    @UriParam(label = "advanced")
    @Metadata(description = "Image export mode for referenced images", enums = "EMBEDDED,PLACEHOLDER,REFERENCED")
    private String imageExportMode;

    @UriParam(label = "advanced")
    @Metadata(description = "Abort processing on error")
    private Boolean abortOnError;

    @UriParam(label = "advanced")
    @Metadata(description = "Document processing timeout in seconds")
    private Long documentTimeout;

    @UriParam(label = "advanced")
    @Metadata(description = "Scale factor for exported images")
    private Double imagesScale;

    @UriParam(label = "advanced")
    @Metadata(description = "Placeholder string for page breaks in markdown output")
    private String mdPageBreakPlaceholder;

    @UriParam(label = "chunking")
    @Metadata(description = "Tokenizer model for hybrid chunking (e.g. sentence-transformers/all-MiniLM-L6-v2)")
    private String chunkingTokenizer;

    @UriParam(label = "chunking")
    @Metadata(description = "Maximum number of tokens per chunk for hybrid chunking")
    private Integer chunkingMaxTokens;

    @UriParam(label = "chunking")
    @Metadata(description = "Whether to merge peer chunks in hybrid chunking", defaultValue = "true")
    private Boolean chunkingMergePeers;

    @UriParam(label = "chunking")
    @Metadata(description = "Include raw text in chunk output", defaultValue = "false")
    private Boolean chunkingIncludeRawText;

    @UriParam(label = "chunking")
    @Metadata(description = "Use markdown format for tables in chunk output", defaultValue = "false")
    private Boolean chunkingUseMarkdownTables;

    public DoclingOperations getOperation() {
        return operation;
    }

    public void setOperation(DoclingOperations operation) {
        this.operation = operation;
    }

    public String getDoclingCommand() {
        return doclingCommand;
    }

    public void setDoclingCommand(String doclingCommand) {
        this.doclingCommand = doclingCommand;
    }

    public String getWorkingDirectory() {
        return workingDirectory;
    }

    public void setWorkingDirectory(String workingDirectory) {
        this.workingDirectory = workingDirectory;
    }

    public boolean isEnableOCR() {
        return enableOCR;
    }

    public void setEnableOCR(boolean enableOCR) {
        this.enableOCR = enableOCR;
    }

    public String getOcrLanguage() {
        return ocrLanguage;
    }

    public void setOcrLanguage(String ocrLanguage) {
        this.ocrLanguage = ocrLanguage;
    }

    public String getOutputFormat() {
        return outputFormat;
    }

    public void setOutputFormat(String outputFormat) {
        this.outputFormat = outputFormat;
    }

    public long getProcessTimeout() {
        return processTimeout;
    }

    public void setProcessTimeout(long processTimeout) {
        this.processTimeout = processTimeout;
    }

    public boolean isIncludeLayoutInfo() {
        return includeLayoutInfo;
    }

    public void setIncludeLayoutInfo(boolean includeLayoutInfo) {
        this.includeLayoutInfo = includeLayoutInfo;
    }

    public long getMaxFileSize() {
        return maxFileSize;
    }

    public void setMaxFileSize(long maxFileSize) {
        this.maxFileSize = maxFileSize;
    }

    public boolean isContentInBody() {
        return contentInBody;
    }

    public void setContentInBody(boolean contentInBody) {
        this.contentInBody = contentInBody;
    }

    public boolean isUseDoclingServe() {
        return useDoclingServe;
    }

    public void setUseDoclingServe(boolean useDoclingServe) {
        this.useDoclingServe = useDoclingServe;
    }

    public String getDoclingServeUrl() {
        return doclingServeUrl;
    }

    public void setDoclingServeUrl(String doclingServeUrl) {
        this.doclingServeUrl = doclingServeUrl;
    }

    public String getAuthenticationToken() {
        return authenticationToken;
    }

    public void setAuthenticationToken(String authenticationToken) {
        this.authenticationToken = authenticationToken;
    }

    public String getOauthProfile() {
        return oauthProfile;
    }

    public void setOauthProfile(String oauthProfile) {
        this.oauthProfile = oauthProfile;
    }

    public AuthenticationScheme getAuthenticationScheme() {
        return authenticationScheme;
    }

    public void setAuthenticationScheme(AuthenticationScheme authenticationScheme) {
        this.authenticationScheme = authenticationScheme;
    }

    public String getApiKeyHeader() {
        return apiKeyHeader;
    }

    public void setApiKeyHeader(String apiKeyHeader) {
        this.apiKeyHeader = apiKeyHeader;
    }

    public boolean isUseAsyncMode() {
        return useAsyncMode;
    }

    public void setUseAsyncMode(boolean useAsyncMode) {
        this.useAsyncMode = useAsyncMode;
    }

    public long getAsyncPollInterval() {
        return asyncPollInterval;
    }

    public void setAsyncPollInterval(long asyncPollInterval) {
        this.asyncPollInterval = asyncPollInterval;
    }

    public long getAsyncTimeout() {
        return asyncTimeout;
    }

    public void setAsyncTimeout(long asyncTimeout) {
        this.asyncTimeout = asyncTimeout;
    }

    public int getBatchSize() {
        return batchSize;
    }

    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }

    public long getBatchTimeout() {
        return batchTimeout;
    }

    public void setBatchTimeout(long batchTimeout) {
        this.batchTimeout = batchTimeout;
    }

    public int getBatchParallelism() {
        return batchParallelism;
    }

    public void setBatchParallelism(int batchParallelism) {
        this.batchParallelism = batchParallelism;
    }

    public boolean isBatchFailOnFirstError() {
        return batchFailOnFirstError;
    }

    public void setBatchFailOnFirstError(boolean batchFailOnFirstError) {
        this.batchFailOnFirstError = batchFailOnFirstError;
    }

    public boolean isSplitBatchResults() {
        return splitBatchResults;
    }

    public void setSplitBatchResults(boolean splitBatchResults) {
        this.splitBatchResults = splitBatchResults;
    }

    public boolean isIncludeMetadataInHeaders() {
        return includeMetadataInHeaders;
    }

    public void setIncludeMetadataInHeaders(boolean includeMetadataInHeaders) {
        this.includeMetadataInHeaders = includeMetadataInHeaders;
    }

    public boolean isIncludeRawMetadata() {
        return includeRawMetadata;
    }

    public void setIncludeRawMetadata(boolean includeRawMetadata) {
        this.includeRawMetadata = includeRawMetadata;
    }

    public Boolean getDoOcr() {
        return doOcr;
    }

    public void setDoOcr(Boolean doOcr) {
        this.doOcr = doOcr;
    }

    public Boolean getForceOcr() {
        return forceOcr;
    }

    public void setForceOcr(Boolean forceOcr) {
        this.forceOcr = forceOcr;
    }

    public String getOcrEngine() {
        return ocrEngine;
    }

    public void setOcrEngine(String ocrEngine) {
        this.ocrEngine = ocrEngine;
    }

    public String getPdfBackend() {
        return pdfBackend;
    }

    public void setPdfBackend(String pdfBackend) {
        this.pdfBackend = pdfBackend;
    }

    public String getTableMode() {
        return tableMode;
    }

    public void setTableMode(String tableMode) {
        this.tableMode = tableMode;
    }

    public Boolean getTableCellMatching() {
        return tableCellMatching;
    }

    public void setTableCellMatching(Boolean tableCellMatching) {
        this.tableCellMatching = tableCellMatching;
    }

    public Boolean getDoTableStructure() {
        return doTableStructure;
    }

    public void setDoTableStructure(Boolean doTableStructure) {
        this.doTableStructure = doTableStructure;
    }

    public String getPipeline() {
        return pipeline;
    }

    public void setPipeline(String pipeline) {
        this.pipeline = pipeline;
    }

    public Boolean getDoCodeEnrichment() {
        return doCodeEnrichment;
    }

    public void setDoCodeEnrichment(Boolean doCodeEnrichment) {
        this.doCodeEnrichment = doCodeEnrichment;
    }

    public Boolean getDoFormulaEnrichment() {
        return doFormulaEnrichment;
    }

    public void setDoFormulaEnrichment(Boolean doFormulaEnrichment) {
        this.doFormulaEnrichment = doFormulaEnrichment;
    }

    public Boolean getDoPictureClassification() {
        return doPictureClassification;
    }

    public void setDoPictureClassification(Boolean doPictureClassification) {
        this.doPictureClassification = doPictureClassification;
    }

    public Boolean getDoPictureDescription() {
        return doPictureDescription;
    }

    public void setDoPictureDescription(Boolean doPictureDescription) {
        this.doPictureDescription = doPictureDescription;
    }

    public Boolean getIncludeImages() {
        return includeImages;
    }

    public void setIncludeImages(Boolean includeImages) {
        this.includeImages = includeImages;
    }

    public String getImageExportMode() {
        return imageExportMode;
    }

    public void setImageExportMode(String imageExportMode) {
        this.imageExportMode = imageExportMode;
    }

    public Boolean getAbortOnError() {
        return abortOnError;
    }

    public void setAbortOnError(Boolean abortOnError) {
        this.abortOnError = abortOnError;
    }

    public Long getDocumentTimeout() {
        return documentTimeout;
    }

    public void setDocumentTimeout(Long documentTimeout) {
        this.documentTimeout = documentTimeout;
    }

    public Double getImagesScale() {
        return imagesScale;
    }

    public void setImagesScale(Double imagesScale) {
        this.imagesScale = imagesScale;
    }

    public String getMdPageBreakPlaceholder() {
        return mdPageBreakPlaceholder;
    }

    public void setMdPageBreakPlaceholder(String mdPageBreakPlaceholder) {
        this.mdPageBreakPlaceholder = mdPageBreakPlaceholder;
    }

    public String getChunkingTokenizer() {
        return chunkingTokenizer;
    }

    public void setChunkingTokenizer(String chunkingTokenizer) {
        this.chunkingTokenizer = chunkingTokenizer;
    }

    public Integer getChunkingMaxTokens() {
        return chunkingMaxTokens;
    }

    public void setChunkingMaxTokens(Integer chunkingMaxTokens) {
        this.chunkingMaxTokens = chunkingMaxTokens;
    }

    public Boolean getChunkingMergePeers() {
        return chunkingMergePeers;
    }

    public void setChunkingMergePeers(Boolean chunkingMergePeers) {
        this.chunkingMergePeers = chunkingMergePeers;
    }

    public Boolean getChunkingIncludeRawText() {
        return chunkingIncludeRawText;
    }

    public void setChunkingIncludeRawText(Boolean chunkingIncludeRawText) {
        this.chunkingIncludeRawText = chunkingIncludeRawText;
    }

    public Boolean getChunkingUseMarkdownTables() {
        return chunkingUseMarkdownTables;
    }

    public void setChunkingUseMarkdownTables(Boolean chunkingUseMarkdownTables) {
        this.chunkingUseMarkdownTables = chunkingUseMarkdownTables;
    }

    public long getAsyncTaskTtl() {
        return asyncTaskTtl;
    }

    public void setAsyncTaskTtl(long asyncTaskTtl) {
        this.asyncTaskTtl = asyncTaskTtl;
    }

    public DoclingConfiguration copy() {
        try {
            return (DoclingConfiguration) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeCamelException(e);
        }
    }

}
