// 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>
/// General extraction result used by the core extraction API.
///
/// This is the main result type returned by all extraction functions.
/// </summary>
public sealed record ExtractionResult
{
    [JsonPropertyName("content")]
    public string Content { get; init; } = "";

    [JsonPropertyName("mime_type")]
    public string MimeType { get; init; } = "";

    [JsonPropertyName("metadata")]
    public Metadata Metadata { get; init; } = default!;

    /// <summary>
    /// Extraction strategy used to produce the returned text.
    ///
    /// Populated when the extractor can reliably distinguish native text extraction,
    /// OCR-only extraction, or mixed native/OCR output.
    /// </summary>
    [JsonPropertyName("extraction_method")]
    public ExtractionMethod? ExtractionMethod { get; init; } = null;

    [JsonPropertyName("tables")]
    public List<Table> Tables { get; init; } = [];

    [JsonPropertyName("detected_languages")]
    public List<string>? DetectedLanguages { get; init; } = null;

    /// <summary>
    /// Text chunks when chunking is enabled.
    ///
    /// When chunking configuration is provided, the content is split into
    /// overlapping chunks for efficient processing. Each chunk contains the text,
    /// optional embeddings (if enabled), and metadata about its position.
    /// </summary>
    [JsonPropertyName("chunks")]
    public List<Chunk>? Chunks { get; init; } = null;

    /// <summary>
    /// Extracted images from the document.
    ///
    /// When image extraction is enabled via `ImageExtractionConfig`, this field
    /// contains all images found in the document with their raw data and metadata.
    /// Each image may optionally contain a nested `ocr_result` if OCR was performed.
    /// </summary>
    [JsonPropertyName("images")]
    public List<ExtractedImage>? Images { get; init; } = null;

    /// <summary>
    /// Per-page content when page extraction is enabled.
    ///
    /// When page extraction is configured, the document is split into per-page content
    /// with tables and images mapped to their respective pages.
    /// </summary>
    [JsonPropertyName("pages")]
    public List<PageContent>? Pages { get; init; } = null;

    /// <summary>
    /// Semantic elements when element-based result format is enabled.
    ///
    /// When result_format is set to ElementBased, this field contains semantic
    /// elements with type classification, unique identifiers, and metadata for
    /// Unstructured-compatible element-based processing.
    /// </summary>
    [JsonPropertyName("elements")]
    public List<Element>? Elements { get; init; } = null;

    /// <summary>
    /// Rich Djot content structure (when extracting Djot documents).
    ///
    /// When extracting Djot documents with structured extraction enabled,
    /// this field contains the full semantic structure including:
    /// - Block-level elements with nesting
    /// - Inline formatting with attributes
    /// - Links, images, footnotes
    /// - Math expressions
    /// - Complete attribute information
    ///
    /// The `content` field still contains plain text for backward compatibility.
    ///
    /// Always `None` for non-Djot documents.
    /// </summary>
    [JsonPropertyName("djot_content")]
    public DjotContent? DjotContent { get; init; } = null;

    /// <summary>
    /// OCR elements with full spatial and confidence metadata.
    ///
    /// When OCR is performed with element extraction enabled, this field contains
    /// the structured representation of detected text including:
    /// - Bounding geometry (rectangles or quadrilaterals)
    /// - Confidence scores (detection and recognition)
    /// - Rotation information
    /// - Hierarchical relationships (Tesseract only)
    ///
    /// This field preserves all metadata that would otherwise be lost when
    /// converting to plain text or markdown output formats.
    ///
    /// Only populated when `OcrElementConfig.include_elements` is true.
    /// </summary>
    [JsonPropertyName("ocr_elements")]
    public List<OcrElement>? OcrElements { get; init; } = null;

    /// <summary>
    /// Structured document tree (when document structure extraction is enabled).
    ///
    /// When `include_document_structure` is true in `ExtractionConfig`, this field
    /// contains the full hierarchical representation of the document including:
    /// - Heading-driven section nesting
    /// - Table grids with cell-level metadata
    /// - Content layer classification (body, header, footer, footnote)
    /// - Inline text annotations (formatting, links)
    /// - Bounding boxes and page numbers
    ///
    /// Independent of `result_format` — can be combined with Unified or ElementBased.
    /// </summary>
    [JsonPropertyName("document")]
    public DocumentStructure? Document { get; init; } = null;

    /// <summary>
    /// Extracted keywords when keyword extraction is enabled.
    ///
    /// When keyword extraction (RAKE or YAKE) is configured, this field contains
    /// the extracted keywords with scores, algorithm info, and position data.
    /// Previously stored in `metadata.additional["keywords"]`.
    /// </summary>
    [JsonPropertyName("extracted_keywords")]
    public List<Keyword>? ExtractedKeywords { get; init; } = null;

    /// <summary>
    /// Document quality score from quality analysis.
    ///
    /// A value between 0.0 and 1.0 indicating the overall text quality.
    /// Previously stored in `metadata.additional["quality_score"]`.
    /// </summary>
    [JsonPropertyName("quality_score")]
    public double? QualityScore { get; init; } = null;

    /// <summary>
    /// Non-fatal warnings collected during processing pipeline stages.
    ///
    /// Captures errors from optional pipeline features (embedding, chunking,
    /// language detection, output formatting) that don't prevent extraction
    /// but may indicate degraded results.
    /// Previously stored as individual keys in `metadata.additional`.
    /// </summary>
    [JsonPropertyName("processing_warnings")]
    public List<ProcessingWarning> ProcessingWarnings { get; init; } = [];

