from typing import Dict, List
from datetime import datetime

class TopologyBuilder:
    """Build network topology from discovered devices"""
    
    def __init__(self, db):
        self.db = db
    
    async def build_topology(self, org_id: str = None) -> Dict:
        """Build complete network topology scoped to organization"""
        query = {"organization_id": org_id} if org_id else {}
        devices = await self.db.devices.find(query).to_list(1000)
        
        nodes = []
        links = []
        
        # Build nodes
        for device in devices:
            node = {
                'id': device['id'],
                'ip': device['ip'],
                'hostname': device.get('hostname', device['ip']),
                'vendor': device.get('vendor', 'Unknown'),
                'type': device.get('device_type', 'Unknown'),
                'status': device['status'],
                'x': 0,  # Will be calculated by frontend
                'y': 0,
            }
            nodes.append(node)
            
            # Build links from neighbors
            neighbors = device.get('neighbors', {})
            
            # LLDP neighbors
            for lldp_neighbor in neighbors.get('lldp', []):
                target_device = await self._find_device_by_name(
                    lldp_neighbor.get('system_name', ''), org_id
                )
                
                if target_device:
                    link = {
                        'source': device['id'],
                        'target': target_device['id'],
                        'protocol': 'LLDP',
                        'port': lldp_neighbor.get('port_id', 'N/A'),
                    }
                    links.append(link)
            
            # CDP neighbors
            for cdp_neighbor in neighbors.get('cdp', []):
                target_device = await self._find_device_by_name(
                    cdp_neighbor.get('device_id', ''), org_id
                )
                
                if target_device:
                    link = {
                        'source': device['id'],
                        'target': target_device['id'],
                        'protocol': 'CDP',
                        'port': cdp_neighbor.get('port_id', 'N/A'),
                    }
                    links.append(link)
        
        # Remove duplicate links
        unique_links = self._remove_duplicate_links(links)
        
        return {
            'nodes': nodes,
            'links': unique_links,
            'generated_at': datetime.utcnow().isoformat()
        }
    
    async def _find_device_by_name(self, name: str, org_id: str = None):
        """Find device by hostname or IP, scoped to organization"""
        if not name:
            return None
        
        base_filter = {"organization_id": org_id} if org_id else {}
        
        device = await self.db.devices.find_one({**base_filter, 'hostname': name})
        if device:
            return device
        
        device = await self.db.devices.find_one({**base_filter, 'ip': name})
        return device
    
    def _remove_duplicate_links(self, links: List[Dict]) -> List[Dict]:
        """Remove duplicate bidirectional links"""
        seen = set()
        unique = []
        
        for link in links:
            # Create a sorted tuple to identify bidirectional duplicates
            pair = tuple(sorted([link['source'], link['target']]))
            
            if pair not in seen:
                seen.add(pair)
                unique.append(link)
        
        return unique
