// 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>
/// Extracted image from a document.
///
/// Contains raw image data, metadata, and optional nested OCR results.
/// Raw bytes allow cross-language compatibility - users can convert to
/// PIL.Image (Python), Sharp (Node.js), or other formats as needed.
/// </summary>
public sealed record ExtractedImage
{
    /// <summary>
    /// Raw image data (PNG, JPEG, WebP, etc. bytes).
    /// Uses `bytes.Bytes` for cheap cloning of large buffers.
    /// </summary>
    [JsonConverter(typeof(ByteArrayToIntArrayConverter))]
    [JsonPropertyName("data")]
    public byte[] Data { get; init; } = [];

    /// <summary>
    /// Image format (e.g., "jpeg", "png", "webp")
    /// Uses Cow&lt;, str&gt; to avoid allocation for static literals.
    /// </summary>
    [JsonPropertyName("format")]
    public required string Format { get; init; }

    /// <summary>
    /// Zero-indexed position of this image in the document/page
    /// </summary>
    [JsonPropertyName("image_index")]
    public uint ImageIndex { get; init; } = 0;

    /// <summary>
    /// Page/slide number where image was found (1-indexed)
    /// </summary>
    [JsonPropertyName("page_number")]
    public uint? PageNumber { get; init; } = null;

    /// <summary>
    /// Image width in pixels
    /// </summary>
    [JsonPropertyName("width")]
    public uint? Width { get; init; } = null;

    /// <summary>
    /// Image height in pixels
    /// </summary>
    [JsonPropertyName("height")]
    public uint? Height { get; init; } = null;

    /// <summary>
    /// Colorspace information (e.g., "RGB", "CMYK", "Gray")
    /// </summary>
    [JsonPropertyName("colorspace")]
    public string? Colorspace { get; init; } = null;

    /// <summary>
    /// Bits per color component (e.g., 8, 16)
    /// </summary>
    [JsonPropertyName("bits_per_component")]
    public uint? BitsPerComponent { get; init; } = null;

    /// <summary>
    /// Whether this image is a mask image
    /// </summary>
    [JsonPropertyName("is_mask")]
    public bool IsMask { get; init; } = false;

    /// <summary>
    /// Optional description of the image
    /// </summary>
    [JsonPropertyName("description")]
    public string? Description { get; init; } = null;

    /// <summary>
    /// Nested OCR extraction result (if image was OCRed)
    ///
    /// When OCR is performed on this image, the result is embedded here
    /// rather than in a separate collection, making the relationship explicit.
    /// </summary>
    [JsonPropertyName("ocr_result")]
    public ExtractionResult? OcrResult { get; init; } = null;

    /// <summary>
    /// Bounding box of the image on the page (PDF coordinates: x0=left, y0=bottom, x1=right, y1=top).
    /// Only populated for PDF-extracted images when position data is available from the PDF extractor.
    /// </summary>
    [JsonPropertyName("bounding_box")]
    public BoundingBox? BoundingBox { get; init; } = null;

    /// <summary>
    /// Original source path of the image within the document archive (e.g., "media/image1.png" in DOCX).
    /// Used for rendering image references when the binary data is not extracted.
    /// </summary>
    [JsonPropertyName("source_path")]
    public string? SourcePath { get; init; } = null;

    /// <summary>
    /// Heuristic classification of what this image likely depicts.
    /// `None` if classification was disabled or inconclusive.
    /// </summary>
    [JsonPropertyName("image_kind")]
    public ImageKind? ImageKind { get; init; } = null;

    /// <summary>
    /// Confidence score for `image_kind`, in the range 0.0 to 1.0.
    /// </summary>
    [JsonPropertyName("kind_confidence")]
    public float? KindConfidence { get; init; } = null;

    /// <summary>
    /// Identifier shared across images that form a single logical figure
    /// (e.g. all raster tiles of one technical drawing). `None` for singletons.
    /// </summary>
    [JsonPropertyName("cluster_id")]
    public uint? ClusterId { get; init; } = null;


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