// This file is auto-generated by alef — DO NOT EDIT.
// alef:hash:4e15143f4af1ae8bafbdb1506ef057da924484c66a19483966333558ad437e75
// To regenerate: alef generate
// To verify freshness: alef verify --exit-code
// Issues & docs: https://github.com/kreuzberg-dev/alef
#nullable enable

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Kreuzberg;

/// <summary>
/// Main extraction configuration.
///
/// This struct contains all configuration options for the extraction process.
/// It can be loaded from TOML, YAML, or JSON files, or created programmatically.
/// </summary>
public sealed record ExtractionConfig
{
    /// <summary>
    /// Enable caching of extraction results
    /// </summary>
    [JsonPropertyName("use_cache")]
    public bool UseCache { get; init; } = true;

    /// <summary>
    /// Enable quality post-processing
    /// </summary>
    [JsonPropertyName("enable_quality_processing")]
    public bool EnableQualityProcessing { get; init; } = true;

    /// <summary>
    /// OCR configuration (null = OCR disabled)
    /// </summary>
    [JsonPropertyName("ocr")]
    public OcrConfig? Ocr { get; init; } = null;

    /// <summary>
    /// Force OCR even for searchable PDFs
    /// </summary>
    [JsonPropertyName("force_ocr")]
    public bool ForceOcr { get; init; } = false;

    /// <summary>
    /// Force OCR on specific pages only (1-indexed page numbers, must be &gt;= 1).
    ///
    /// When set, only the listed pages are OCR'd regardless of text layer quality.
    /// Unlisted pages use native text extraction. Ignored when `force_ocr` is `true`.
    /// Only applies to PDF documents. Duplicates are automatically deduplicated.
    /// An `ocr` config is recommended for backend/language selection; defaults are used if absent.
    /// </summary>
    [JsonPropertyName("force_ocr_pages")]
    public List<uint>? ForceOcrPages { get; init; } = null;

    /// <summary>
    /// Disable OCR entirely, even for images.
    ///
    /// When `true`, OCR is skipped for all document types. Images return metadata
    /// only (dimensions, format, EXIF) without text extraction. PDFs use only
    /// native text extraction without OCR fallback.
    ///
    /// Cannot be `true` simultaneously with `force_ocr`.
    ///
    /// *Added in v4.7.0.*
    /// </summary>
    [JsonPropertyName("disable_ocr")]
    public bool DisableOcr { get; init; } = false;

    /// <summary>
    /// Text chunking configuration (null = chunking disabled)
    /// </summary>
    [JsonPropertyName("chunking")]
    public ChunkingConfig? Chunking { get; init; } = null;

    /// <summary>
    /// Content filtering configuration (null = use extractor defaults).
    ///
    /// Controls whether document "furniture" (headers, footers, watermarks,
    /// repeating text) is included in or stripped from extraction results.
    /// See `ContentFilterConfig` for per-field documentation.
    /// </summary>
    [JsonPropertyName("content_filter")]
    public ContentFilterConfig? ContentFilter { get; init; } = null;

    /// <summary>
    /// Image extraction configuration (null = no image extraction)
    /// </summary>
    [JsonPropertyName("images")]
    public ImageExtractionConfig? Images { get; init; } = null;

    /// <summary>
    /// PDF-specific options (null = use defaults)
    /// </summary>
    [JsonPropertyName("pdf_options")]
    public PdfConfig? PdfOptions { get; init; } = null;

    /// <summary>
    /// Token reduction configuration (null = no token reduction)
    /// </summary>
    [JsonPropertyName("token_reduction")]
    public TokenReductionOptions? TokenReduction { get; init; } = null;

    /// <summary>
    /// Language detection configuration (null = no language detection)
    /// </summary>
    [JsonPropertyName("language_detection")]
    public LanguageDetectionConfig? LanguageDetection { get; init; } = null;

    /// <summary>
    /// Page extraction configuration (null = no page tracking)
    /// </summary>
    [JsonPropertyName("pages")]
    public PageConfig? Pages { get; init; } = null;

