"""
Section Parser Module
Parses INI-style configuration files and preserves formatting.
"""

import re
from typing import Dict, List, Tuple, Optional
from collections import OrderedDict


class SectionParser:
    """
    Parses INI-style configuration files into sections and key-value pairs.
    Preserves original formatting for accurate comparison and modification.
    """
    
    def __init__(self):
        """Initialize the section parser."""
        self.sections: OrderedDict = OrderedDict()
        self.raw_content: str = ""
        self.section_order: List[str] = []
    
    def parse_file(self, file_path: str) -> bool:
        """
        Parse an INI-style configuration file.
        
        Args:
            file_path: Path to the file to parse
            
        Returns:
            True if successful, False otherwise
        """
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                self.raw_content = f.read()
            
            self._parse_content(self.raw_content)
            return True
            
        except FileNotFoundError:
            return False
        except Exception as e:
            return False
    
    def parse_string(self, content: str) -> bool:
        """
        Parse INI-style content from a string.
        
        Args:
            content: String content to parse
            
        Returns:
            True if successful, False otherwise
        """
        try:
            self.raw_content = content
            self._parse_content(content)
            return True
        except Exception:
            return False
    
    def _parse_content(self, content: str):
        """
        Internal method to parse content into sections.
        
        Args:
            content: Content to parse
        """
        self.sections = OrderedDict()
        self.section_order = []
        
        current_section = None
        section_pattern = re.compile(r'^\[([^\]]+)\]$')
        key_value_pattern = re.compile(r'^([^=]+)=(.*)$')
        
        for line in content.split('\n'):
            stripped_line = line.strip()
            
            # Check for section header
            section_match = section_pattern.match(stripped_line)
            if section_match:
                section_name = f"[{section_match.group(1)}]"
                current_section = section_name
                if section_name not in self.sections:
                    self.sections[section_name] = OrderedDict()
                    self.section_order.append(section_name)
                continue
            
            # Check for key-value pair
            if current_section and stripped_line and not stripped_line.startswith('#'):
                kv_match = key_value_pattern.match(stripped_line)
                if kv_match:
                    key = kv_match.group(1).strip()
                    value = kv_match.group(2).strip()
                    self.sections[current_section][key] = value
    
    def get_section(self, section_name: str) -> Optional[Dict[str, str]]:
        """
        Get all key-value pairs in a section.
        
        Args:
            section_name: Name of the section (e.g., "[connection]")
            
        Returns:
            Dictionary of key-value pairs, or None if section doesn't exist
        """
        return self.sections.get(section_name)
    
    def get_value(self, section_name: str, key: str) -> Optional[str]:
        """
        Get a specific value from a section.
        
        Args:
            section_name: Name of the section
            key: Key name
            
        Returns:
            Value string, or None if not found
        """
        section = self.get_section(section_name)
        if section:
            return section.get(key)
        return None
    
    def set_value(self, section_name: str, key: str, value: str):
        """
        Set a value in a section.
        
        Args:
            section_name: Name of the section
            key: Key name
            value: Value to set
        """
        if section_name not in self.sections:
            self.sections[section_name] = OrderedDict()
            self.section_order.append(section_name)
        
        self.sections[section_name][key] = value
    
    def has_section(self, section_name: str) -> bool:
        """
        Check if a section exists.
        
        Args:
            section_name: Name of the section
            
        Returns:
            True if section exists, False otherwise
        """
        return section_name in self.sections
    
    def get_all_sections(self) -> List[str]:
        """
        Get list of all section names.
        
        Returns:
            List of section names
        """
        return self.section_order
    
    def compare_section(self, section_name: str, reference_parser: 'SectionParser',
                       immutable_keys: List[str]) -> Tuple[bool, List[str]]:
        """
        Compare a section against a reference, checking only immutable keys.
        
        Args:
            section_name: Name of the section to compare
            reference_parser: Reference parser to compare against
            immutable_keys: List of keys that must match
            
        Returns:
            Tuple of (matches, list of mismatched keys)
        """
        mismatches = []
        
        current_section = self.get_section(section_name)
        reference_section = reference_parser.get_section(section_name)
        
        if current_section is None or reference_section is None:
            return False, ["Section missing"]
        
        for key in immutable_keys:
            current_value = current_section.get(key)
            reference_value = reference_section.get(key)
            
            if current_value != reference_value:
                mismatches.append(key)
        
        return len(mismatches) == 0, mismatches
    
    def update_from_reference(self, section_name: str, reference_parser: 'SectionParser',
                             keys_to_update: List[str]) -> bool:
        """
        Update specific keys in a section from a reference parser.
        
        Args:
            section_name: Name of the section to update
            reference_parser: Reference parser to copy values from
            keys_to_update: List of keys to update
            
        Returns:
            True if successful, False otherwise
        """
        try:
            reference_section = reference_parser.get_section(section_name)
            if reference_section is None:
                return False
            
            if section_name not in self.sections:
                self.sections[section_name] = OrderedDict()
                self.section_order.append(section_name)
            
            for key in keys_to_update:
                if key in reference_section:
                    self.sections[section_name][key] = reference_section[key]
            
            return True
        except Exception:
            return False
    
    def to_string(self) -> str:
        """
        Convert parsed sections back to INI-style string.
        
        Returns:
            INI-style formatted string
        """
        lines = []
        
        for section_name in self.section_order:
            lines.append(section_name)
            section = self.sections[section_name]
            
            for key, value in section.items():
                lines.append(f"{key}={value}")
            
            lines.append("")  # Empty line between sections
        
        return '\n'.join(lines)
    
    def write_to_file(self, file_path: str) -> bool:
        """
        Write parsed content to a file.
        
        Args:
            file_path: Path to write to
            
        Returns:
            True if successful, False otherwise
        """
        try:
            content = self.to_string()
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(content)
            return True
        except Exception:
            return False
    
    @staticmethod
    def merge_files(current_file: str, reference_file: str, output_file: str,
                   section_rules: Dict[str, Dict[str, List[str]]]) -> bool:
        """
        Merge two INI files based on section rules.
        
        Args:
            current_file: Path to current file
            reference_file: Path to reference file
            output_file: Path to output merged file
            section_rules: Rules for each section with immutable/mutable keys
            
        Returns:
            True if successful, False otherwise
        """
        try:
            current_parser = SectionParser()
            reference_parser = SectionParser()
            
            if not current_parser.parse_file(current_file):
                return False
            if not reference_parser.parse_file(reference_file):
                return False
            
            # For each section in rules
            for section_name, rules in section_rules.items():
                immutable_keys = rules.get('immutable_keys', [])
                
                # Update immutable keys from reference
                if immutable_keys:
                    current_parser.update_from_reference(
                        section_name, reference_parser, immutable_keys
                    )
            
            # Write merged content
            return current_parser.write_to_file(output_file)
            
        except Exception:
            return False
