//! The target-classification port.
//!
//! A [`TargetClassifier`] inspects a target's artifacts (and optionally their
//! ingested working directories) and proposes one or more [`ClassifierVerdict`]s
//! — a target type, a confidence, and the facts the decision rested on. Concrete
//! classifiers live in the agent (language/build-system fingerprinting, a
//! firmware detector backed by tramiton, etc.); a registry merges and ranks
//! their verdicts. This mirrors the [`crate::traits::Scanner`] port so the two
//! read the same way.

use std::collections::HashMap;
use std::path::PathBuf;

use crate::error::CoreError;
use crate::models::{Artifact, DetectedFact, TargetType};

/// Everything a classifier needs to reason about a target.
pub struct ClassificationInput<'a> {
    /// The artifacts declared for the target.
    pub artifacts: &'a [Artifact],
    /// Ingested working paths, keyed by [`Artifact::id`]. Absent for artifacts
    /// with no on-disk form (e.g. a live URL).
    pub working_paths: &'a HashMap<String, PathBuf>,
    /// Free-form description of the target, if provided.
    pub description: Option<&'a str>,
}

/// A single classifier's proposal for a target.
pub struct ClassifierVerdict {
    /// The proposed target type.
    pub target_type: TargetType,
    /// Confidence in `[0.0, 1.0]`.
    pub confidence: f32,
    /// Facts that informed the proposal.
    pub facts: Vec<DetectedFact>,
    /// Human-readable explanation.
    pub rationale: String,
}

/// A source of target-type classification.
#[allow(async_fn_in_trait)]
pub trait TargetClassifier: Send + Sync {
    /// Stable identifier for this classifier (recorded in `detected_by`).
    fn name(&self) -> &str;

    /// Propose zero or more ranked verdicts for the given input.
    async fn classify(
        &self,
        input: &ClassificationInput<'_>,
    ) -> Result<Vec<ClassifierVerdict>, CoreError>;
}