    /// <summary>
    /// Keyword extraction configuration (null = no keyword extraction)
    /// </summary>
    [JsonPropertyName("keywords")]
    public KeywordConfig? Keywords { get; init; } = null;

    /// <summary>
    /// Post-processor configuration (null = use defaults)
    /// </summary>
    [JsonPropertyName("postprocessor")]
    public PostProcessorConfig? Postprocessor { get; init; } = null;

    /// <summary>
    /// HTML to Markdown conversion options (null = use defaults)
    ///
    /// Configure how HTML documents are converted to Markdown, including heading styles,
    /// list formatting, code block styles, and preprocessing options.
    /// </summary>
    [JsonPropertyName("html_options")]
    public string? HtmlOptions { get; init; } = null;

    /// <summary>
    /// Styled HTML output configuration.
    ///
    /// When set alongside `output_format = OutputFormat.Html`, the extraction
    /// pipeline uses `StyledHtmlRenderer`(crate.rendering.StyledHtmlRenderer)
    /// which emits stable `kb-*` CSS class hooks on every structural element
    /// and optionally embeds theme CSS or user-supplied CSS in a `&lt;style&gt;` block.
    ///
    /// When `None`, the existing plain comrak-based HTML renderer is used.
    /// </summary>
    [JsonPropertyName("html_output")]
    public HtmlOutputConfig? HtmlOutput { get; init; } = null;

    /// <summary>
    /// Default per-file timeout in seconds for batch extraction.
    ///
    /// When set, each file in a batch will be canceled after this duration
    /// unless overridden by `FileExtractionConfig.timeout_secs`.
    ///
    /// Defaults to `Some(60)` to prevent pathological files (e.g. deeply
    /// nested archives, documents with millions of cells) from running
    /// indefinitely and exhausting caller resources. Set to `None` to
    /// disable the timeout for trusted input or long-running workloads.
    /// </summary>
    [JsonPropertyName("extraction_timeout_secs")]
    public ulong? ExtractionTimeoutSecs { get; init; } = null;

    /// <summary>
    /// Maximum concurrent extractions in batch operations (null = (num_cpus × 1.5).ceil()).
    ///
    /// Limits parallelism to prevent resource exhaustion when processing
    /// large batches. Defaults to (num_cpus × 1.5).ceil() when not set.
    /// </summary>
    [JsonPropertyName("max_concurrent_extractions")]
    public ulong? MaxConcurrentExtractions { get; init; } = null;

    /// <summary>
    /// Result structure format
    ///
    /// Controls whether results are returned in unified format (default) with all
    /// content in the `content` field, or element-based format with semantic
    /// elements (for Unstructured-compatible output).
    /// </summary>
    [JsonPropertyName("result_format")]
    public ResultFormat? ResultFormat { get; init; } = null;

    /// <summary>
    /// Security limits for archive extraction.
    ///
    /// Controls maximum archive size, compression ratio, file count, and other
    /// security thresholds to prevent decompression bomb attacks. Also caps
    /// nesting depth, iteration count, entity / token length, total
    /// content size, and table cell count for every extraction path that
    /// ingests user-controlled bytes.
    /// When `None`, default limits are used.
    /// </summary>
    [JsonPropertyName("security_limits")]
    public SecurityLimits? SecurityLimits { get; init; } = null;

    /// <summary>
    /// Maximum uncompressed size in bytes for a single embedded file before
    /// recursive extraction is attempted (default: 50 MiB).
    ///
    /// Applies to embedded objects inside OOXML containers (DOCX, PPTX) and
    /// to email attachments processed via recursive extraction. Files that
    /// exceed this limit are skipped with a `ProcessingWarning` rather than
    /// passed to the extraction pipeline, preventing a single oversized
    /// embedded object from consuming unbounded memory or time.
    ///
    /// Set to `None` to disable the per-embedded-file cap (falls back to
    /// `security_limits.max_archive_size` as the only guard).
    /// </summary>
    [JsonPropertyName("max_embedded_file_bytes")]
    public ulong? MaxEmbeddedFileBytes { get; init; } = null;

