// 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>
/// Quality thresholds for OCR fallback decisions and pipeline quality gating.
///
/// All fields default to the values that match the previous hardcoded behavior,
/// so `OcrQualityThresholds.default()` preserves existing semantics exactly.
/// </summary>
public sealed record OcrQualityThresholds
{
    /// <summary>
    /// Minimum total non-whitespace characters to consider text substantive.
    /// </summary>
    [JsonPropertyName("min_total_non_whitespace")]
    public ulong MinTotalNonWhitespace { get; init; } = 64;

    /// <summary>
    /// Minimum non-whitespace characters per page on average.
    /// </summary>
    [JsonPropertyName("min_non_whitespace_per_page")]
    public double MinNonWhitespacePerPage { get; init; } = 32.0;

    /// <summary>
    /// Minimum character count for a word to be "meaningful".
    /// </summary>
    [JsonPropertyName("min_meaningful_word_len")]
    public ulong MinMeaningfulWordLen { get; init; } = 4;

    /// <summary>
    /// Minimum count of meaningful words before text is accepted.
    /// </summary>
    [JsonPropertyName("min_meaningful_words")]
    public ulong MinMeaningfulWords { get; init; } = 3;

    /// <summary>
    /// Minimum alphanumeric ratio (non-whitespace chars that are alphanumeric).
    /// </summary>
    [JsonPropertyName("min_alnum_ratio")]
    public double MinAlnumRatio { get; init; } = 0.3;

    /// <summary>
    /// Minimum Unicode replacement characters (U+FFFD) to trigger OCR fallback.
    /// </summary>
    [JsonPropertyName("min_garbage_chars")]
    public ulong MinGarbageChars { get; init; } = 5;

    /// <summary>
    /// Maximum fraction of short (1-2 char) words before text is considered fragmented.
    /// </summary>
    [JsonPropertyName("max_fragmented_word_ratio")]
    public double MaxFragmentedWordRatio { get; init; } = 0.6;

    /// <summary>
    /// Critical fragmentation threshold — triggers OCR regardless of meaningful words.
    /// Normal English text has ~20-30% short words. 80%+ is definitive garbage.
    /// </summary>
    [JsonPropertyName("critical_fragmented_word_ratio")]
    public double CriticalFragmentedWordRatio { get; init; } = 0.8;

    /// <summary>
    /// Minimum average word length. Below this with enough words indicates garbled extraction.
    /// </summary>
    [JsonPropertyName("min_avg_word_length")]
    public double MinAvgWordLength { get; init; } = 2.0;

    /// <summary>
    /// Minimum word count before average word length check applies.
    /// </summary>
    [JsonPropertyName("min_words_for_avg_length_check")]
    public ulong MinWordsForAvgLengthCheck { get; init; } = 50;

    /// <summary>
    /// Minimum consecutive word repetition ratio to detect column scrambling.
    /// </summary>
    [JsonPropertyName("min_consecutive_repeat_ratio")]
    public double MinConsecutiveRepeatRatio { get; init; } = 0.08;

    /// <summary>
    /// Minimum word count before consecutive repetition check is applied.
    /// </summary>
    [JsonPropertyName("min_words_for_repeat_check")]
    public ulong MinWordsForRepeatCheck { get; init; } = 50;

    /// <summary>
    /// Minimum character count for "substantive markdown" OCR skip gate.
    /// </summary>
    [JsonPropertyName("substantive_min_chars")]
    public ulong SubstantiveMinChars { get; init; } = 100;

    /// <summary>
    /// Minimum character count for "non-text content" OCR skip gate.
    /// </summary>
    [JsonPropertyName("non_text_min_chars")]
    public ulong NonTextMinChars { get; init; } = 20;

    /// <summary>
    /// Alphanumeric+whitespace ratio threshold for skip decisions.
    /// </summary>
    [JsonPropertyName("alnum_ws_ratio_threshold")]
    public double AlnumWsRatioThreshold { get; init; } = 0.4;

    /// <summary>
    /// Minimum quality score (0.0-1.0) for a pipeline stage result to be accepted.
    /// If the result from a backend scores below this, try the next backend.
    /// </summary>
    [JsonPropertyName("pipeline_min_quality")]
    public double PipelineMinQuality { get; init; } = 0.5;


    /// <summary>
    /// Parse a <see cref="OcrQualityThresholds"/> from a JSON string.
    /// </summary>
    /// <exception cref="KreuzbergException">When the JSON cannot be deserialised.</exception>
    public static OcrQualityThresholds FromJson(string json)
    {
        try
        {
            return JsonSerializer.Deserialize<OcrQualityThresholds>(json, JsonOptions)
                ?? throw new KreuzbergException($"Failed to parse OcrQualityThresholds from JSON: deserializer returned null");
        }
        catch (KreuzbergException)
        {
            throw;
        }
        catch (Exception e)
        {
            throw new KreuzbergException($"Failed to parse OcrQualityThresholds 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 OcrQualityThresholds Default()
    {
        var nativeResult = NativeMethods.OcrQualityThresholdsDefault();
        var jsonPtr = NativeMethods.OcrQualityThresholdsToJson(nativeResult);
        var json = global::System.Runtime.InteropServices.Marshal.PtrToStringUTF8(jsonPtr);
        NativeMethods.FreeString(jsonPtr);
        NativeMethods.OcrQualityThresholdsFree(nativeResult);
        return JsonSerializer.Deserialize<OcrQualityThresholds>(json ?? "null", JsonOptions)!;
    }
}