    /// <summary>
    /// PDF annotations extracted from the document.
    ///
    /// When annotation extraction is enabled via `PdfConfig.extract_annotations`,
    /// this field contains text notes, highlights, links, stamps, and other
    /// annotations found in PDF documents.
    /// </summary>
    [JsonPropertyName("annotations")]
    public List<PdfAnnotation>? Annotations { get; init; } = null;

    /// <summary>
    /// Nested extraction results from archive contents.
    ///
    /// When extracting archives, each processable file inside produces its own
    /// full extraction result. Set to `None` for non-archive formats.
    /// Use `max_archive_depth` in config to control recursion depth.
    /// </summary>
    [JsonPropertyName("children")]
    public List<ArchiveEntry>? Children { get; init; } = null;

    /// <summary>
    /// URIs/links discovered during document extraction.
    ///
    /// Contains hyperlinks, image references, citations, email addresses, and
    /// other URI-like references found in the document. Always extracted when
    /// present in the source document.
    /// </summary>
    [JsonPropertyName("uris")]
    public List<ExtractedUri>? Uris { get; init; } = null;

    /// <summary>
    /// Tracked changes embedded in the source document.
    ///
    /// Populated by per-format extractors that understand change-tracking
    /// metadata (DOCX `w:ins`/`w:del`/`w:rPrChange`, ODT `text:change-*`,
    /// …). Every extractor defaults to `None` until its format-specific
    /// implementation is added. Extractors that do populate this field follow
    /// the "accepted-changes" convention: inserted text is present in
    /// `content`, deleted text is absent — the revision list is the separate
    /// audit trail.
    /// </summary>
    [JsonPropertyName("revisions")]
    public List<DocumentRevision>? Revisions { get; init; } = null;

    /// <summary>
    /// Structured extraction output from LLM-based JSON schema extraction.
    ///
    /// When `structured_extraction` is configured in `ExtractionConfig`, the
    /// extracted document content is sent to a VLM with the provided JSON schema.
    /// The response is parsed and stored here as a JSON value matching the schema.
    /// </summary>
    [JsonPropertyName("structured_output")]
    public string? StructuredOutput { get; init; } = null;

    /// <summary>
    /// Code intelligence results from tree-sitter analysis.
    ///
    /// Populated when extracting source code files with the `tree-sitter` feature.
    /// Contains metrics, structural analysis, imports/exports, comments,
    /// docstrings, symbols, diagnostics, and optionally chunked code segments.
    ///
    /// Stored as an opaque JSON value so that all language bindings (Go, Java,
    /// C#, …) can deserialize it as a raw JSON object rather than a typed struct.
    /// The underlying type is `tree_sitter_language_pack.ProcessResult`.
    /// </summary>
    [JsonPropertyName("code_intelligence")]
    public string? CodeIntelligence { get; init; } = null;

    /// <summary>
    /// LLM token usage and cost data for all LLM calls made during this extraction.
    ///
    /// Contains one entry per LLM call. Multiple entries are produced when
    /// VLM OCR, structured extraction, or LLM embeddings run during
    /// the same extraction.
    ///
    /// `None` when no LLM was used.
    /// </summary>
    [JsonPropertyName("llm_usage")]
    public List<LlmUsage>? LlmUsage { get; init; } = null;

    /// <summary>
    /// Pre-rendered content in the requested output format.
    ///
    /// Populated during `derive_extraction_result` before tree derivation consumes
    /// element data. `apply_output_format` swaps this into `content` at the end
    /// of the pipeline, after post-processors have operated on plain text.
    /// </summary>
    [JsonPropertyName("formatted_content")]
    public string? FormattedContent { get; init; } = null;

    /// <summary>
    /// Structured hOCR document for the OCR+layout pipeline.
    ///
    /// When tesseract produces hOCR output, the parsed `InternalDocument` carries
    /// paragraph structure with bounding boxes and confidence scores. The layout
    /// classification step enriches these elements before final rendering.
    /// </summary>
    [JsonPropertyName("ocr_internal_document")]
    public string? OcrInternalDocument { get; init; } = null;


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

    /// <summary>
    /// Convert from an OCR result.
    /// </summary>
    public static ExtractionResult FromOcr(OcrExtractionResult ocr)
    {
        var ocrJson = JsonSerializer.Serialize(ocr, JsonSerializationOptions);
        var ocrHandle = NativeMethods.OcrExtractionResultFromJson(ocrJson);
        if (ocrHandle == IntPtr.Zero)
        {
            var ec = NativeMethods.LastErrorCode();
            var ctxPtr = NativeMethods.LastErrorContext();
            var msg = global::System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ctxPtr) ?? "OcrExtractionResultFromJson failed";
            throw new KreuzbergException(ec, msg);
        }
        try
        {
            var nativeResult = NativeMethods.ExtractionResultFromOcr(ocrHandle);
            var jsonPtr = NativeMethods.ExtractionResultToJson(nativeResult);
            var json = global::System.Runtime.InteropServices.Marshal.PtrToStringUTF8(jsonPtr);
            NativeMethods.FreeString(jsonPtr);
            NativeMethods.ExtractionResultFree(nativeResult);
            return JsonSerializer.Deserialize<ExtractionResult>(json ?? "null", JsonOptions)!;
        }
        finally
        {
            if (ocrHandle != global::System.IntPtr.Zero) NativeMethods.OcrExtractionResultFree(ocrHandle);
        }
    }
}
