// ---------------------------------------------------------------------------
// Field defaults (port of Python _FIELD_DEFAULTS, lines 479-522)
// ---------------------------------------------------------------------------

use super::signals::EntityType;
use crate::ir::{FsComparatorType, CompiledRuleType, SimilarityAlgorithm};
use std::collections::HashMap;

/// Default configuration for a detected identity signal.
#[derive(Debug, Clone)]
pub struct FieldDefault {
    pub comparator: FsComparatorType,
    pub weight: f64,
    pub m_probability: f64,
    pub u_probability: f64,
    pub normalizer: &'static str,
    pub rule_comparator: CompiledRuleType,
}

/// Per-entity signal catalog: defines which fields, defaults, blocking templates,
/// and field ordering to use when building an IdentityPlan for a given entity type.
pub struct SignalCatalog {
    pub entity_type: EntityType,
    pub field_defaults: HashMap<&'static str, FieldDefault>,
    pub blocking_templates: Vec<Vec<&'static str>>,
    pub field_order: Vec<&'static str>,
}

// Helper to build an exact-match FieldDefault.
fn exact_default(
    field: &'static str,
    weight: f64,
    m: f64,
    u: f64,
    normalizer: &'static str,
) -> FieldDefault {
    FieldDefault {
        comparator: FsComparatorType::Exact,
        weight,
        m_probability: m,
        u_probability: u,
        normalizer,
        rule_comparator: CompiledRuleType::Exact {
            field: field.into(),
            weight,
            case_insensitive: true,
            normalize: true,
            normalizer: Some(normalizer.into()),
        },
    }
}

// Helper to build a similarity-match FieldDefault.
fn similarity_default(
    field: &'static str,
    weight: f64,
    m: f64,
    u: f64,
    threshold: f64,
    normalizer: &'static str,
) -> FieldDefault {
    FieldDefault {
        comparator: FsComparatorType::JaroWinkler,
        weight,
        m_probability: m,
        u_probability: u,
        normalizer,
        rule_comparator: CompiledRuleType::Similarity {
            field: field.into(),
            algorithm: SimilarityAlgorithm::JaroWinkler,
            threshold,
            weight,
            normalizer: Some(normalizer.into()),
        },
    }
}

fn person_catalog() -> SignalCatalog {
    let mut m = HashMap::new();
    m.insert("email", exact_default("email", 2.0, 0.95, 0.001, "email"));
    m.insert("phone", exact_default("phone", 2.0, 0.95, 0.001, "phone"));
    m.insert("first_name", similarity_default("first_name", 1.0, 0.85, 0.05, 0.85, "nickname"));
    m.insert("last_name", similarity_default("last_name", 1.0, 0.88, 0.02, 0.85, "name"));
    m.insert("full_name", similarity_default("full_name", 1.0, 0.80, 0.03, 0.80, "name"));
    m.insert("company_name", similarity_default("company_name", 1.0, 0.80, 0.02, 0.80, "generic"));
    SignalCatalog {
        entity_type: EntityType::Person,
        field_defaults: m,
        blocking_templates: vec![
            vec!["email"],
            vec!["phone"],
            vec!["last_name", "first_name"],
            vec!["company_name", "last_name"],
        ],
        field_order: vec!["email", "phone", "first_name", "last_name", "full_name", "company_name"],
    }
}

fn company_catalog() -> SignalCatalog {
    let mut m = HashMap::new();
    m.insert("domain", exact_default("domain", 2.0, 0.92, 0.001, "generic"));
    m.insert("tax_id", exact_default("tax_id", 2.0, 0.98, 0.0001, "generic"));
    m.insert("company_name", similarity_default("company_name", 1.5, 0.80, 0.02, 0.80, "generic"));
    m.insert("industry", similarity_default("industry", 0.5, 0.60, 0.10, 0.90, "generic"));
    m.insert("email", exact_default("email", 1.5, 0.90, 0.002, "email"));
    m.insert("phone", exact_default("phone", 1.0, 0.80, 0.008, "phone"));
    SignalCatalog {
        entity_type: EntityType::Company,
        field_defaults: m,
        blocking_templates: vec![
            vec!["domain"],
            vec!["tax_id"],
            vec!["company_name"],
            vec!["company_name", "industry"],
        ],
        field_order: vec!["domain", "tax_id", "company_name", "industry", "email", "phone"],
    }
}

