//! INI file repair module

use crate::error::Result;
use crate::traits::{Repair, RepairStrategy, Validator};
use regex::Regex;
use std::sync::OnceLock;

/// Cached regex patterns for INI performance optimization
#[allow(dead_code)]
struct IniRegexCache {
    malformed_sections: Regex,
    malformed_keys: Regex,
    missing_equals: Regex,
    unquoted_values: Regex,
    malformed_comments: Regex,
    duplicate_sections: Regex,
}

impl IniRegexCache {
    fn new() -> Result<Self> {
        Ok(Self {
            malformed_sections: Regex::new(r#"^(\s*)\[([^]]*)\s*$"#)?,
            malformed_keys: Regex::new(r#"^(\s*)([^=\s]+)\s*([^=].*)$"#)?,
            missing_equals: Regex::new(r#"^(\s*)([^=\s\[\#]+)\s+([^=\s\[\#].*)$"#)?,
            unquoted_values: Regex::new(r#"^(\s*)([^=\s]+)\s*=\s*([^"\s].*[^"\s])\s*$"#)?,
            malformed_comments: Regex::new(r#"^(\s*)#\s*([^#\s].*)$"#)?,
            duplicate_sections: Regex::new(r#"^\[([^\]]+)\]"#)?,
        })
    }
}

static INI_REGEX_CACHE: OnceLock<IniRegexCache> = OnceLock::new();

fn get_ini_regex_cache() -> &'static IniRegexCache {
    INI_REGEX_CACHE.get_or_init(|| IniRegexCache::new().expect("Failed to initialize INI regex cache"))
}

/// INI repairer that can fix common INI file issues
/// 
/// Uses trait-based composition with GenericRepairer for better modularity
pub struct IniRepairer {
    inner: crate::repairer_base::GenericRepairer,
}

impl IniRepairer {
    /// Create a new INI repairer
    pub fn new() -> Self {
        let strategies: Vec<Box<dyn RepairStrategy>> = vec![
            Box::new(FixMalformedSectionsStrategy),
            Box::new(FixMalformedKeysStrategy),
            Box::new(FixMissingEqualsStrategy),
            Box::new(FixUnquotedValuesStrategy),
            Box::new(FixMalformedCommentsStrategy),
            Box::new(RemoveDuplicateSectionsStrategy),
            Box::new(AddDefaultSectionStrategy),
        ];
        
        let validator: Box<dyn Validator> = Box::new(IniValidator);
        let inner = crate::repairer_base::GenericRepairer::new(validator, strategies);
        
        Self { inner }
    }
}

impl Default for IniRepairer {
    fn default() -> Self {
        Self::new()
    }
}

impl Repair for IniRepairer {
    fn repair(&mut self, content: &str) -> Result<String> {
        self.inner.repair(content)
    }
    
    fn needs_repair(&self, content: &str) -> bool {
        self.inner.needs_repair(content)
    }
    
    fn confidence(&self, content: &str) -> f64 {
        if content.trim().is_empty() {
            return 0.0;
        }
        
        // Calculate confidence based on INI-like patterns
        let mut score: f64 = 0.0;
        
        // Check for section headers
        if content.contains('[') && content.contains(']') {
            score += 0.3;
        }
        
        // Check for key-value pairs
        if content.contains('=') {
            score += 0.3;
        }
        
        // Check for comments
        if content.contains('#') {
            score += 0.1;
        }
        
        // Check for consistent structure
        let lines: Vec<&str> = content.lines().collect();
        let has_sections = lines.iter().any(|line| line.trim().starts_with('['));
        let has_keys = lines.iter().any(|line| line.contains('=') && !line.trim().starts_with('#'));
        
        if has_sections || has_keys {
            score += 0.2;
        }
        
        // Check for proper line endings
        if content.contains('\n') {
            score += 0.1;
        }
        
        score.min(1.0)
    }
}

/// INI validator
pub struct IniValidator;

impl Validator for IniValidator {
    fn is_valid(&self, content: &str) -> bool {
        if content.trim().is_empty() {
            return false;
        }
        
        // Check for missing equals signs in key-value pairs
        let lines: Vec<&str> = content.lines().collect();
        for line in &lines {
            let line = line.trim();
            if line.is_empty() || line.starts_with('#') || line.starts_with('[') {
                continue;
            }
            
            // If line contains spaces but no equals sign, it's invalid
            if line.contains(' ') && !line.contains('=') {
                return false;
            }
        }
        
        // Basic INI validation - check for common INI patterns
        let has_sections = lines.iter().any(|line| {
            let line = line.trim();
            line.starts_with('[') && line.contains(']')
        });
        let has_keys = lines.iter().any(|line| {
            let line = line.trim();
            line.contains('=') && !line.starts_with('#') && !line.starts_with('[')
        });
        
        has_sections || has_keys
    }
    
    fn validate(&self, content: &str) -> Vec<String> {
        let mut errors = Vec::new();
        
        if content.trim().is_empty() {
            errors.push("Empty INI content".to_string());
            return errors;
        }
        
        // Basic INI validation - check for common issues
        let lines: Vec<&str> = content.lines().collect();
        for (i, line) in lines.iter().enumerate() {
            let line = line.trim();
            if line.is_empty() || line.starts_with('#') {
                continue;
            }
            
            if line.starts_with('[') && !line.contains(']') {
                errors.push(format!("Malformed section header at line {}: {}", i + 1, line));
            } else if line.contains('=') {
                let parts: Vec<&str> = line.splitn(2, '=').collect();
                if parts.len() != 2 {
                    errors.push(format!("Malformed key-value pair at line {}: {}", i + 1, line));
                }
            }
        }
        
        errors
    }
}

/// Strategy to fix malformed section headers
struct FixMalformedSectionsStrategy;

impl RepairStrategy for FixMalformedSectionsStrategy {
    fn apply(&self, content: &str) -> Result<String> {
        let cache = get_ini_regex_cache();
        let _result = cache.malformed_sections.replace_all(content, |caps: &regex::Captures| {
            let indent = &caps[1];
            let section_name = &caps[2];
            format!("{}[{}]", indent, section_name)
        });
        
        // Also try a simpler approach for lines that start with [ but don't end with ]
        let lines: Vec<&str> = content.lines().collect();
        let mut result_lines = Vec::new();
        for line in lines {
            let trimmed = line.trim();
            if trimmed.starts_with('[') && !trimmed.ends_with(']') {
                let indent = line.chars().take_while(|c| c.is_whitespace()).collect::<String>();
                let section_name = trimmed.trim_start_matches('[');
                result_lines.push(format!("{}[{}]", indent, section_name));
            } else {
                result_lines.push(line.to_string());
            }
        }
        let result = result_lines.join("\n");
        Ok(result.to_string())
    }
    
    fn priority(&self) -> u8 {
        6
    }

    fn name(&self) -> &str {
        "FixMalformedSectionsStrategy"
    }
}

/// Strategy to fix malformed keys
struct FixMalformedKeysStrategy;

impl RepairStrategy for FixMalformedKeysStrategy {
    fn apply(&self, content: &str) -> Result<String> {
        let cache = get_ini_regex_cache();
        let result = cache.malformed_keys.replace_all(content, |caps: &regex::Captures| {
            let indent = &caps[1];
            let key = &caps[2];
            let value = &caps[3];
            format!("{}{} = {}", indent, key, value)
        });
        
        Ok(result.to_string())
    }
    
    fn priority(&self) -> u8 {
        5
    }

    fn name(&self) -> &str {
        "FixMalformedKeysStrategy"
    }
}

/// Strategy to fix missing equals signs
struct FixMissingEqualsStrategy;

impl RepairStrategy for FixMissingEqualsStrategy {
    fn apply(&self, content: &str) -> Result<String> {
        let lines: Vec<&str> = content.lines().collect();
        let mut result = Vec::new();
        
        for line in lines {
            let trimmed = line.trim();
            if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with('[') {
                result.push(line.to_string());
                continue;
            }
            
            // If line contains spaces but no equals sign, add equals sign
            if trimmed.contains(' ') && !trimmed.contains('=') {
                let parts: Vec<&str> = trimmed.splitn(2, ' ').collect();
                if parts.len() == 2 {
                    let key = parts[0];
                    let value = parts[1];
                    let indent = line.chars().take_while(|c| c.is_whitespace()).collect::<String>();
                    result.push(format!("{}{} = {}", indent, key, value));
                } else {
                    result.push(line.to_string());
                }
            } else {
                result.push(line.to_string());
            }
        }
        
        Ok(result.join("\n"))
    }
    
    fn priority(&self) -> u8 {
        4
    }

    fn name(&self) -> &str {
        "FixMissingEqualsStrategy"
    }
}

/// Strategy to fix unquoted values that should be quoted
struct FixUnquotedValuesStrategy;

impl RepairStrategy for FixUnquotedValuesStrategy {
    fn apply(&self, content: &str) -> Result<String> {
        let cache = get_ini_regex_cache();
        let result = cache.unquoted_values.replace_all(content, |caps: &regex::Captures| {
            let indent = &caps[1];
            let key = &caps[2];
            let value = &caps[3];
            // Only quote if it contains spaces or special characters
            if value.contains(' ') || value.contains(',') || value.contains(';') {
                format!("{}{} = \"{}\"", indent, key, value)
            } else {
                format!("{}{} = {}", indent, key, value)
            }
        });
        
        Ok(result.to_string())
    }
    
    fn priority(&self) -> u8 {
        3
    }

    fn name(&self) -> &str {
        "FixUnquotedValuesStrategy"
    }
}

/// Strategy to fix malformed comments
struct FixMalformedCommentsStrategy;

impl RepairStrategy for FixMalformedCommentsStrategy {
    fn apply(&self, content: &str) -> Result<String> {
        let cache = get_ini_regex_cache();
        let result = cache.malformed_comments.replace_all(content, |caps: &regex::Captures| {
            let indent = &caps[1];
            let comment = &caps[2];
            format!("{}{} {}", indent, "#", comment)
        });
        
        Ok(result.to_string())
    }
    
    fn priority(&self) -> u8 {
        2
    }

    fn name(&self) -> &str {
        "FixMalformedCommentsStrategy"
    }
}

/// Strategy to remove duplicate sections
struct RemoveDuplicateSectionsStrategy;

impl RepairStrategy for RemoveDuplicateSectionsStrategy {
    fn apply(&self, content: &str) -> Result<String> {
        let cache = get_ini_regex_cache();
        let mut seen_sections = std::collections::HashSet::new();
        let mut result = Vec::new();
        
        for line in content.lines() {
            if let Some(caps) = cache.duplicate_sections.captures(line) {
                let section_name = &caps[1];
                if seen_sections.contains(section_name) {
                    // Skip duplicate section
                    continue;
                }
                seen_sections.insert(section_name.to_string());
            }
            result.push(line);
        }
        
        Ok(result.join("\n"))
    }
    
    fn priority(&self) -> u8 {
        1
    }

    fn name(&self) -> &str {
        "RemoveDuplicateSectionsStrategy"
    }
}

/// Strategy to add default section if missing
struct AddDefaultSectionStrategy;

impl RepairStrategy for AddDefaultSectionStrategy {
    fn apply(&self, content: &str) -> Result<String> {
        let lines: Vec<&str> = content.lines().collect();
        if lines.is_empty() {
            return Ok(content.to_string());
        }
        
        let first_line = lines[0].trim();
        
        // Check if first line is a key-value pair without a section
        if first_line.contains('=') && !first_line.starts_with('[') {
            let mut result = vec!["[default]".to_string()];
            result.extend(lines.iter().map(|s| s.to_string()));
            Ok(result.join("\n"))
        } else {
            Ok(content.to_string())
        }
    }
    
    fn priority(&self) -> u8 {
        0
    }

    fn name(&self) -> &str {
        "AddDefaultSectionStrategy"
    }
}
