use crate::types::NormalizedEntity;

pub trait BlockingStrategy: Send + Sync {
    fn generate_block_key(&self, entity: &NormalizedEntity) -> Vec<String>;
}

pub mod lsh;

pub struct FieldBlocking {
    pub fields: Vec<String>,
}

impl BlockingStrategy for FieldBlocking {
    fn generate_block_key(&self, entity: &NormalizedEntity) -> Vec<String> {
        self.fields
            .iter()
            .filter_map(|field| entity.data.get(field).map(|v| format!("{}:{}", field, v)))
            .collect()
    }
}

pub struct DomainBlocking;
impl BlockingStrategy for DomainBlocking {
    fn generate_block_key(&self, entity: &NormalizedEntity) -> Vec<String> {
        entity.data.get("email")
            .and_then(|email| email.split('@').last())
            .map(|domain| vec![format!("domain:{}", domain)])
            .unwrap_or_default()
    }
}

pub struct SortedNeighborhoodBlocking {
    pub sort_fields: Vec<String>,
}

impl BlockingStrategy for SortedNeighborhoodBlocking {
    fn generate_block_key(&self, entity: &NormalizedEntity) -> Vec<String> {
        let key_parts: Vec<&str> = self.sort_fields.iter()
            .filter_map(|f| entity.data.get(f).map(|v| v.as_str()))
            .collect();
        if key_parts.is_empty() {
            return vec![];
        }
        vec![format!("sorted_neighborhood:{}", key_parts.join(":"))]
    }
}

pub struct PlanBlocking {
    pub keys: Vec<crate::ir::BlockingKey>,
}

impl BlockingStrategy for PlanBlocking {
    fn generate_block_key(&self, entity: &NormalizedEntity) -> Vec<String> {
        use crate::ir::BlockingTransformation;
        use rphonetic::{Soundex, DoubleMetaphone, Encoder};

        let mut block_keys = Vec::new();

        for key_def in &self.keys {
             let mut candidates = vec![String::new()];
             let mut valid_key = true;
             
             for (i, field) in key_def.fields.iter().enumerate() {
                 if let Some(val) = entity.data.get(field).map(|v| v.as_str()) {
                     let mut field_values = vec![val.to_string()];
                     
                     for t in &key_def.transformations {
                          let mut next_values = Vec::new();
                          for v in field_values {
                              match t {
                                  BlockingTransformation::Lowercase => next_values.push(v.to_lowercase()),
                                  BlockingTransformation::FirstInitial => {
                                      next_values.push(v.chars().next().map(|c| c.to_string()).unwrap_or_default());
                                  },
                                  BlockingTransformation::Soundex => {
                                      let encoder = Soundex::default();
                                      next_values.push(encoder.encode(&v));
                                  },
                                  BlockingTransformation::DoubleMetaphone => {
                                      let encoder = DoubleMetaphone::default();
                                      next_values.push(encoder.encode(&v));
                                  },
                                  BlockingTransformation::AreaCode => {
                                      let digits: String = v.chars().filter(|c| c.is_ascii_digit()).collect();
                                      // If 11+ digits starting with country code 1, skip it
                                      let area_digits = if digits.len() >= 11 && digits.starts_with('1') {
                                          &digits[1..]
                                      } else {
                                          &digits
                                      };
                                      if area_digits.len() >= 3 {
                                          next_values.push(area_digits[0..3].to_string());
                                      } else {
                                          next_values.push(v);
                                      }
                                  },
                                  BlockingTransformation::Last4 => {
                                       let digits: String = v.chars().filter(|c| c.is_ascii_digit()).collect();
                                       if digits.len() >= 4 {
                                           next_values.push(digits[digits.len()-4..].to_string());
                                       } else {
                                           next_values.push(v);
                                       }
                                  },
                                  BlockingTransformation::Trigrams => {
                                      if v.len() < 3 {
                                          next_values.push(v);
                                      } else {
                                          for i in 0..v.len()-2 {
                                              next_values.push(v[i..i+3].to_string());
                                          }
                                      }
                                  }
                                  BlockingTransformation::NormalizePhone => {
                                      use crate::normalization::{PhoneNormalizer, Normalizer, NormalizationResult};
                                      match PhoneNormalizer.normalize(&v) {
                                          NormalizationResult::Normalized(n) => next_values.push(n),
                                          _ => next_values.push(v),
                                      }
                                  }
                                  BlockingTransformation::NormalizeEmail => {
                                      use crate::normalization::{EmailNormalizer, Normalizer, NormalizationResult};
                                      match EmailNormalizer.normalize(&v) {
                                          NormalizationResult::Normalized(n) => next_values.push(n),
                                          _ => next_values.push(v),
                                      }
                                  }
                                  BlockingTransformation::NormalizeName => {
                                      use crate::normalization::{NameNormalizer, Normalizer, NormalizationResult};
                                      match NameNormalizer.normalize(&v) {
                                          NormalizationResult::Normalized(n) => next_values.push(n),
                                          _ => next_values.push(v),
                                      }
                                  }
                                  BlockingTransformation::NormalizeDomain => {
                                      use crate::normalization::{DomainNormalizer, Normalizer, NormalizationResult};
                                      match DomainNormalizer.normalize(&v) {
                                          NormalizationResult::Normalized(n) => next_values.push(n),
                                          _ => next_values.push(v),
                                      }
                                  }
                              }
                          }
                          field_values = next_values;
                     }
                     
                     let mut new_candidates = Vec::new();
                     for c in candidates {
                         for fv in &field_values {
                             let mut new_c = c.clone();
                             if i > 0 { new_c.push('|'); }
                             new_c.push_str(fv);
                             new_candidates.push(new_c);
                         }
                     }
                     candidates = new_candidates;
                 } else {
                     valid_key = false;
                     break; 
                 }
                 
                 if candidates.len() > 100 {
                     candidates.truncate(100);
                     break;
                 }
             }
             
             if valid_key {
                 block_keys.extend(candidates);
             }
        }
        
        block_keys
    }
}