    /// <summary>
    /// Content text format (default: Plain).
    ///
    /// Controls the format of the extracted content:
    /// - `Plain`: Raw extracted text (default)
    /// - `Markdown`: Markdown formatted output
    /// - `Djot`: Djot markup format (requires djot feature)
    /// - `Html`: HTML formatted output
    ///
    /// When set to a structured format, extraction results will include
    /// formatted output. The `formatted_content` field may be populated
    /// when format conversion is applied.
    /// </summary>
    [JsonPropertyName("output_format")]
    public OutputFormat OutputFormat { get; init; } = OutputFormat.Plain;

    /// <summary>
    /// Layout detection configuration (null = layout detection disabled).
    ///
    /// When set, PDF pages and images are analyzed for document structure
    /// (headings, code, formulas, tables, figures, etc.) using RT-DETR models
    /// via ONNX Runtime. For PDFs, layout hints override paragraph classification
    /// in the markdown pipeline. For images, per-region OCR is performed with
    /// markdown formatting based on detected layout classes.
    /// Requires the `layout-detection` feature to run inference; the field is
    /// present whenever the `layout-types` feature is active (which includes
    /// `layout-detection` as well as the no-ORT target groups).
    /// </summary>
    [JsonPropertyName("layout")]
    public LayoutDetectionConfig? Layout { get; init; } = null;

    /// <summary>
    /// Run layout detection on the non-OCR PDF markdown path.
    ///
    /// When `true` and `layout` is `Some(_)`, layout regions inform heading,
    /// table, list, and figure detection in the structure pipeline that would
    /// otherwise rely on font-clustering heuristics alone. Significantly
    /// improves SF1 (structural F1) at the cost of inference latency
    /// (~150-300ms/page CPU, ~20-50ms/page GPU). Default: `false`.
    /// Requires the `layout-detection` feature.
    /// </summary>
    [JsonPropertyName("use_layout_for_markdown")]
    public bool UseLayoutForMarkdown { get; init; } = false;

    /// <summary>
    /// Enable structured document tree output.
    ///
    /// When true, populates the `document` field on `ExtractionResult` with a
    /// hierarchical `DocumentStructure` containing heading-driven section nesting,
    /// table grids, content layer classification, and inline annotations.
    ///
    /// Independent of `result_format` — can be combined with Unified or ElementBased.
    /// </summary>
    [JsonPropertyName("include_document_structure")]
    public bool IncludeDocumentStructure { get; init; } = false;

    /// <summary>
    /// Hardware acceleration configuration for ONNX Runtime models.
    ///
    /// Controls execution provider selection for layout detection and embedding
    /// models. When `None`, uses platform defaults (CoreML on macOS, CUDA on
    /// Linux, CPU on Windows).
    /// </summary>
    [JsonPropertyName("acceleration")]
    public AccelerationConfig? Acceleration { get; init; } = null;

    /// <summary>
    /// Cache namespace for tenant isolation.
    ///
    /// When set, cache entries are stored under `{cache_dir}/{namespace}/`.
    /// Must be alphanumeric, hyphens, or underscores only (max 64 chars).
    /// Different namespaces have isolated cache spaces on the same filesystem.
    /// </summary>
    [JsonPropertyName("cache_namespace")]
    public string? CacheNamespace { get; init; } = null;

    /// <summary>
    /// Per-request cache TTL in seconds.
    ///
    /// Overrides the global `max_age_days` for this specific extraction.
    /// When `0`, caching is completely skipped (no read or write).
    /// When `None`, the global TTL applies.
    /// </summary>
    [JsonPropertyName("cache_ttl_secs")]
    public ulong? CacheTtlSecs { get; init; } = null;

