//! Image classifier node configuration

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;

/// Image classifier node configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageClassifierConfig {
    #[serde(default = "default_processing_rate_hz")]
    pub processing_rate_hz: u32,

    #[serde(default = "default_top_k")]
    pub top_k: usize,

    pub model: ModelConfig,

    #[serde(default)]
    pub topics: Topics,
}

/// Topics configuration for image classifier
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Topics {
    /// Topics this node publishes to
    /// Format: [{ "output": "/vision/classification" }]
    #[serde(default)]
    pub publishes: Vec<HashMap<String, String>>,

    /// Topics this node subscribes to
    /// Format: [{ "input": "/camera/rgb" }]
    #[serde(default)]
    pub subscribes: Vec<HashMap<String, String>>,
}

impl Default for Topics {
    fn default() -> Self {
        let mut publishes = Vec::new();
        let mut subscribes = Vec::new();

        // Default publish topic
        let mut pub_map = HashMap::new();
        pub_map.insert("output".to_string(), "/vision/classification".to_string());
        publishes.push(pub_map);

        // Default subscribe topics
        let mut input_map = HashMap::new();
        input_map.insert("input".to_string(), "/camera/rgb".to_string());
        subscribes.push(input_map);

        let mut control_map = HashMap::new();
        control_map.insert("control".to_string(), "/vision/classification/control".to_string());
        subscribes.push(control_map);

        Self { publishes, subscribes }
    }
}

impl ImageClassifierConfig {
    /// Get input topic path from topics config
    pub fn input_topic(&self) -> String {
        self.topics
            .subscribes
            .iter()
            .find_map(|t| t.get("input"))
            .cloned()
            .unwrap_or_else(|| "/camera/rgb".to_string())
    }

    /// Get output topic path from topics config
    pub fn output_topic(&self) -> String {
        self.topics
            .publishes
            .iter()
            .find_map(|t| t.get("output"))
            .cloned()
            .unwrap_or_else(|| "/vision/classification".to_string())
    }

    /// Get control topic path from topics config
    pub fn control_topic(&self) -> String {
        self.topics
            .subscribes
            .iter()
            .find_map(|t| t.get("control"))
            .cloned()
            .unwrap_or_else(|| "/vision/classification/control".to_string())
    }
}

/// Model configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelConfig {
    /// Model name from catalog (e.g., "mobilenet-v2")
    pub name: Option<String>,

    /// Or direct path to ONNX file
    pub path: Option<PathBuf>,

    /// Override confidence threshold
    pub confidence_threshold: Option<f32>,
}

/// Model catalog entry from model_catalog.toml (deprecated - for backwards compatibility only)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelCatalogEntry {
    pub name: String,
    pub description: String,
    pub task: String,
    pub repo: String,
    pub filename: String,
}

fn default_processing_rate_hz() -> u32 {
    10
}

fn default_top_k() -> usize {
    5
}

impl Default for ImageClassifierConfig {
    fn default() -> Self {
        Self {
            processing_rate_hz: default_processing_rate_hz(),
            top_k: default_top_k(),
            model: ModelConfig {
                name: Some("mobilenet-v2".to_string()),
                path: None,
                confidence_threshold: None,
            },
            topics: Topics::default(),
        }
    }
}
