"""
Configuration Validation Module
Add configuration validation and error handling
"""

import json
import os
import re
from typing import Dict, List, Optional, Any
from colorama import Fore, Style

class ConfigValidator:
    """Validate and handle configuration files"""
    
    def __init__(self, config_path: str = "config.json"):
        self.config_path = config_path
        self.config_schema = self.get_config_schema()
        self.errors = []
        self.warnings = []
    
    def get_config_schema(self) -> Dict:
        """Define the expected configuration schema"""
        return {
            "tool_info": {
                "required": ["name", "version", "description"],
                "fields": {
                    "name": {"type": str, "min_length": 1},
                    "version": {"type": str, "pattern": r"^\d+\.\d+\.\d+$"},
                    "description": {"type": str, "min_length": 10}
                }
            },
            "api_keys": {
                "required": [],
                "fields": {
                    "haveibeenpwned": {"type": str, "optional": True},
                    "hunter": {"type": str, "optional": True},
                    "abuseipdb": {"type": str, "optional": True},
                    "shodan": {"type": str, "optional": True}
                }
            },
            "output": {
                "required": ["format", "verbose"],
                "fields": {
                    "format": {"type": str, "choices": ["json", "text", "csv"]},
                    "verbose": {"type": bool},
                    "save_results": {"type": bool, "optional": True},
                    "output_dir": {"type": str, "optional": True}
                }
            },
            "username_search": {
                "required": ["max_concurrent", "timeout", "retry_attempts"],
                "fields": {
                    "max_concurrent": {"type": int, "min": 1, "max": 50},
                    "timeout": {"type": int, "min": 5, "max": 60},
                    "retry_attempts": {"type": int, "min": 1, "max": 5},
                    "request_delay": {"type": float, "min": 0.1, "max": 5.0, "optional": True}
                }
            },
            "email_investigation": {
                "required": ["verify_domains", "check_breaches", "check_disposable"],
                "fields": {
                    "verify_domains": {"type": bool},
                    "check_breaches": {"type": bool},
                    "check_disposable": {"type": bool},
                    "social_associations": {"type": bool, "optional": True}
                }
            },
            "domain_analysis": {
                "required": ["dns_enumeration", "subdomain_enumeration", "http_analysis"],
                "fields": {
                    "dns_enumeration": {"type": bool},
                    "subdomain_enumeration": {"type": bool},
                    "http_analysis": {"type": bool},
                    "ssl_analysis": {"type": bool, "optional": True}
                }
            },
            "ip_analysis": {
                "required": ["geolocation", "port_scan", "reverse_dns"],
                "fields": {
                    "geolocation": {"type": bool},
                    "port_scan": {"type": bool},
                    "reverse_dns": {"type": bool},
                    "vpn_detection": {"type": bool, "optional": True},
                    "abuse_check": {"type": bool, "optional": True}
                }
            }
        }
    
    def validate_field(self, field_name: str, value: Any, field_schema: Dict) -> bool:
        """Validate a single field against its schema"""
        # Check type
        if "type" in field_schema and not isinstance(value, field_schema["type"]):
            self.errors.append(f"Field '{field_name}' must be of type {field_schema['type'].__name__}")
            return False
        
        # Check choices
        if "choices" in field_schema and value not in field_schema["choices"]:
            self.errors.append(f"Field '{field_name}' must be one of {field_schema['choices']}")
            return False
        
        # Check pattern (for strings)
        if "pattern" in field_schema and isinstance(value, str):
            if not re.match(field_schema["pattern"], value):
                self.errors.append(f"Field '{field_name}' does not match required pattern")
                return False
        
        # Check min/max for numbers
        if isinstance(value, (int, float)):
            if "min" in field_schema and value < field_schema["min"]:
                self.errors.append(f"Field '{field_name}' must be >= {field_schema['min']}")
                return False
            if "max" in field_schema and value > field_schema["max"]:
                self.errors.append(f"Field '{field_name}' must be <= {field_schema['max']}")
                return False
        
        # Check min_length for strings
        if isinstance(value, str) and "min_length" in field_schema:
            if len(value) < field_schema["min_length"]:
                self.errors.append(f"Field '{field_name}' must be at least {field_schema['min_length']} characters")
                return False
        
        return True
    
    def validate_section(self, section_name: str, section_data: Dict, section_schema: Dict) -> bool:
        """Validate a configuration section"""
        is_valid = True
        
        # Check required fields
        for required_field in section_schema.get("required", []):
            if required_field not in section_data:
                self.errors.append(f"Required field '{required_field}' missing from section '{section_name}'")
                is_valid = False
        
        # Validate all present fields
        for field_name, field_value in section_data.items():
            if field_name in section_schema["fields"]:
                field_schema = section_schema["fields"][field_name]
                if not self.validate_field(f"{section_name}.{field_name}", field_value, field_schema):
                    is_valid = False
            else:
                self.warnings.append(f"Unknown field '{field_name}' in section '{section_name}'")
        
        return is_valid
    
    def validate_config(self, config: Dict) -> bool:
        """Validate the entire configuration"""
        self.errors = []
        self.warnings = []
        is_valid = True
        
        # Check required sections
        required_sections = ["tool_info", "output"]
        for section in required_sections:
            if section not in config:
                self.errors.append(f"Required section '{section}' missing from configuration")
                is_valid = False
        
        # Validate each section
        for section_name, section_data in config.items():
            if section_name in self.config_schema:
                if not self.validate_section(section_name, section_data, self.config_schema[section_name]):
                    is_valid = False
            else:
                self.warnings.append(f"Unknown section '{section_name}' in configuration")
        
        return is_valid
    
    def load_and_validate_config(self) -> Dict:
        """Load configuration file and validate it"""
        # Check if config file exists
        if not os.path.exists(self.config_path):
            print(f"{Fore.YELLOW}[!] Configuration file '{self.config_path}' not found{Style.RESET_ALL}")
            print(f"{Fore.YELLOW}[!] Creating default configuration file...{Style.RESET_ALL}")
            self.create_default_config()
            return self.load_and_validate_config()
        
        try:
            # Load configuration
            with open(self.config_path, 'r') as f:
                config = json.load(f)
            
            # Validate configuration
            if self.validate_config(config):
                if self.warnings:
                    print(f"{Fore.YELLOW}[!] Configuration warnings:{Style.RESET_ALL}")
                    for warning in self.warnings:
                        print(f"  - {warning}{Style.RESET_ALL}")
                return config
            else:
                print(f"{Fore.RED}[-] Configuration validation failed:{Style.RESET_ALL}")
                for error in self.errors:
                    print(f"  - {error}{Style.RESET_ALL}")
                return None
        
        except json.JSONDecodeError as e:
            print(f"{Fore.RED}[-] Invalid JSON in configuration file: {str(e)}{Style.RESET_ALL}")
            return None
        except Exception as e:
            print(f"{Fore.RED}[-] Error loading configuration: {str(e)}{Style.RESET_ALL}")
            return None
    
    def create_default_config(self):
        """Create a default configuration file"""
        default_config = {
            "tool_info": {
                "name": "OSINT Intelligence Framework",
                "version": "2.0.0",
                "description": "Terminal-based Open Source Intelligence Tool"
            },
            "api_keys": {
                "haveibeenpwned": "",
                "hunter": "",
                "abuseipdb": "",
                "shodan": ""
            },
            "output": {
                "format": "json",
                "verbose": True,
                "save_results": True,
                "output_dir": "results"
            },
            "username_search": {
                "max_concurrent": 20,
                "timeout": 15,
                "retry_attempts": 3,
                "request_delay": 0.5
            },
            "email_investigation": {
                "verify_domains": True,
                "check_breaches": True,
                "check_disposable": True,
                "social_associations": True
            },
            "domain_analysis": {
                "dns_enumeration": True,
                "subdomain_enumeration": True,
                "http_analysis": True,
                "ssl_analysis": True
            },
            "ip_analysis": {
                "geolocation": True,
                "port_scan": True,
                "reverse_dns": True,
                "vpn_detection": True,
                "abuse_check": True
            }
        }
        
        try:
            with open(self.config_path, 'w') as f:
                json.dump(default_config, f, indent=2)
            print(f"{Fore.GREEN}[+] Default configuration created: {self.config_path}{Style.RESET_ALL}")
        except Exception as e:
            print(f"{Fore.RED}[-] Error creating default configuration: {str(e)}{Style.RESET_ALL}")
    
    def get_api_key(self, service: str) -> Optional[str]:
        """Get API key for a specific service"""
        try:
            with open(self.config_path, 'r') as f:
                config = json.load(f)
                return config.get("api_keys", {}).get(service, "")
        except:
            return ""
    
    def validate_api_key(self, service: str, api_key: str) -> bool:
        """Basic validation of API key format"""
        if not api_key:
            return False
        
        # Basic format checks for different services
        if service == "haveibeenpwned":
            return len(api_key) >= 20  # HIBP keys are typically long
        elif service == "hunter":
            return len(api_key) >= 10
        elif service == "abuseipdb":
            return len(api_key) >= 10
        elif service == "shodan":
            return len(api_key) >= 20
        else:
            return len(api_key) >= 5  # Generic check
    
    def check_api_keys(self) -> Dict[str, bool]:
        """Check which API keys are configured and valid"""
        api_status = {}
        services = ["haveibeenpwned", "hunter", "abuseipdb", "shodan"]
        
        for service in services:
            api_key = self.get_api_key(service)
            is_valid = self.validate_api_key(service, api_key)
            api_status[service] = is_valid
        
        return api_status