fn product_catalog() -> SignalCatalog {
    let mut m = HashMap::new();
    // Deterministic identifiers - highest weights, auto-merge confidence
    m.insert("gtin", exact_default("gtin", 5.0, 0.99, 0.0001, "barcode"));
    m.insert("upc", exact_default("upc", 5.0, 0.99, 0.0001, "barcode"));
    m.insert("asin", exact_default("asin", 4.0, 0.98, 0.001, "generic"));
    m.insert("sku", exact_default("sku", 2.5, 0.95, 0.0005, "generic"));
    m.insert("mpn", exact_default("mpn", 4.0, 0.98, 0.001, "mpn"));
    m.insert("model_number", exact_default("model_number", 3.5, 0.97, 0.002, "mpn"));
    // Fuzzy signals - brand is a hard gate, product_name is fuzzy
    m.insert("brand", similarity_default("brand", 2.0, 0.85, 0.15, 0.85, "brand"));
    m.insert("product_name", similarity_default("product_name", 1.5, 0.70, 0.30, 0.80, "generic"));
    m.insert("category", similarity_default("category", 0.5, 0.60, 0.10, 0.90, "generic"));
    // Variant attributes - low weight, used for variant classification not merge decisions
    m.insert("color", exact_default("color", 0.3, 0.50, 0.10, "generic"));
    m.insert("size", exact_default("size", 0.3, 0.50, 0.10, "generic"));
    m.insert("capacity", exact_default("capacity", 0.5, 0.60, 0.10, "generic"));
    SignalCatalog {
        entity_type: EntityType::Product,
        field_defaults: m,
        blocking_templates: vec![
            vec!["gtin"],
            vec!["upc"],
            vec!["asin"],
            vec!["sku"],
            vec!["mpn", "brand"],
            vec!["brand", "model_number"],
            vec!["product_name"],
            vec!["brand", "product_name"],
            vec!["category", "product_name"],
        ],
        field_order: vec!["gtin", "upc", "asin", "mpn", "model_number", "sku", "brand", "product_name", "category", "color", "size", "capacity"],
    }
}

fn transaction_catalog() -> SignalCatalog {
    let mut m = HashMap::new();
    m.insert("transaction_id", exact_default("transaction_id", 3.0, 0.99, 0.0001, "generic"));
    m.insert("amount", exact_default("amount", 1.5, 0.80, 0.01, "generic"));
    m.insert("currency", exact_default("currency", 0.3, 0.95, 0.20, "generic"));
    SignalCatalog {
        entity_type: EntityType::Transaction,
        field_defaults: m,
        blocking_templates: vec![
            vec!["transaction_id"],
            vec!["amount", "currency"],
        ],
        field_order: vec!["transaction_id", "amount", "currency"],
    }
}

fn healthcare_catalog() -> SignalCatalog {
    let mut m = HashMap::new();
    m.insert("mrn", exact_default("mrn", 2.5, 0.97, 0.0002, "generic"));
    m.insert("npi", exact_default("npi", 2.0, 0.95, 0.0005, "generic"));
    m.insert("patient_name", similarity_default("patient_name", 1.0, 0.80, 0.03, 0.80, "name"));
    m.insert("date_of_birth", exact_default("date_of_birth", 1.5, 0.90, 0.003, "generic"));
    m.insert("email", exact_default("email", 1.5, 0.90, 0.002, "email"));
    m.insert("phone", exact_default("phone", 1.0, 0.80, 0.008, "phone"));
    SignalCatalog {
        entity_type: EntityType::Healthcare,
        field_defaults: m,
        blocking_templates: vec![
            vec!["mrn"],
            vec!["npi"],
            vec!["patient_name", "date_of_birth"],
        ],
        field_order: vec!["mrn", "npi", "patient_name", "date_of_birth", "email", "phone"],
    }
}

/// Get the signal catalog for an entity type. Custom types fall back to Person catalog.
pub fn catalog_for_entity(entity_type: &EntityType) -> SignalCatalog {
    match entity_type {
        EntityType::Person => person_catalog(),
        EntityType::Company => company_catalog(),
        EntityType::Product => product_catalog(),
        EntityType::Transaction => transaction_catalog(),
        EntityType::Healthcare => healthcare_catalog(),
        EntityType::Custom(_) => person_catalog(),
    }
}

/// Preferred ordering when building the plan (Person default for backward compat).
pub(crate) const FIELD_ORDER: &[&str] = &[
    "email",
    "phone",
    "first_name",
    "last_name",
    "full_name",
    "company_name",
];
