//! Whole-image classification via `VNClassifyImageRequest`.
//!
//! Returns the top-N classifications by confidence from Apple's
//! built-in image classifier (~1300 classes). `top_k = 0` returns
//! every classification with non-zero confidence — typical agent
//! use is `top_k = 5` or `10`.
//!
//! Useful for routing decisions: an agent that processes an image
//! can ask "is this a screenshot vs. a photo vs. a chart" before
//! choosing a downstream pipeline.

use std::path::Path;

#[cfg(all(target_os = "macos", car_vision_swift_built))]
use std::ffi::CString;
#[cfg(all(target_os = "macos", car_vision_swift_built))]
use std::os::raw::c_char;
#[cfg(all(target_os = "macos", car_vision_swift_built))]
use std::ptr;

use serde::{Deserialize, Serialize};

use crate::{Result, VisionError};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Classification {
    /// Apple's class identifier (e.g. `"laptop"`, `"document"`).
    /// Stable across macOS minor releases for a given Vision
    /// classifier version.
    pub identifier: String,
    /// Confidence in `[0, 1]`.
    pub confidence: f32,
}

/// Run image classification on `path`. `top_k = 0` returns every
/// non-zero classification; positive values cap the result list.
pub fn classify(path: impl AsRef<Path>, top_k: usize) -> Result<Vec<Classification>> {
    let path = path.as_ref();

    #[cfg(not(all(target_os = "macos", car_vision_swift_built)))]
    {
        let _ = (path, top_k);
        return Err(VisionError::PlatformUnsupported {
            reason: "Vision framework requires aarch64-apple-darwin with the Swift shim built",
        });
    }

    #[cfg(all(target_os = "macos", car_vision_swift_built))]
    {
        let path_c = CString::new(path.to_string_lossy().as_bytes())
            .map_err(|e| VisionError::InvalidInput(format!("path has interior NUL: {e}")))?;

        let mut out_json: *mut c_char = ptr::null_mut();
        let mut out_err: *mut c_char = ptr::null_mut();

        let rc = unsafe {
            crate::ffi::car_vision_classify_image(
                path_c.as_ptr(),
                top_k as i32,
                &mut out_json as *mut *mut c_char,
                &mut out_err as *mut *mut c_char,
            )
        };

        if rc != 0 {
            return Err(VisionError::RequestFailed(consume_swift_string(out_err)));
        }

        let json = consume_swift_string(out_json);
        serde_json::from_str(&json).map_err(|e| {
            VisionError::MalformedResponse(format!("classify JSON parse: {e}; payload: {json}"))
        })
    }
}

#[cfg(all(target_os = "macos", car_vision_swift_built))]
fn consume_swift_string(ptr: *mut c_char) -> String {
    if ptr.is_null() {
        return String::new();
    }
    let s = unsafe { std::ffi::CStr::from_ptr(ptr) }
        .to_string_lossy()
        .into_owned();
    unsafe { crate::ffi::car_vision_free_string(ptr) };
    s
}
