"""
Rule Engine Module
Evaluates mutability rules and constraints for semi-mutable files.
"""

import os
import socket
from typing import Dict, List, Optional, Any
from .section_parser import SectionParser


class RuleEngine:
    """
    Evaluates section-based mutability rules and constraints.
    Handles special cases like hostname matching and dynamic values.
    """
    
    def __init__(self, hostname: Optional[str] = None):
        """
        Initialize the rule engine.
        
        Args:
            hostname: System hostname (auto-detected if not provided)
        """
        self.hostname = hostname or self._get_hostname()
    
    @staticmethod
    def _get_hostname() -> str:
        """Get the system hostname."""
        try:
            return socket.gethostname()
        except Exception:
            return "stellarmate"  # Default fallback
    
    def validate_section(self, current_parser: SectionParser, 
                        reference_parser: SectionParser,
                        section_name: str,
                        rules: Dict[str, Any],
                        revert_only_if_empty: bool = False) -> tuple[bool, List[str], Dict[str, str], List[str]]:
        """
        Validate a section according to its rules.
        
        Args:
            current_parser: Parser for current file
            reference_parser: Parser for reference file
            section_name: Name of section to validate
            rules: Rules for this section including immutable_keys, mutable_keys, constraints
            revert_only_if_empty: If True, only revert mismatches if current value is empty/missing
            
        Returns:
            Tuple of (is_valid, list of violations, dict of required fixes, list of warning-only violations)
        """
        violations = []
        fixes = {}
        warnings = []
        
        current_section = current_parser.get_section(section_name)
        reference_section = reference_parser.get_section(section_name)
        
        if current_section is None:
            violations.append(f"Section {section_name} missing in current file")
            # If section is missing, we might want to restore all immutable keys
            if reference_section:
                for key in rules.get('immutable_keys', []):
                    if key in reference_section:
                        fixes[key] = reference_section[key]
            return False, violations, fixes, warnings
        
        if reference_section is None:
            warnings.append(f"Section {section_name} missing in reference file")
            return True, violations, fixes, warnings
        
        # Check immutable keys
        immutable_keys = rules.get('immutable_keys', [])
        for key in immutable_keys:
            current_value = current_section.get(key)
            reference_value = reference_section.get(key)
            
            if current_value != reference_value:
                is_empty = current_value is None or current_value.strip() == ""
                
                if revert_only_if_empty:
                    if is_empty:
                        violations.append(
                            f"{section_name}.{key}: missing or empty, required value='{reference_value}'"
                        )
                        fixes[key] = reference_value
                    else:
                        warnings.append(
                            f"{section_name}.{key}: current='{current_value}' "
                            f"!= reference='{reference_value}' (Preserving current value)"
                        )
                else:
                    # Strict mode: mismatch always needs fix
                    violations.append(
                        f"{section_name}.{key}: current='{current_value}' "
                        f"!= reference='{reference_value}'"
                    )
                    fixes[key] = reference_value
        
        # Check required keys (must exist even if mutable)
        required_keys = rules.get('required_keys', [])
        for key in required_keys:
            if key not in current_section and key in reference_section:
                reference_value = reference_section.get(key)
                violations.append(
                    f"{section_name}.{key}: missing in current file"
                )
                fixes[key] = reference_value

        # Check constraints on mutable keys
        constraints = rules.get('constraints', {})
        for key, constraint in constraints.items():
            current_value = current_section.get(key)
            
            if constraint == "must_match_hostname":
                if current_value != self.hostname:
                    violations.append(
                        f"{section_name}.{key}: current='{current_value}' "
                        f"must match hostname='{self.hostname}'"
                    )
                    fixes[key] = self.hostname
        
        return len(violations) == 0, violations, fixes, warnings
    
    def validate_file(self, current_file: str, reference_file: str,
                     section_rules: Dict[str, Dict[str, Any]],
                     revert_only_if_empty: bool = False) -> tuple[bool, List[str], Dict[str, Dict[str, str]], List[str]]:
        """
        Validate an entire file according to section rules.
        
        Args:
            current_file: Path to current file
            reference_file: Path to reference file
            section_rules: Dictionary of section names to their rules
            revert_only_if_empty: If True, only revert mismatches if current value is empty/missing
            
        Returns:
            Tuple of (is_valid, list of all violations, dict of all fixes by section, list of warnings)
        """
        current_parser = SectionParser()
        reference_parser = SectionParser()
        
        if not current_parser.parse_file(current_file):
            return False, ["Failed to parse current file"], {}, []
        
        if not reference_parser.parse_file(reference_file):
            return False, ["Failed to parse reference file"], {}, []
        
        all_violations = []
        all_fixes = {}
        all_warnings = []
        
        for section_name, rules in section_rules.items():
            is_valid, violations, fixes, warnings = self.validate_section(
                current_parser, reference_parser, section_name, rules, revert_only_if_empty
            )
            
            all_warnings.extend(warnings)
            
            if not is_valid:
                all_violations.extend(violations)
                if fixes:
                    all_fixes[section_name] = fixes
        
        return len(all_violations) == 0, all_violations, all_fixes, all_warnings
    
    def apply_fixes(self, file_path: str, reference_file: str,
                   section_rules: Dict[str, Dict[str, Any]],
                   fixes: Dict[str, Dict[str, str]]) -> bool:
        """
        Apply fixes to a file while preserving mutable values.
        
        Args:
            file_path: Path to file to fix
            reference_file: Path to reference file
            section_rules: Section rules
            fixes: Dictionary of fixes by section
            
        Returns:
            True if successful, False otherwise
        """
        try:
            current_parser = SectionParser()
            reference_parser = SectionParser()
            
            if not current_parser.parse_file(file_path):
                return False
            
            if not reference_parser.parse_file(reference_file):
                return False
            
            # Apply fixes section by section
            for section_name, section_fixes in fixes.items():
                for key, value in section_fixes.items():
                    current_parser.set_value(section_name, key, value)
            
            # Write back to file
            return current_parser.write_to_file(file_path)
            
        except Exception:
            return False
    
    @staticmethod
    def create_network_manager_rules() -> Dict[str, Dict[str, Any]]:
        """
        Create standard rules for NetworkManager connection files.
        
        Returns:
            Dictionary of section rules
        """
        return {
            "[connection]": {
                "immutable_keys": [
                    "id", "uuid", "type", "autoconnect", "autoconnect-priority",
                    "permissions", "secondaries", "timestamp", "mdns"
                ],
                "mutable_keys": []
            },
            "[wifi]": {
                "immutable_keys": [
                    "mac-address-blacklist", "mac-address-randomization",
                    "mode", "seen-bssids"
                ],
                "mutable_keys": ["band", "channel", "ssid"],
                "constraints": {
                    "ssid": "must_match_hostname"
                }
            },
            "[wifi-security]": {
                "immutable_keys": ["group", "key-mgmt", "pairwise", "proto"],
                "mutable_keys": ["psk"]
            },
            "[ipv4]": {
                "immutable_keys": ["dns-search", "method", "address1"]
            },
            "[ipv6]": {
                "immutable_keys": [
                    "addr-gen-mode", "dns-search", "ip6-privacy", "method"
                ]
            }
        }
    
    @staticmethod
    def create_default_rules(file_type: str) -> Dict[str, Dict[str, Any]]:
        """
        Create default rules for common file types.
        
        Args:
            file_type: Type of file (e.g., 'networkmanager', 'systemd', etc.)
            
        Returns:
            Dictionary of section rules
        """
        if file_type == "networkmanager":
            return RuleEngine.create_network_manager_rules()
        
        # Add more file types as needed