    /// <summary>
    /// Email extraction configuration (null = use defaults).
    ///
    /// Currently supports configuring the fallback codepage for MSG files
    /// that do not specify one. See `EmailConfig` for details.
    /// </summary>
    [JsonPropertyName("email")]
    public EmailConfig? Email { get; init; } = null;

    /// <summary>
    /// Concurrency limits for constrained environments (null = use defaults).
    ///
    /// Controls Rayon thread pool size, ONNX Runtime intra-op threads, and
    /// (when `max_concurrent_extractions` is unset) the batch concurrency
    /// semaphore. See `ConcurrencyConfig` for details.
    /// </summary>
    [JsonPropertyName("concurrency")]
    public string? Concurrency { get; init; } = null;

    /// <summary>
    /// Maximum recursion depth for archive extraction (default: 3).
    /// Set to 0 to disable recursive extraction (legacy behavior).
    /// </summary>
    [JsonPropertyName("max_archive_depth")]
    public ulong MaxArchiveDepth { get; init; } = 0;

    /// <summary>
    /// Tree-sitter language pack configuration (null = tree-sitter disabled).
    ///
    /// When set, enables code file extraction using tree-sitter parsers.
    /// Controls grammar download behavior and code analysis options.
    /// </summary>
    [JsonPropertyName("tree_sitter")]
    public TreeSitterConfig? TreeSitter { get; init; } = null;

    /// <summary>
    /// Structured extraction via LLM (null = disabled).
    ///
    /// When set, the extracted document content is sent to an LLM with the
    /// provided JSON schema. The structured response is stored in
    /// `ExtractionResult.structured_output`.
    /// </summary>
    [JsonPropertyName("structured_extraction")]
    public StructuredExtractionConfig? StructuredExtraction { get; init; } = null;

    /// <summary>
    /// Cancellation token for this extraction (null = no external cancellation).
    ///
    /// Pass a `CancellationToken` clone here and call `CancellationToken.cancel`
    /// from another thread / task to abort the extraction in progress. The extractor
    /// checks the token at safe checkpoints (before lock acquisition, between pages,
    /// between batch items) and returns `KreuzbergError.Cancelled` when set.
    ///
    /// The field is excluded from serialization because `CancellationToken` is a
    /// runtime handle, not a configuration value.
    /// </summary>
    [JsonPropertyName("cancel_token")]
    public string? CancelToken { get; init; } = null;


    /// <summary>
    /// Parse a <see cref="ExtractionConfig"/> from a JSON string.
    /// </summary>
    /// <exception cref="KreuzbergException">When the JSON cannot be deserialised.</exception>
    public static ExtractionConfig FromJson(string json)
    {
        try
        {
            return JsonSerializer.Deserialize<ExtractionConfig>(json, JsonOptions)
                ?? throw new KreuzbergException($"Failed to parse ExtractionConfig from JSON: deserializer returned null");
        }
        catch (KreuzbergException)
        {
            throw;
        }
        catch (Exception e)
        {
            throw new KreuzbergException($"Failed to parse ExtractionConfig from JSON: {e.Message}", e);
        }
    }

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
        Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) },
    };

    /// <summary>Options for serializing config/input objects to FFI. Strips nulls
    /// (nullable C# fields default to null and would override required Rust fields with
    /// non-deserialisable nulls) but preserves explicit false/0 so caller intent is kept.</summary>
    private static readonly JsonSerializerOptions JsonSerializationOptions = new()
    {
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) },
    };

    public static ExtractionConfig Default()
    {
        var nativeResult = NativeMethods.ExtractionConfigDefault();
        var jsonPtr = NativeMethods.ExtractionConfigToJson(nativeResult);
        var json = global::System.Runtime.InteropServices.Marshal.PtrToStringUTF8(jsonPtr);
        NativeMethods.FreeString(jsonPtr);
        NativeMethods.ExtractionConfigFree(nativeResult);
        return JsonSerializer.Deserialize<ExtractionConfig>(json ?? "null", JsonOptions)!;
    }
}
