from typing import Dict, List, Optional
import logging
import re
import os
import asyncio
from datetime import datetime
from dotenv import load_dotenv

load_dotenv()

logger = logging.getLogger(__name__)

# Check if emergentintegrations is available
try:
    from emergentintegrations.llm.chat import LlmChat, UserMessage
    EMERGENT_AVAILABLE = True
except ImportError:
    EMERGENT_AVAILABLE = False
    logger.warning("emergentintegrations not available - AI features will be limited")


class AIConfigEngine:
    """AI-powered configuration engine with LLM support"""
    
    def __init__(self, db):
        self.db = db
        self.config_templates = {}
        self.llm_key = os.getenv('EMERGENT_LLM_KEY')
        self.manual_llm_key = None  # For user-provided keys
    
    def set_manual_key(self, key: str, provider: str = 'openai'):
        """Set manual LLM key (user-provided)"""
        self.manual_llm_key = key
        self.manual_provider = provider
    
    def _get_llm_chat(self, session_id: str, system_message: str) -> Optional['LlmChat']:
        """Initialize LLM chat with appropriate key"""
        if not EMERGENT_AVAILABLE:
            return None
        
        key = self.manual_llm_key or self.llm_key
        if not key:
            return None
        
        try:
            chat = LlmChat(
                api_key=key,
                session_id=session_id,
                system_message=system_message
            ).with_model("openai", "gpt-5.4")
            return chat
        except Exception as e:
            logger.error(f"Failed to initialize LLM: {e}")
            return None
    
    async def auto_detect_device_capabilities(self, device_id: str, org_id: str = None) -> Dict:
        """Auto-detect what a device can do"""
        query = {'id': device_id}
        if org_id:
            query['organization_id'] = org_id
        device = await self.db.devices.find_one(query)
        if not device:
            raise Exception("Device not found")
        
        capabilities = {
            'device_id': device_id,
            'vendor': device.get('vendor', 'Unknown'),
            'model': device.get('model', 'Unknown'),
            'device_type': device.get('device_type', 'Unknown'),
            'features': []
        }
        
        vendor_lower = device.get('vendor', '').lower()
        device_type_lower = device.get('device_type', '').lower()
        
        # Detect capabilities based on vendor/type
        if 'mikrotik' in vendor_lower:
            capabilities['features'] = [
                'routing', 'nat', 'firewall', 'queues', 'pppoe', 
                'dhcp', 'wireless', 'vpn', 'hotspot'
            ]
        elif 'cisco' in vendor_lower:
            if 'switch' in device_type_lower:
                capabilities['features'] = ['switching', 'vlan', 'stp', 'port-security']
            else:
                capabilities['features'] = ['routing', 'bgp', 'ospf', 'eigrp', 'acl']
        elif 'huawei' in vendor_lower and 'olt' in device_type_lower:
            capabilities['features'] = ['gpon', 'onu-management', 'service-port', 'vlan']
        elif 'switch' in device_type_lower:
            capabilities['features'] = ['switching', 'vlan', 'stp']
        elif 'router' in device_type_lower:
            capabilities['features'] = ['routing', 'nat', 'firewall']
        elif 'firewall' in device_type_lower:
            capabilities['features'] = ['firewall', 'nat', 'vpn', 'ids']
        elif 'access' in device_type_lower or 'wifi' in device_type_lower:
            capabilities['features'] = ['wireless', 'ssid', 'security']
        
        # Store capabilities
        await self.db.devices.update_one(
            {'id': device_id},
            {'$set': {'capabilities': capabilities['features']}}
        )
        
        return capabilities
    
    def generate_basic_config(self, vendor: str, device_type: str, 
                             hostname: str, management_ip: str) -> str:
        """Generate basic configuration"""
        vendor_lower = vendor.lower()
        
        if 'mikrotik' in vendor_lower:
            return self._generate_mikrotik_basic(hostname, management_ip)
        elif 'cisco' in vendor_lower:
            return self._generate_cisco_basic(hostname, management_ip)
        elif 'huawei' in vendor_lower:
            return self._generate_huawei_basic(hostname, management_ip)
        else:
            return self._generate_generic_basic(hostname, management_ip)
    
    def _generate_mikrotik_basic(self, hostname: str, ip: str) -> str:
        """Generate MikroTik basic config"""
        config = f"""# MikroTik Basic Configuration
/system identity set name={hostname}

# Set IP address
/ip address add address={ip}/24 interface=ether1

# Enable SNMP
/snmp set enabled=yes contact="Network Admin" location="Data Center"
/snmp community set public read-access=yes

# Enable SSH
/ip service enable ssh
/ip service disable telnet

# Set DNS
/ip dns set servers=8.8.8.8,8.8.4.4 allow-remote-requests=yes

# Enable NTP
/system ntp client set enabled=yes primary-ntp=pool.ntp.org

# Basic Firewall
/ip firewall filter
add chain=input action=accept connection-state=established,related comment="Accept established"
add chain=input action=accept protocol=icmp comment="Accept ICMP"
add chain=input action=accept src-address={ip}/24 comment="Accept from LAN"
add chain=input action=drop comment="Drop all else"
"""
        return config
    
    def _generate_cisco_basic(self, hostname: str, ip: str) -> str:
        """Generate Cisco basic config"""
        config = f"""! Cisco Basic Configuration
hostname {hostname}

! Enable SSH
ip domain-name local.domain
crypto key generate rsa modulus 2048
line vty 0 4
 transport input ssh
 login local
ip ssh version 2

! Set IP address (assuming vlan1)
interface Vlan1
 ip address {ip} 255.255.255.0
 no shutdown

! Enable SNMP
snmp-server community public RO
snmp-server location Data Center
snmp-server contact Network Admin

! NTP
ntp server pool.ntp.org

! DNS
ip name-server 8.8.8.8 8.8.4.4

! Logging
logging buffered 10000
logging console warnings

! Save config
end
write memory
"""
        return config
    
    def _generate_huawei_basic(self, hostname: str, ip: str) -> str:
        """Generate Huawei basic config"""
        config = f"""# Huawei Basic Configuration
sysname {hostname}

# Set IP address (MEth interface)
interface MEth 0/0/0
 ip address {ip} 255.255.255.0
 undo shutdown

# Enable SNMP
snmp-agent
snmp-agent community read public
snmp-agent sys-info contact Network Admin
snmp-agent sys-info location Data Center

# Enable SSH
ssh server enable
stelnet server enable

# NTP
ntp-service unicast-server pool.ntp.org

# Save
save
"""
        return config
    
    def _generate_generic_basic(self, hostname: str, ip: str) -> str:
        """Generate generic basic config"""
        config = f"""# Generic Basic Configuration
# Hostname: {hostname}
# Management IP: {ip}

# Set hostname
hostname {hostname}

# Set IP address
ip address {ip} 255.255.255.0

# Enable SNMP
snmp-server community public RO

# Set DNS
nameserver 8.8.8.8
nameserver 8.8.4.4

# Enable SSH
service ssh

# Save configuration
write memory
"""
        return config
    
    async def generate_vlan_config(self, vendor: str, vlan_id: int,
                                  vlan_name: str, ports: List[str]) -> str:
        """Generate VLAN configuration"""
        vendor_lower = vendor.lower()
        
        if 'cisco' in vendor_lower:
            config = f"vlan {vlan_id}\n name {vlan_name}\n!\n"
            for port in ports:
                config += f"interface {port}\n switchport mode access\n switchport access vlan {vlan_id}\n!\n"
        elif 'mikrotik' in vendor_lower:
            config = f"/interface vlan add name=vlan{vlan_id} vlan-id={vlan_id} interface=bridge\n"
            for port in ports:
                config += f"/interface bridge port set [find interface={port}] pvid={vlan_id}\n"
        else:
            config = f"# VLAN {vlan_id} - {vlan_name}\n"
            config += f"vlan {vlan_id}\n name {vlan_name}\n"
        
        return config
    
    async def generate_dhcp_config(self, vendor: str, pool_name: str,
                                  network: str, gateway: str,
                                  dns: List[str]) -> str:
        """Generate DHCP server configuration"""
        vendor_lower = vendor.lower()
        
        if 'cisco' in vendor_lower:
            config = f"""ip dhcp pool {pool_name}
 network {network}
 default-router {gateway}
 dns-server {' '.join(dns)}
 lease 7
!
"""
        elif 'mikrotik' in vendor_lower:
            config = f"""/ip pool add name={pool_name}-pool ranges={network}
/ip dhcp-server add name={pool_name} interface=bridge address-pool={pool_name}-pool disabled=no
/ip dhcp-server network add address={network} gateway={gateway} dns-server={','.join(dns)}
"""
        else:
            config = f"# DHCP Pool: {pool_name}\n"
            config += f"dhcp-pool {pool_name}\n network {network}\n gateway {gateway}\n"
        
        return config
    
    async def ai_suggest_optimization(self, device_id: str, org_id: str = None) -> Dict:
        """AI-powered optimization suggestions"""
        query = {'id': device_id}
        if org_id:
            query['organization_id'] = org_id
        device = await self.db.devices.find_one(query)
        if not device:
            raise Exception("Device not found")
        
        suggestions = []
        
        # Check if device has high CPU
        cpu_usage = device.get('cpu_usage', 0)
        if cpu_usage and cpu_usage > 80:
            suggestions.append({
                'type': 'performance',
                'severity': 'high',
                'issue': 'High CPU usage detected',
                'suggestion': 'Consider upgrading hardware or optimizing running processes',
                'commands': []
            })
        
        # Check if SNMP is disabled
        if not device.get('snmp_enabled'):
            suggestions.append({
                'type': 'monitoring',
                'severity': 'medium',
                'issue': 'SNMP not enabled',
                'suggestion': 'Enable SNMP for better monitoring',
                'commands': ['snmp-server community public RO']
            })
        
        # Check if device has no backup
        last_backup = device.get('last_backup_at')
        if not last_backup:
            suggestions.append({
                'type': 'backup',
                'severity': 'high',
                'issue': 'No configuration backup found',
                'suggestion': 'Create a configuration backup immediately',
                'commands': []
            })
        
        return {
            'device_id': device_id,
            'suggestions_count': len(suggestions),
            'suggestions': suggestions
        }
    
    async def ai_generate_config(self, device_id: str, requirements: str, org_id: str = None) -> Dict:
        """Use AI to generate configuration based on requirements"""
        query = {'id': device_id}
        if org_id:
            query['organization_id'] = org_id
        device = await self.db.devices.find_one(query)
        if not device:
            raise Exception("Device not found")
        
        vendor = device.get('vendor', 'generic')
        device_type = device.get('device_type', 'router')
        
        system_message = f"""You are an expert network engineer specializing in {vendor} devices.
You generate precise, production-ready configuration scripts.
Device type: {device_type}
Vendor: {vendor}
Only output the configuration commands, no explanations unless asked.
Always include comments explaining each section."""

        chat = self._get_llm_chat(f"config-{device_id}", system_message)
        
        if not chat:
            # Fallback to template-based generation
            return {
                'device_id': device_id,
                'vendor': vendor,
                'ai_generated': False,
                'config': self._generate_fallback_config(vendor, requirements),
                'message': 'AI not available - using template-based generation'
            }
        
        try:
            user_message = UserMessage(
                text=f"""Generate configuration for the following requirements:
{requirements}

Device: {vendor} {device_type}
IP: {device.get('ip', 'unknown')}
Hostname: {device.get('hostname', 'device')}"""
            )
            
            response = await chat.send_message(user_message)
            
            return {
                'device_id': device_id,
                'vendor': vendor,
                'ai_generated': True,
                'config': response.text,
                'requirements': requirements
            }
        except Exception as e:
            logger.error(f"AI config generation failed: {e}")
            return {
                'device_id': device_id,
                'vendor': vendor,
                'ai_generated': False,
                'config': self._generate_fallback_config(vendor, requirements),
                'error': str(e)
            }
    
    def _generate_fallback_config(self, vendor: str, requirements: str) -> str:
        """Generate basic config when AI is not available"""
        return f"""# Configuration generated without AI
# Vendor: {vendor}
# Requirements: {requirements}
# 
# Please manually configure based on requirements above
# or enable AI by providing API key
"""
    
    async def ai_explain_config(self, config: str, vendor: str) -> Dict:
        """Use AI to explain a configuration"""
        system_message = f"""You are an expert network engineer specializing in {vendor} devices.
Explain the given configuration in simple terms.
Break down each section and explain what it does.
Highlight any security concerns or best practice violations."""

        chat = self._get_llm_chat(f"explain-config-{datetime.utcnow().timestamp()}", system_message)
        
        if not chat:
            return {
                'ai_available': False,
                'explanation': 'AI explanation not available. Please review configuration manually.',
                'vendor': vendor
            }
        
        try:
            user_message = UserMessage(
                text=f"Please explain this {vendor} configuration:\n\n{config}"
            )
            
            response = await chat.send_message(user_message)
            
            return {
                'ai_available': True,
                'explanation': response.text,
                'vendor': vendor,
                'config_length': len(config)
            }
        except Exception as e:
            logger.error(f"AI explanation failed: {e}")
            return {
                'ai_available': False,
                'explanation': f'AI explanation failed: {str(e)}',
                'vendor': vendor
            }
    
    async def ai_optimize_config(self, config: str, vendor: str) -> Dict:
        """Use AI to optimize a configuration"""
        system_message = f"""You are an expert network engineer specializing in {vendor} devices.
Analyze the given configuration and suggest optimizations.
Focus on:
1. Security improvements
2. Performance optimizations
3. Best practices
4. Redundant or conflicting rules
Output the optimized configuration with comments explaining changes."""

        chat = self._get_llm_chat(f"optimize-config-{datetime.utcnow().timestamp()}", system_message)
        
        if not chat:
            return {
                'ai_available': False,
                'optimized': False,
                'message': 'AI optimization not available'
            }
        
        try:
            user_message = UserMessage(
                text=f"Please optimize this {vendor} configuration:\n\n{config}"
            )
            
            response = await chat.send_message(user_message)
            
            return {
                'ai_available': True,
                'optimized': True,
                'original_config': config,
                'optimized_config': response.text,
                'vendor': vendor
            }
        except Exception as e:
            logger.error(f"AI optimization failed: {e}")
            return {
                'ai_available': False,
                'optimized': False,
                'error': str(e)
            }
    
    async def validate_config(self, config: str, vendor: str) -> Dict:
        """Validate configuration syntax"""
        errors = []
        warnings = []
        
        # Basic validation
        lines = config.split('\n')
        
        # Check for empty config
        if not config.strip():
            errors.append("Configuration is empty")
        
        # Check for common syntax errors
        for i, line in enumerate(lines, 1):
            line = line.strip()
            if not line or line.startswith('#') or line.startswith('!'):
                continue
            
            # Check for unmatched quotes
            if line.count('"') % 2 != 0:
                warnings.append(f"Line {i}: Unmatched quotes")
            
            # Check for very long lines
            if len(line) > 200:
                warnings.append(f"Line {i}: Very long command")
        
        is_valid = len(errors) == 0
        
        return {
            'valid': is_valid,
            'errors': errors,
            'warnings': warnings,
            'line_count': len(lines)
        }


# Global instance will be set with db
ai_config_engine = None
