"""
Network Analyzer for MCP Security Auditor.

Analyzes network endpoints, external connections, and data transmission patterns.
"""

import re
from pathlib import Path
from typing import List, Set, Tuple
from urllib.parse import urlparse

from ..core.config import AuditConfig
from ..core.models import (
    Finding, CodeLocation, Remediation, MCPServerInfo, NetworkEndpoint
)
from .static import BaseAnalyzer


class NetworkAnalyzer(BaseAnalyzer):
    """
    Analyzes network access patterns and endpoints.
    
    Checks for:
    - Unauthorized external endpoints
    - Data exfiltration patterns
    - Insecure protocols (HTTP vs HTTPS)
    - Hardcoded URLs
    - DNS rebinding vulnerabilities
    - SSRF patterns
    """
    
    name = "network"
    
    # Patterns to extract URLs from code
    URL_PATTERNS = [
        r'https?://[^\s\'"<>\)\]]+',
        r'wss?://[^\s\'"<>\)\]]+',
        r'ftp://[^\s\'"<>\)\]]+',
    ]
    
    # Known safe/trusted domains
    DEFAULT_TRUSTED_DOMAINS = {
        'localhost', '127.0.0.1', '::1',
        'api.anthropic.com', 'modelcontextprotocol.io',
        'api.openai.com', 'api.github.com',
        'registry.npmjs.org', 'pypi.org', 'files.pythonhosted.org',
        'cdn.jsdelivr.net', 'cdnjs.cloudflare.com',
    }
    
    # Suspicious domain patterns
    SUSPICIOUS_PATTERNS = [
        (r'ngrok\.io', "Ngrok tunnel - may bypass network controls"),
        (r'localtunnel\.me', "Local tunnel service"),
        (r'webhook\.site', "Webhook testing service - potential data exfil"),
        (r'requestbin\.', "Request capture service - potential data exfil"),
        (r'burpcollaborator', "Burp Collaborator - security testing tool"),
        (r'interact\.sh', "Interact.sh - potential data exfil"),
        (r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', "Hardcoded IP address"),
        (r'pastebin\.com', "Pastebin - potential data exfil"),
        (r'paste\.ee', "Paste service - potential data exfil"),
    ]
    
    async def analyze(self, path: str, server_info: MCPServerInfo) -> List[Finding]:
        """Analyze network access patterns."""
        findings = []
        path = Path(path)
        
        # Extract all URLs from code
        endpoints = await self._extract_endpoints(path, server_info)
        
        # Analyze each endpoint
        findings.extend(self._analyze_endpoints(endpoints, path))
        
        # Check for SSRF patterns
        findings.extend(await self._check_ssrf_patterns(path, server_info))
        
        # Check for data exfiltration patterns
        findings.extend(await self._check_data_exfil(path, server_info))
        
        # Check for insecure protocols
        findings.extend(self._check_insecure_protocols(endpoints))
        
        return findings
    
    async def _extract_endpoints(self, path: Path, server_info: MCPServerInfo) -> List[NetworkEndpoint]:
        """Extract all network endpoints from code."""
        endpoints = []
        seen_urls: Set[str] = set()
        
        # Determine file extensions
        if server_info.language == "python":
            extensions = ["*.py"]
        else:
            extensions = ["*.ts", "*.js", "*.mjs"]
        
        for ext in extensions:
            for file_path in path.rglob(ext):
                if self._should_skip_file(file_path):
                    continue
                
                content = self._read_file_content(file_path)
                if not content:
                    continue
                
                relative_path = str(file_path.relative_to(path))
                lines = content.split('\n')
                
                for pattern in self.URL_PATTERNS:
                    for match in re.finditer(pattern, content):
                        url = match.group(0).rstrip('.,;:\'")')
                        
                        if url in seen_urls:
                            continue
                        seen_urls.add(url)
                        
                        # Find line number
                        pos = match.start()
                        line_num = content[:pos].count('\n') + 1
                        
                        endpoints.append(NetworkEndpoint(
                            url=url,
                            file_path=relative_path,
                            line_number=line_num,
                            is_hardcoded=True
                        ))
        
        return endpoints
    
    def _analyze_endpoints(self, endpoints: List[NetworkEndpoint], base_path: Path) -> List[Finding]:
        """Analyze endpoints for security issues."""
        findings = []
        
        # Combine config allowed domains with defaults
        trusted_domains = self.DEFAULT_TRUSTED_DOMAINS.union(set(self.config.allowed_domains))
        blocked_domains = set(self.config.blocked_domains)
        
        for endpoint in endpoints:
            try:
                parsed = urlparse(endpoint.url)
                domain = parsed.netloc.lower()
                
                # Remove port from domain
                if ':' in domain:
                    domain = domain.split(':')[0]
                
                # Check blocked domains
                if any(blocked in domain for blocked in blocked_domains):
                    findings.append(Finding(
                        title=f"Blocked domain access: {domain}",
                        description=f"Connection to blocked domain '{domain}' detected. "
                                   f"This domain is on the blocked list.",
                        severity="critical",
                        category="network",
                        analyzer=self.name,
                        location=CodeLocation(
                            file=endpoint.file_path,
                            line_start=endpoint.line_number,
                            line_end=endpoint.line_number,
                            snippet=self._get_code_snippet(
                                base_path / endpoint.file_path, 
                                endpoint.line_number
                            )
                        ),
                        remediation=Remediation(
                            description=f"Remove connection to blocked domain '{domain}'."
                        ),
                        confidence="high"
                    ))
                    continue
                
                # Check suspicious patterns
                for pattern, description in self.SUSPICIOUS_PATTERNS:
                    if re.search(pattern, domain, re.IGNORECASE) or re.search(pattern, endpoint.url, re.IGNORECASE):
                        findings.append(Finding(
                            title=f"Suspicious endpoint: {description}",
                            description=f"URL '{endpoint.url}' matches suspicious pattern: {description}",
                            severity="high",
                            category="network",
                            analyzer=self.name,
                            location=CodeLocation(
                                file=endpoint.file_path,
                                line_start=endpoint.line_number,
                                line_end=endpoint.line_number,
                                snippet=self._get_code_snippet(
                                    base_path / endpoint.file_path,
                                    endpoint.line_number
                                )
                            ),
                            remediation=Remediation(
                                description=f"Review this endpoint and ensure it's legitimate: {endpoint.url}"
                            ),
                            confidence="medium"
                        ))
                        break
                
                # Check untrusted domains (not in allowed list)
                domain_base = '.'.join(domain.split('.')[-2:]) if '.' in domain else domain
                
                if domain and domain not in trusted_domains and domain_base not in trusted_domains:
                    # Check if it's a config-type reference
                    if not any(trusted in domain for trusted in trusted_domains):
                        findings.append(Finding(
                            title=f"External endpoint: {domain}",
                            description=f"Connection to external domain '{domain}' that is not in the allowed list. "
                                       f"URL: {endpoint.url}",
                            severity="medium",
                            category="network",
                            analyzer=self.name,
                            location=CodeLocation(
                                file=endpoint.file_path,
                                line_start=endpoint.line_number,
                                line_end=endpoint.line_number,
                                snippet=self._get_code_snippet(
                                    base_path / endpoint.file_path,
                                    endpoint.line_number
                                )
                            ),
                            remediation=Remediation(
                                description=f"Add '{domain}' to allowed_domains in configuration if this is a legitimate endpoint, "
                                           f"or remove the connection if unnecessary."
                            ),
                            confidence="medium"
                        ))
                
            except Exception:
                continue
        
        return findings
    
    async def _check_ssrf_patterns(self, path: Path, server_info: MCPServerInfo) -> List[Finding]:
        """Check for Server-Side Request Forgery (SSRF) patterns."""
        findings = []
        
        # Patterns that might indicate SSRF vulnerabilities
        ssrf_patterns = [
            (r'fetch\s*\(\s*[^"\']+\s*\)', "Dynamic URL in fetch()"),
            (r'requests\.get\s*\(\s*[^"\']+\s*\)', "Dynamic URL in requests.get()"),
            (r'httpx\.get\s*\(\s*[^"\']+\s*\)', "Dynamic URL in httpx.get()"),
            (r'urllib\.request\.urlopen\s*\(\s*[^"\']+\s*\)', "Dynamic URL in urlopen()"),
            (r'axios\.(get|post)\s*\(\s*[^"\']+\s*\)', "Dynamic URL in axios"),
            (r'http\.request\s*\(\s*\{[^}]*url\s*:\s*[^"\']+', "Dynamic URL in http.request"),
        ]
        
        if server_info.language == "python":
            extensions = ["*.py"]
        else:
            extensions = ["*.ts", "*.js", "*.mjs"]
        
        for ext in extensions:
            for file_path in path.rglob(ext):
                if self._should_skip_file(file_path):
                    continue
                
                content = self._read_file_content(file_path)
                if not content:
                    continue
                
                relative_path = str(file_path.relative_to(path))
                lines = content.split('\n')
                
                for pattern, description in ssrf_patterns:
                    for i, line in enumerate(lines, 1):
                        if re.search(pattern, line):
                            # Check if URL comes from user input
                            user_input_indicators = [
                                'params', 'args', 'input', 'request', 'query',
                                'user', 'url', 'uri', 'path', 'target'
                            ]
                            if any(ind in line.lower() for ind in user_input_indicators):
                                findings.append(Finding(
                                    title="Potential SSRF vulnerability",
                                    description=f"{description} with potentially user-controlled URL. "
                                               f"This could allow Server-Side Request Forgery attacks.",
                                    severity="high",
                                    category="network",
                                    analyzer=self.name,
                                    location=CodeLocation(
                                        file=relative_path,
                                        line_start=i,
                                        line_end=i,
                                        snippet=self._get_code_snippet(file_path, i)
                                    ),
                                    remediation=Remediation(
                                        description="Validate and sanitize URLs before making requests. "
                                                   "Use allowlists for permitted domains and protocols. "
                                                   "Block requests to internal IP ranges.",
                                        references=["https://cwe.mitre.org/data/definitions/918.html"]
                                    ),
                                    cwe_id="CWE-918",
                                    confidence="medium"
                                ))
        
        return findings
    
    async def _check_data_exfil(self, path: Path, server_info: MCPServerInfo) -> List[Finding]:
        """Check for potential data exfiltration patterns."""
        findings = []
        
        # Patterns that might indicate data exfiltration
        exfil_patterns = [
            (r'base64\.encode|btoa\(', "Base64 encoding (often used to hide data)"),
            (r'JSON\.stringify.*fetch|post.*JSON\.stringify', "Data serialization before external request"),
            (r'webhook|callback.*http', "Webhook/callback with external URL"),
            (r'upload|send.*file|document', "File upload to external service"),
        ]
        
        if server_info.language == "python":
            extensions = ["*.py"]
        else:
            extensions = ["*.ts", "*.js", "*.mjs"]
        
        for ext in extensions:
            for file_path in path.rglob(ext):
                if self._should_skip_file(file_path):
                    continue
                
                content = self._read_file_content(file_path)
                if not content:
                    continue
                
                relative_path = str(file_path.relative_to(path))
                
                for pattern, description in exfil_patterns:
                    if re.search(pattern, content, re.IGNORECASE):
                        # Find the line
                        lines = content.split('\n')
                        for i, line in enumerate(lines, 1):
                            if re.search(pattern, line, re.IGNORECASE):
                                findings.append(Finding(
                                    title=f"Potential data exfiltration pattern",
                                    description=f"Detected pattern that may indicate data exfiltration: {description}",
                                    severity="medium",
                                    category="network",
                                    analyzer=self.name,
                                    location=CodeLocation(
                                        file=relative_path,
                                        line_start=i,
                                        line_end=i,
                                        snippet=self._get_code_snippet(file_path, i)
                                    ),
                                    remediation=Remediation(
                                        description="Review this code to ensure sensitive data is not being "
                                                   "transmitted to unauthorized endpoints."
                                    ),
                                    confidence="low"
                                ))
                                break
        
        return findings
    
    def _check_insecure_protocols(self, endpoints: List[NetworkEndpoint]) -> List[Finding]:
        """Check for use of insecure protocols."""
        findings = []
        
        for endpoint in endpoints:
            # Check for HTTP (not HTTPS)
            if endpoint.url.startswith('http://'):
                # Skip localhost
                parsed = urlparse(endpoint.url)
                if parsed.netloc.split(':')[0] in ['localhost', '127.0.0.1', '::1']:
                    continue
                
                findings.append(Finding(
                    title="Insecure HTTP connection",
                    description=f"Unencrypted HTTP connection to '{endpoint.url}'. "
                               f"Use HTTPS for secure data transmission.",
                    severity="medium",
                    category="network",
                    analyzer=self.name,
                    location=CodeLocation(
                        file=endpoint.file_path,
                        line_start=endpoint.line_number,
                        line_end=endpoint.line_number
                    ),
                    remediation=Remediation(
                        description="Change HTTP to HTTPS to encrypt data in transit.",
                        code_example=f"# Change: {endpoint.url}\n# To: {endpoint.url.replace('http://', 'https://')}"
                    ),
                    cwe_id="CWE-319",
                    confidence="high"
                ))
            
            # Check for FTP
            elif endpoint.url.startswith('ftp://'):
                findings.append(Finding(
                    title="Insecure FTP connection",
                    description=f"Unencrypted FTP connection detected. Use SFTP or FTPS instead.",
                    severity="medium",
                    category="network",
                    analyzer=self.name,
                    location=CodeLocation(
                        file=endpoint.file_path,
                        line_start=endpoint.line_number,
                        line_end=endpoint.line_number
                    ),
                    remediation=Remediation(
                        description="Use SFTP or FTPS for secure file transfers."
                    ),
                    cwe_id="CWE-319",
                    confidence="high"
                ))
        
        return findings
