//! Type definitions for declarative extractors.

use serde::Deserialize;

use crate::types::PolicySourceInfo;

/// Definition of a declarative extractor from config.
///
/// This struct is deserialized from `aphoria.toml` or Trust Packs and
/// represents the user's intent for a custom pattern-based extractor.
#[derive(Debug, Clone, Deserialize)]
pub struct DeclarativeExtractorDef {
    /// Unique name for this extractor.
    pub name: String,

    /// Human-readable description.
    #[serde(default)]
    pub description: String,

    /// Languages this extractor applies to (e.g., ["rust", "go"]).
    pub languages: Vec<String>,

    /// Regex pattern to match.
    pub pattern: String,

    /// Claim configuration.
    pub claim: DeclarativeClaimDef,

    /// Confidence score (0.0-1.0), default 1.0.
    #[serde(default = "default_confidence")]
    pub confidence: f32,

    /// Source attribution (populated when loaded from Trust Pack).
    #[serde(skip)]
    pub source: Option<PolicySourceInfo>,
}

fn default_confidence() -> f32 {
    1.0
}

/// Claim definition for declarative extractors.
#[derive(Debug, Clone, Deserialize)]
pub struct DeclarativeClaimDef {
    /// Subject/concept leaf path (appended to code://{lang}/{project}/).
    pub subject: String,

    /// Predicate for the claim.
    pub predicate: String,

    /// Value specification.
    #[serde(flatten)]
    pub value: DeclarativeValue,
}

/// Value specification for declarative claims.
///
/// Uses `#[serde(untagged)]` to allow flexible TOML syntax:
/// - `value = true` or `value = false` → Boolean
/// - `value = "some text"` → Text
/// - `value_from_match = true` → MatchedText (uses the regex match as value)
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum DeclarativeValue {
    /// Use the matched text as the value.
    ///
    /// When `value_from_match = true` is specified in config, the regex match
    /// itself becomes the claim value. This is useful for extracting dynamic
    /// values like algorithm names or API versions from the matched content.
    MatchedText {
        /// Marker field to trigger this variant via `value_from_match = true`.
        /// The actual bool value is ignored - presence of the field is what matters.
        #[allow(dead_code)]
        value_from_match: bool,
    },
    /// Fixed boolean value.
    Boolean {
        /// The boolean value to use.
        value: bool,
    },
    /// Fixed string value.
    Text {
        /// The string value to use.
        value: String,
    },
}
