"""
Name Searcher Module
Google search functionality for name-based OSINT investigations
"""

import asyncio
import aiohttp
import json
import re
import urllib.parse
from bs4 import BeautifulSoup
from colorama import Fore, Style
import time
import random

class NameSearcher:
    """Enhanced name search with Google scraping capabilities"""
    
    def __init__(self):
        self.session = None
        self.timeout = 30
        self.max_retries = 3
        self.rate_limit_delay = random.uniform(1, 3)
        
        # Search query templates
        self.search_queries = [
            "{name} site:linkedin.com",
            "{name} site:twitter.com",
            "{name} site:facebook.com",
            "{name} site:instagram.com",
            "{name} professional",
            "{name} company",
            "{name} biography",
            "{name} profile",
            "\"{name}\"",
            "{name} -site:facebook.com -site:twitter.com"
        ]
        
        # Social media patterns
        self.social_patterns = {
            'linkedin': {
                'url_pattern': r'linkedin\.com/in/([a-zA-Z0-9-]+)',
                'name_pattern': r'"name":"([^"]+)"'
            },
            'twitter': {
                'url_pattern': r'twitter\.com/([a-zA-Z0-9_]+)',
                'name_pattern': r'"name":"([^"]+)"'
            },
            'facebook': {
                'url_pattern': r'facebook\.com/([a-zA-Z0-9.-]+)',
                'name_pattern': r'"name":"([^"]+)"'
            },
            'instagram': {
                'url_pattern': r'instagram\.com/([a-zA-Z0-9_.]+)',
                'name_pattern': r'"name":"([^"]+)"'
            }
        }
    
    def get_search_url(self, query):
        """Generate Google search URL"""
        encoded_query = urllib.parse.quote(query)
        return f"https://www.google.com/search?q={encoded_query}&num=10&hl=en"
    
    def get_headers(self):
        """Get realistic headers for Google search"""
        return {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.9',
            'Accept-Encoding': 'gzip, deflate, br',
            'DNT': '1',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1',
            'Sec-Fetch-Dest': 'document',
            'Sec-Fetch-Mode': 'navigate',
            'Sec-Fetch-Site': 'none',
            'Sec-Fetch-User': '?1'
        }
    
    async def search_name(self, name, verbose=False):
        """Search for information about a name"""
        if verbose:
            print(f"{Fore.CYAN}[*] Starting comprehensive name search for: {name}{Style.RESET_ALL}")
        
        results = {
            "name": name,
            "google_results": [],
            "social_profiles": [],
            "professional_info": {},
            "additional_info": {},
            "search_timestamp": time.time()
        }
        
        try:
            # Create session
            timeout = aiohttp.ClientTimeout(total=self.timeout)
            async with aiohttp.ClientSession(timeout=timeout) as session:
                
                # Search with different queries
                all_results = []
                social_profiles = {}
                professional_info = {}
                
                for i, query_template in enumerate(self.search_queries):
                    if verbose:
                        print(f"{Fore.YELLOW}[+] Searching with query: {query_template.format(name=name)}{Style.RESET_ALL}")
                    
                    query = query_template.format(name=name)
                    search_url = self.get_search_url(query)
                    
                    try:
                        # Rate limiting
                        await asyncio.sleep(self.rate_limit_delay)
                        
                        async with session.get(search_url, headers=self.get_headers()) as response:
                            if response.status == 200:
                                content = await response.text()
                                soup = BeautifulSoup(content, 'html.parser')
                                
                                # Extract search results
                                search_results = self.extract_search_results(soup)
                                all_results.extend(search_results)
                                
                                # Extract social media profiles
                                profiles = self.extract_social_profiles(soup, name)
                                for platform, profile_data in profiles.items():
                                    if platform not in social_profiles:
                                        social_profiles[platform] = []
                                    social_profiles[platform].extend(profile_data)
                                
                                # Extract professional information
                                prof_info = self.extract_professional_info(soup, name)
                                professional_info.update(prof_info)
                                
                                if verbose:
                                    print(f"{Fore.GREEN}[+] Found {len(search_results)} results for query {i+1}/{len(self.search_queries)}{Style.RESET_ALL}")
                            
                            elif response.status == 429:
                                if verbose:
                                    print(f"{Fore.YELLOW}[!] Rate limited, waiting...{Style.RESET_ALL}")
                                await asyncio.sleep(random.uniform(5, 10))
                                continue
                            else:
                                if verbose:
                                    print(f"{Fore.RED}[-] HTTP {response.status} for query {i+1}{Style.RESET_ALL}")
                    
                    except Exception as e:
                        if verbose:
                            print(f"{Fore.RED}[-] Error in query {i+1}: {str(e)}{Style.RESET_ALL}")
                        continue
                
                # Process and deduplicate results
                results["google_results"] = self.deduplicate_results(all_results)[:10]
                results["social_profiles"] = self.process_social_profiles(social_profiles)
                results["professional_info"] = professional_info
                results["additional_info"] = self.extract_additional_info(all_results, name)
                
                if verbose:
                    print(f"{Fore.GREEN}[+] Name search completed successfully{Style.RESET_ALL}")
                
                return results
                
        except Exception as e:
            error_msg = f"Name search failed: {str(e)}"
            if verbose:
                print(f"{Fore.RED}[-] {error_msg}{Style.RESET_ALL}")
            return {"error": error_msg, "name": name}
    
    def extract_search_results(self, soup):
        """Extract search results from Google page"""
        results = []
        
        # Find search result divs
        search_divs = soup.find_all('div', class_='g')
        
        for div in search_divs:
            try:
                # Extract title and URL
                title_elem = div.find('h3')
                if title_elem:
                    title_link = title_elem.find('a')
                    if title_link:
                        title = title_link.get_text().strip()
                        url = title_link.get('href', '')
                        
                        # Clean Google redirect URLs
                        if url.startswith('/url?'):
                            parsed = urllib.parse.urlparse(url)
                            query_params = urllib.parse.parse_qs(parsed.query)
                            if 'q' in query_params:
                                url = query_params['q'][0]
                        
                        # Extract snippet
                        snippet_elem = div.find('span', class_='aCOpRe')
                        if not snippet_elem:
                            snippet_elem = div.find('div', class_='VwiC3b')
                        snippet = snippet_elem.get_text().strip() if snippet_elem else ""
                        
                        if title and url:
                            results.append({
                                "title": title,
                                "url": url,
                                "snippet": snippet
                            })
            except Exception:
                continue
        
        return results
    
    def extract_social_profiles(self, soup, name):
        """Extract social media profiles from search results"""
        profiles = {}
        
        # Find all links that might be social media
        all_links = soup.find_all('a', href=True)
        
        for link in all_links:
            href = link.get('href', '')
            text = link.get_text().strip()
            
            for platform, patterns in self.social_patterns.items():
                if platform in href.lower():
                    # Extract username from URL
                    import re
                    match = re.search(patterns['url_pattern'], href)
                    if match:
                        username = match.group(1)
                        
                        if platform not in profiles:
                            profiles[platform] = []
                        
                        profile_data = {
                            "username": username,
                            "url": href,
                            "text": text
                        }
                        
                        # Avoid duplicates
                        if not any(p["username"] == username for p in profiles[platform]):
                            profiles[platform].append(profile_data)
        
        return profiles
    
    def extract_professional_info(self, soup, name):
        """Extract professional information"""
        info = {}
        
        # Look for LinkedIn profiles
        linkedin_links = soup.find_all('a', href=re.compile(r'linkedin\.com', re.IGNORECASE))
        for link in linkedin_links:
            href = link.get('href', '')
            if href and 'linkedin.com' in href:
                info["linkedin"] = href
                break
        
        # Look for company information
        text_content = soup.get_text().lower()
        if 'works at' in text_content or 'employed at' in text_content:
            # Try to extract company name
            company_patterns = [
                r'works at ([A-Za-z\s&]+)',
                r'employed at ([A-Za-z\s&]+)',
                r'at ([A-Za-z\s&]+) company'
            ]
            
            for pattern in company_patterns:
                match = re.search(pattern, text_content)
                if match:
                    info["company"] = match.group(1).strip()
                    break
        
        # Look for position/title
        position_patterns = [
            r'(software engineer|developer|manager|director|ceo|cto|founder)',
            r'(senior|lead|principal) ([a-z\s]+)',
        ]
        
        for pattern in position_patterns:
            match = re.search(pattern, text_content)
            if match:
                info["position"] = match.group(0)
                break
        
        return info
    
    def extract_additional_info(self, results, name):
        """Extract additional information from search results"""
        info = {}
        
        # Count result types
        linkedin_count = sum(1 for r in results if 'linkedin.com' in r.get('url', ''))
        twitter_count = sum(1 for r in results if 'twitter.com' in r.get('url', ''))
        facebook_count = sum(1 for r in results if 'facebook.com' in r.get('url', ''))
        
        if linkedin_count > 0:
            info["linkedin_profiles"] = linkedin_count
        if twitter_count > 0:
            info["twitter_profiles"] = twitter_count
        if facebook_count > 0:
            info["facebook_profiles"] = facebook_count
        
        # Extract common keywords
        all_snippets = ' '.join(r.get('snippet', '') for r in results).lower()
        
        keywords = {
            'developer': ['software', 'programming', 'coding', 'engineer'],
            'business': ['company', 'entrepreneur', 'startup', 'business'],
            'academic': ['university', 'research', 'professor', 'student'],
            'creative': ['artist', 'designer', 'photographer', 'writer']
        }
        
        for category, word_list in keywords.items():
            if any(word in all_snippets for word in word_list):
                info[f"{category}_keywords"] = True
        
        return info
    
    def deduplicate_results(self, results):
        """Remove duplicate search results"""
        seen_urls = set()
        unique_results = []
        
        for result in results:
            url = result.get('url', '')
            if url and url not in seen_urls:
                seen_urls.add(url)
                unique_results.append(result)
        
        return unique_results
    
    def process_social_profiles(self, profiles):
        """Process and clean social media profiles"""
        processed = {}
        
        for platform, profile_list in profiles.items():
            # Keep only the most relevant profiles
            if profile_list:
                # Sort by relevance (URLs with the name are more relevant)
                profile_list.sort(key=lambda x: (
                    x.get('username', '').lower() in x.get('url', '').lower(),
                    len(x.get('username', ''))
                ), reverse=True)
                
                processed[platform] = profile_list[:3]  # Keep top 3
        
        return processed
