//! Raw FFI declarations matching the Swift bridge.

#![allow(missing_docs, non_camel_case_types)]

use core::ffi::{c_char, c_void};

/// Mirrors `SPTranscriptionSegmentRaw` in Speech.swift.
#[repr(C)]
pub struct TranscriptionSegmentRaw {
    pub text: *mut c_char,
    pub confidence: f32,
    pub timestamp: f64,
    pub duration: f64,
}

extern "C" {
    pub fn sp_string_free(s: *mut c_char);

    pub fn sp_authorization_status() -> i32;
    pub fn sp_request_authorization() -> i32;

    pub fn sp_recognizer_is_available(locale_id: *const c_char) -> bool;
    pub fn sp_recognizer_default_locale_identifier() -> *mut c_char;

    pub fn sp_recognize_url(
        audio_path: *const c_char,
        locale_id: *const c_char,
        out_transcript: *mut *mut c_char,
        out_segments: *mut *mut c_void,
        out_segment_count: *mut usize,
        out_error_message: *mut *mut c_char,
    ) -> i32;

    pub fn sp_transcription_segments_free(array: *mut c_void, count: usize);

    pub fn sp_recognize_url_with_metadata(
        audio_path: *const c_char,
        locale_id: *const c_char,
        out_transcript: *mut *mut c_char,
        out_segments: *mut *mut c_void,
        out_segment_count: *mut usize,
        out_metadata: *mut RecognitionMetadataRaw,
        out_error_message: *mut *mut c_char,
    ) -> i32;

    pub fn sp_live_recognition_start(
        locale_id: *const c_char,
        callback: LiveCallback,
        user_info: *mut c_void,
        out_error_message: *mut *mut c_char,
    ) -> *mut c_void;
    pub fn sp_live_recognition_stop(token: *mut c_void);
    pub fn sp_live_recognition_end_audio(token: *mut c_void);
    pub fn sp_live_recognition_cancel(token: *mut c_void);

    pub fn sp_recognize_url_with_custom_model(
        audio_path: *const c_char,
        locale_id: *const c_char,
        language_model_path: *const c_char,
        vocabulary_path: *const c_char,
        out_transcript: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;

    pub fn sp_supported_locales_json() -> *mut c_char;
    pub fn sp_recognizer_locale_identifier(
        locale_id: *const c_char,
        recognizer_json: *const c_char,
        out_error_message: *mut *mut c_char,
    ) -> *mut c_char;
    pub fn sp_recognizer_supports_on_device_recognition(
        locale_id: *const c_char,
        recognizer_json: *const c_char,
    ) -> bool;
    pub fn sp_recognizer_observe_availability(
        locale_id: *const c_char,
        recognizer_json: *const c_char,
        callback: AvailabilityCallback,
        user_info: *mut c_void,
        out_error_message: *mut *mut c_char,
    ) -> *mut c_void;
    pub fn sp_recognizer_availability_observer_stop(token: *mut c_void);

    pub fn sp_recognize_url_detailed_json(
        audio_path: *const c_char,
        locale_id: *const c_char,
        recognizer_json: *const c_char,
        request_json: *const c_char,
        out_result_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;

    pub fn sp_start_url_task(
        audio_path: *const c_char,
        locale_id: *const c_char,
        recognizer_json: *const c_char,
        request_json: *const c_char,
        callback: TaskEventCallback,
        user_info: *mut c_void,
        out_error_message: *mut *mut c_char,
    ) -> *mut c_void;
    pub fn sp_start_audio_buffer_task(
        locale_id: *const c_char,
        recognizer_json: *const c_char,
        request_json: *const c_char,
        callback: TaskEventCallback,
        user_info: *mut c_void,
        out_error_message: *mut *mut c_char,
    ) -> *mut c_void;
    pub fn sp_start_microphone_task(
        locale_id: *const c_char,
        recognizer_json: *const c_char,
        request_json: *const c_char,
        callback: TaskEventCallback,
        user_info: *mut c_void,
        out_error_message: *mut *mut c_char,
    ) -> *mut c_void;
    pub fn sp_task_finish(token: *mut c_void);
    pub fn sp_task_cancel(token: *mut c_void);
    pub fn sp_task_state(token: *mut c_void) -> i32;
    pub fn sp_task_is_finishing(token: *mut c_void) -> bool;
    pub fn sp_task_is_cancelled(token: *mut c_void) -> bool;
    pub fn sp_task_error_json(token: *mut c_void) -> *mut c_char;
    pub fn sp_task_release(token: *mut c_void);

    pub fn sp_audio_buffer_request_native_format_json() -> *mut c_char;
    pub fn sp_audio_buffer_task_end_audio(token: *mut c_void);
    pub fn sp_audio_buffer_task_native_format_json(token: *mut c_void) -> *mut c_char;
    pub fn sp_audio_buffer_task_append_f32(
        token: *mut c_void,
        samples: *const f32,
        sample_count: usize,
        sample_rate: f64,
        channels: i32,
        interleaved: bool,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_audio_buffer_task_append_i16(
        token: *mut c_void,
        samples: *const i16,
        sample_count: usize,
        sample_rate: f64,
        channels: i32,
        interleaved: bool,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_audio_buffer_task_append_pcm_buffer_raw(
        token: *mut c_void,
        buffer: *mut c_void,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_audio_buffer_task_append_sample_buffer_raw(
        token: *mut c_void,
        sample_buffer: *mut c_void,
        out_error_message: *mut *mut c_char,
    ) -> i32;

    pub fn sp_prepare_custom_language_model(
        asset_path: *const c_char,
        configuration_json: *const c_char,
        ignores_cache: bool,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_prepare_custom_language_model_with_client_identifier(
        asset_path: *const c_char,
        client_identifier: *const c_char,
        configuration_json: *const c_char,
        ignores_cache: bool,
        out_error_message: *mut *mut c_char,
    ) -> i32;

    pub fn sp_dictation_supported_locales_json(
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_dictation_installed_locales_json(
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_dictation_supported_locale_identifier(
        locale_id: *const c_char,
        out_locale_id: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_dictation_selected_locales_json(
        configuration_json: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_dictation_available_audio_formats_json(
        configuration_json: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_dictation_transcribe_url_json(
        audio_path: *const c_char,
        configuration_json: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;

    pub fn sp_speech_transcriber_is_available() -> bool;
    pub fn sp_speech_transcriber_supported_locales_json(
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_speech_transcriber_installed_locales_json(
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_speech_transcriber_supported_locale_identifier(
        locale_id: *const c_char,
        out_locale_id: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_speech_transcriber_selected_locales_json(
        configuration_json: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_speech_transcriber_available_audio_formats_json(
        configuration_json: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_speech_detector_available_audio_formats_json(
        configuration_json: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_speech_analyzer_best_audio_format_json(
        modules_json: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_speech_analyzer_analyze_url_json(
        audio_path: *const c_char,
        analyzer_json: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_speech_models_end_retention(out_error_message: *mut *mut c_char) -> i32;

    pub fn sp_asset_inventory_maximum_reserved_locales(
        out_value: *mut usize,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_asset_inventory_reserved_locales_json(
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_asset_inventory_reserve_locale(
        locale_id: *const c_char,
        out_reserved: *mut bool,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_asset_inventory_release_locale(
        locale_id: *const c_char,
        out_released: *mut bool,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_asset_inventory_status_for_modules(
        modules_json: *const c_char,
        out_status: *mut i32,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_asset_inventory_installation_request_for_modules(
        modules_json: *const c_char,
        out_has_request: *mut bool,
        out_error_message: *mut *mut c_char,
    ) -> *mut c_void;
    pub fn sp_asset_installation_request_progress_json(token: *mut c_void) -> *mut c_char;
    pub fn sp_asset_installation_request_download_and_install(
        token: *mut c_void,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_asset_installation_request_release(token: *mut c_void);

    pub fn sp_custom_language_model_supported_phonemes_json(
        locale_id: *const c_char,
        out_json: *mut *mut c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
    pub fn sp_custom_language_model_export(
        data_json: *const c_char,
        output_path: *const c_char,
        out_error_message: *mut *mut c_char,
    ) -> i32;
}

// ============================================================================
// Async callback-based FFI declarations (feature = "async")
// ============================================================================

/// Callback type for `sp_request_authorization_async`.
/// Delivers the raw `SFSpeechRecognizerAuthorizationStatus` code as `i32`.
pub type AuthAsyncCallback = unsafe extern "C" fn(status: i32, ctx: *mut c_void);

/// Callback type for `sp_recognize_url_async` and `sp_speech_analyzer_analyze_url_async`.
///
/// On success: `result_json` is non-null, `error_cstr` is null.
/// On failure: `result_json` is null, `error_cstr` is non-null.
pub type StringAsyncCallback =
    unsafe extern "C" fn(result_json: *const c_char, error_cstr: *const c_char, ctx: *mut c_void);

/// Callback type for `sp_prepare_custom_language_model_async`.
/// On success: `error_cstr` is null.  On failure: `error_cstr` is non-null.
pub type VoidAsyncCallback = unsafe extern "C" fn(error_cstr: *const c_char, ctx: *mut c_void);

extern "C" {
    /// Non-blocking bridge for `SFSpeechRecognizer.requestAuthorization`.
    /// Fires `cb(status_code, ctx)` once on the authorization queue.
    pub fn sp_request_authorization_async(cb: AuthAsyncCallback, ctx: *mut c_void);

    /// Non-blocking one-shot URL recognition via `SFSpeechRecognitionTask`.
    /// Fires `cb(json, nil, ctx)` on success, `cb(nil, error, ctx)` on failure.
    /// JSON payload is compatible with `DetailedRecognitionResult`.
    pub fn sp_recognize_url_async(
        audio_path: *const c_char,
        locale_id: *const c_char,
        recognizer_json: *const c_char,
        request_json: *const c_char,
        cb: StringAsyncCallback,
        ctx: *mut c_void,
    );

    /// Non-blocking `SpeechAnalyzer` analysis (macOS 26.0+).
    /// Fires `cb(json, nil, ctx)` on success, `cb(nil, error, ctx)` on failure.
    /// JSON payload is compatible with `SpeechAnalyzerOutput`.
    pub fn sp_speech_analyzer_analyze_url_async(
        audio_path: *const c_char,
        analyzer_json: *const c_char,
        cb: StringAsyncCallback,
        ctx: *mut c_void,
    );

    /// Non-blocking `SFSpeechLanguageModel.prepareCustomLanguageModel` (macOS 14.0+).
    /// Fires `cb(nil, ctx)` on success, `cb(error, ctx)` on failure.
    pub fn sp_prepare_custom_language_model_async(
        asset_path: *const c_char,
        configuration_json: *const c_char,
        ignores_cache: bool,
        cb: VoidAsyncCallback,
        ctx: *mut c_void,
    );
}

#[repr(C)]
pub struct RecognitionMetadataRaw {
    pub has_metadata: bool,
    pub speaking_rate: f64,
    pub average_pause_duration: f64,
    pub speech_start_timestamp: f64,
    pub speech_duration: f64,
}

pub type LiveCallback =
    unsafe extern "C" fn(user_info: *mut c_void, transcript: *const c_char, is_final: bool);

pub type TaskEventCallback =
    unsafe extern "C" fn(user_info: *mut c_void, payload_json: *const c_char);
pub type AvailabilityCallback = unsafe extern "C" fn(user_info: *mut c_void, available: bool);

pub mod status {
    pub const OK: i32 = 0;
    pub const INVALID_ARGUMENT: i32 = -1;
    pub const NOT_AUTHORIZED: i32 = -2;
    pub const RECOGNIZER_UNAVAILABLE: i32 = -3;
    pub const AUDIO_LOAD_FAILED: i32 = -4;
    pub const RECOGNITION_FAILED: i32 = -5;
    pub const TIMED_OUT: i32 = -6;
    pub const UNKNOWN: i32 = -99;
}
