//! Blocking key inference from detected signals and entity catalogs.

use super::catalogs::catalog_for_entity;
use super::profile::DataProfile;
use super::signals::EntityType;
use std::collections::HashSet;

// ---------------------------------------------------------------------------
// Blocking key inference (port of Python _infer_blocking_keys, lines 443-475)
// ---------------------------------------------------------------------------

/// Infer blocking keys from the detected signals in the profile.
///
/// Returns blocking keys as lists of canonical field names.
/// Uses the profile's inferred entity type if available, otherwise defaults to Person.
pub fn infer_blocking_keys(profile: &DataProfile) -> Vec<Vec<String>> {
    let entity_type = profile
        .inferred_entity_type
        .as_ref()
        .cloned()
        .unwrap_or(EntityType::Person);
    infer_blocking_keys_for_entity(profile, &entity_type)
}

/// Infer blocking keys using the entity type's catalog templates.
///
/// For each blocking template in the catalog, checks whether all required signals
/// are present in the profile. Only templates where all fields are detected are included.
pub fn infer_blocking_keys_for_entity(
    profile: &DataProfile,
    entity_type: &EntityType,
) -> Vec<Vec<String>> {
    let detected_signals: HashSet<String> = profile
        .columns
        .iter()
        .filter_map(|col| col.signal.as_ref().map(|s| s.canonical_name().to_string()))
        .collect();

    let catalog = catalog_for_entity(entity_type);

    catalog
        .blocking_templates
        .iter()
        .filter(|template| template.iter().all(|field| detected_signals.contains(*field)))
        .map(|template| template.iter().map(|s| s.to_string()).collect())
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::field_profiler::profile::ColumnProfile;
    use crate::field_profiler::signals::IdentitySignal;
    use std::collections::HashMap;
    use uuid::Uuid;

    #[test]
    fn test_infer_blocking_keys_all_signals() {
        let profile = DataProfile {
            tenant_id: Uuid::nil(),
            columns: vec![
                ColumnProfile {
                    name: "email".into(),
                    signal: Some(IdentitySignal::Email),
                    distinct_count: 100, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "phone".into(),
                    signal: Some(IdentitySignal::Phone),
                    distinct_count: 90, null_count: 10, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "first_name".into(),
                    signal: Some(IdentitySignal::FirstName),
                    distinct_count: 50, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "last_name".into(),
                    signal: Some(IdentitySignal::LastName),
                    distinct_count: 60, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "company_name".into(),
                    signal: Some(IdentitySignal::CompanyName),
                    distinct_count: 40, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
            ],
            total_records: 100,
            source_names: vec!["sf_contact".into()],
            inferred_entity_type: Some(EntityType::Person),
            source_columns: HashMap::new(),
        };

        let keys = infer_blocking_keys(&profile);
        assert_eq!(keys.len(), 4);
        assert_eq!(keys[0], vec!["email"]);
        assert_eq!(keys[1], vec!["phone"]);
        assert_eq!(keys[2], vec!["last_name", "first_name"]);
        assert_eq!(keys[3], vec!["company_name", "last_name"]);
    }

    #[test]
    fn test_infer_blocking_keys_email_only() {
        let profile = DataProfile {
            tenant_id: Uuid::nil(),
            columns: vec![ColumnProfile {
                name: "email".into(),
                signal: Some(IdentitySignal::Email),
                distinct_count: 100, null_count: 0, total_count: 100,
                sample_values: Vec::new(),
                cross_source_overlap: None,
            }],
            total_records: 100,
            source_names: vec!["sf_contact".into()],
            inferred_entity_type: Some(EntityType::Person),
            source_columns: HashMap::new(),
        };

        let keys = infer_blocking_keys(&profile);
        assert_eq!(keys.len(), 1);
        assert_eq!(keys[0], vec!["email"]);
    }

    #[test]
    fn test_blocking_keys_product() {
        let profile = DataProfile {
            tenant_id: Uuid::nil(),
            columns: vec![
                ColumnProfile {
                    name: "sku".into(),
                    signal: Some(IdentitySignal::Sku),
                    distinct_count: 100, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "upc".into(),
                    signal: Some(IdentitySignal::Upc),
                    distinct_count: 90, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "product_name".into(),
                    signal: Some(IdentitySignal::ProductName),
                    distinct_count: 80, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "brand".into(),
                    signal: Some(IdentitySignal::Brand),
                    distinct_count: 20, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "category".into(),
                    signal: Some(IdentitySignal::Category),
                    distinct_count: 10, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
            ],
            total_records: 100,
            source_names: vec!["shopify".into()],
            inferred_entity_type: Some(EntityType::Product),
            source_columns: HashMap::new(),
        };

        let keys = infer_blocking_keys(&profile);
        assert_eq!(keys.len(), 5);
        assert_eq!(keys[0], vec!["upc"]);
        assert_eq!(keys[1], vec!["sku"]);
        assert_eq!(keys[2], vec!["product_name"]);
        assert_eq!(keys[3], vec!["brand", "product_name"]);
        assert_eq!(keys[4], vec!["category", "product_name"]);
    }

    #[test]
    fn test_blocking_keys_company() {
        let profile = DataProfile {
            tenant_id: Uuid::nil(),
            columns: vec![
                ColumnProfile {
                    name: "domain".into(),
                    signal: Some(IdentitySignal::Domain),
                    distinct_count: 90, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "company_name".into(),
                    signal: Some(IdentitySignal::CompanyName),
                    distinct_count: 80, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
                ColumnProfile {
                    name: "industry".into(),
                    signal: Some(IdentitySignal::Industry),
                    distinct_count: 15, null_count: 0, total_count: 100,
                    sample_values: Vec::new(),
                    cross_source_overlap: None,
                },
            ],
            total_records: 100,
            source_names: vec!["clearbit".into()],
            inferred_entity_type: Some(EntityType::Company),
            source_columns: HashMap::new(),
        };

        let keys = infer_blocking_keys(&profile);
        assert_eq!(keys.len(), 3);
        assert_eq!(keys[0], vec!["domain"]);
        assert_eq!(keys[1], vec!["company_name"]);
        assert_eq!(keys[2], vec!["company_name", "industry"]);
    }
}
