import requests
import time
import csv
import json
import re
from datetime import datetime
import logging
import os
from bs4 import BeautifulSoup
import random
from urllib.parse import quote

# Try to import selenium for browser automation fallback
try:
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.chrome.options import Options
    SELENIUM_AVAILABLE = True
except ImportError:
    SELENIUM_AVAILABLE = False

class InstagramLikesScraper:
    def __init__(self, reel_url: str, output_file: str = 'instagram_likes_data.csv'):
        """
        Initialize Instagram Likes Scraper
        
        Args:
            reel_url: URL of the Instagram reel to track
            output_file: CSV file to store the data
        """
        self.reel_url = reel_url
        self.output_file = output_file
        
        # User agents to rotate
        self.user_agents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:89.0) Gecko/20100101 Firefox/89.0'
        ]
        
        # Setup logging
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler('instagram_scraper.log'),
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger(__name__)
        
        # Initialize CSV file
        self._initialize_csv()
        
        # Session for maintaining cookies
        self.session = requests.Session()
    
    def _initialize_csv(self):
        """Initialize CSV file with headers if it doesn't exist"""
        if not os.path.exists(self.output_file):
            with open(self.output_file, 'w', newline='', encoding='utf-8') as file:
                writer = csv.writer(file)
                writer.writerow([
                    'timestamp', 
                    'datetime', 
                    'likes_count', 
                    'comments_count',
                    'views_count',
                    'status'
                ])
            self.logger.info(f"Created new CSV file: {self.output_file}")
    
    def _get_random_headers(self):
        """Get random headers to avoid detection"""
        return {
            'User-Agent': random.choice(self.user_agents),
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.5',
            'Accept-Encoding': 'gzip, deflate',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1',
        }
    
    def _extract_json_data(self, html_content):
        """Extract JSON data from Instagram page"""
        try:
            # Look for shared data in script tags
            pattern = r'window\._sharedData\s*=\s*({.*?});'
            match = re.search(pattern, html_content)
            if match:
                return json.loads(match.group(1))
            
            # Alternative pattern for newer Instagram versions
            pattern = r'window\.__additionalDataLoaded\(\'[^\']+\',\s*({.*?})\);'
            matches = re.findall(pattern, html_content)
            if matches:
                return json.loads(matches[0])
                
            return None
        except Exception as e:
            self.logger.error(f"Error extracting JSON data: {e}")
            return None
    
    def _extract_likes_from_html(self, html_content):
        """Extract likes count from HTML using multiple methods"""
        try:
            soup = BeautifulSoup(html_content, 'html.parser')
            
            # Method 1: Target the exact span with Instagram likes classes
            likes_classes = [
                'xdj266r', 'x14z9mp', 'xat24cr', 'x1lziwak', 'xexx8yu', 
                'xyri2b', 'x18d9i69', 'x1c1uobl', 'x1hl2dhg', 'x16tdsg8', 
                'x1vvkbs'
            ]
            
            # Look for span with these specific classes
            for span in soup.find_all('span', class_=likes_classes):
                text = span.get_text().strip()
                if re.match(r'^\d+(?:,\d+)*$', text):  # Pure number format like "13,572"
                    likes_count = int(text.replace(',', ''))
                    self.logger.info(f"Found likes via exact classes: {likes_count}")
                    return likes_count
            
            # Method 2: Look for spans with any combination of these classes
            for span in soup.find_all('span'):
                span_classes = span.get('class', [])
                # Check if span has at least 3 of our target classes
                matching_classes = len([c for c in likes_classes if c in span_classes])
                if matching_classes >= 3:
                    text = span.get_text().strip()
                    if re.match(r'^\d+(?:,\d+)*$', text):
                        likes_count = int(text.replace(',', ''))
                        self.logger.info(f"Found likes via partial class match ({matching_classes} classes): {likes_count}")
                        return likes_count
            
            # Method 3: Look for any span with comma-separated numbers (Instagram format)
            for span in soup.find_all('span'):
                text = span.get_text().strip()
                if re.match(r'^\d{1,3}(?:,\d{3})*$', text):  # Format like 13,572 or 1,234,567
                    likes_count = int(text.replace(',', ''))
                    # Only consider it if it's a reasonable likes count (not too small, not too big)
                    if 1 <= likes_count <= 100000000:  # Between 1 and 100M
                        self.logger.info(f"Found potential likes via number format: {likes_count}")
                        return likes_count
            
            # Method 4: Use the original long CSS selector
            css_selector = "#mount_0_0_pK > div > div > div.x9f619.x1n2onr6.x1ja2u2z > div > div > div.x78zum5.xdt5ytf.x1t2pt76.x1n2onr6.x1ja2u2z.x10cihs4 > div.x9f619.xvbhtw8.x78zum5.x15mokao.x1ga7v0g.x16uus16.xbiv7yw.x1uhb9sk.x1plvlek.xryxfnj.x1c4vz4f.x2lah0s.xdt5ytf.xqjyukv.x1qjc9v5.x1oa3qoh.x1qughib > div.xvc5jky.xh8yej3.x10o80wk.x14k21rp.x17snn68.x6osk4m.x1porb0y.x8vgawa > section > main > div > article > div:nth-child(3) > div > div:nth-child(1) > section.x1e56ztr.x1xmf6yo > div > div > span > a > span > span"
            
            likes_element = soup.select_one(css_selector)
            if likes_element:
                likes_text = likes_element.get_text().strip()
                self.logger.info(f"Found element via CSS selector: '{likes_text}'")
                
                likes_match = re.search(r'(\d+(?:,\d+)*)', likes_text)
                if likes_match:
                    likes_count = int(likes_match.group(1).replace(',', ''))
                    self.logger.info(f"Extracted likes from CSS selector: {likes_count}")
                    return likes_count
            
            # Method 5: Search for likes in context (near "like" text)
            for element in soup.find_all(['button', 'a', 'div']):
                text = element.get_text().lower()
                aria_label = element.get('aria-label', '').lower()
                
                if 'like' in text or 'like' in aria_label:
                    # Look for numbers in this element or nearby elements
                    for span in element.find_all('span'):
                        span_text = span.get_text().strip()
                        if re.match(r'^\d+(?:,\d+)*$', span_text):
                            likes_count = int(span_text.replace(',', ''))
                            self.logger.info(f"Found likes near 'like' context: {likes_count}")
                            return likes_count
            
            # Method 6: Debug - print all spans with numbers
            self.logger.info("DEBUG: Looking for all spans with number patterns...")
            for span in soup.find_all('span'):
                text = span.get_text().strip()
                classes = span.get('class', [])
                if re.search(r'\d+', text) and len(text) < 20:
                    self.logger.info(f"DEBUG Span: text='{text}', classes={classes[:5]}...")  # Show first 5 classes
            
            self.logger.warning("No likes count found with any method")
            return 0
            
        except Exception as e:
            self.logger.error(f"Error extracting likes from HTML: {e}")
            return 0
    
    def scrape_with_selenium(self):
        """Fallback method using Selenium for JavaScript-heavy content"""
        if not SELENIUM_AVAILABLE:
            self.logger.error("Selenium not available. Install with: pip install selenium")
            return None
        
        try:
            # Setup Chrome options
            chrome_options = Options()
            chrome_options.add_argument('--headless')  # Run in background
            chrome_options.add_argument('--no-sandbox')
            chrome_options.add_argument('--disable-dev-shm-usage')
            chrome_options.add_argument('--disable-gpu')
            chrome_options.add_argument('--window-size=1920,1080')
            chrome_options.add_argument(f'--user-agent={random.choice(self.user_agents)}')
            
            # Initialize driver
            driver = webdriver.Chrome(options=chrome_options)
            driver.get(self.reel_url)
            
            # Wait for page to load
            time.sleep(5)
            
            # Try multiple selectors for likes
            likes_selectors = [
                'button span span',  # Common likes button
                '[aria-label*="like"] span',
                'span[class*="_ac2a"]',
                'span:contains("like")',
                'button[aria-label*="like"]',
            ]
            
            likes_count = 0
            for selector in likes_selectors:
                try:
                    if 'contains' in selector:
                        # Use XPath for contains
                        xpath = f"//span[contains(text(), 'like')]"
                        elements = driver.find_elements(By.XPATH, xpath)
                    else:
                        elements = driver.find_elements(By.CSS_SELECTOR, selector)
                    
                    for element in elements:
                        text = element.text.strip()
                        aria_label = element.get_attribute('aria-label') or ''
                        
                        # Look for numbers in text or aria-label
                        for content in [text, aria_label]:
                            if content and 'like' in content.lower():
                                match = re.search(r'(\d+(?:,\d+)*)', content)
                                if match:
                                    likes_count = int(match.group(1).replace(',', ''))
                                    self.logger.info(f"Selenium found likes: {likes_count}")
                                    driver.quit()
                                    return likes_count
                
                except Exception as e:
                    continue
            
            # If no likes found, get page source for debugging
            page_source = driver.page_source
            with open('selenium_debug.html', 'w', encoding='utf-8') as f:
                f.write(page_source)
            self.logger.info("Selenium page source saved to selenium_debug.html")
            
            driver.quit()
            return 0
            
        except Exception as e:
            self.logger.error(f"Selenium scraping failed: {e}")
            return None
    
    def scrape_reel_data(self):
        """Scrape reel data from Instagram"""
        try:
            headers = self._get_random_headers()
            
            # Add random delay to avoid rate limiting
            time.sleep(random.uniform(2, 5))
            
            response = self.session.get(self.reel_url, headers=headers, timeout=30)
            response.raise_for_status()
            
            html_content = response.text
            self.logger.info(f"Page loaded successfully, content length: {len(html_content)}")
            
            # Save HTML content for debugging (optional)
            # with open('debug_page.html', 'w', encoding='utf-8') as f:
            #     f.write(html_content)
            
            # Extract JSON data
            json_data = self._extract_json_data(html_content)
            
            likes_count = 0
            comments_count = 0
            views_count = 0
            
            if json_data:
                try:
                    # Navigate through JSON structure to find post data
                    entry_data = json_data.get('entry_data', {})
                    post_page = entry_data.get('PostPage', [])
                    
                    if post_page and len(post_page) > 0:
                        media = post_page[0].get('graphql', {}).get('shortcode_media', {})
                        
                        likes_count = media.get('edge_media_preview_like', {}).get('count', 0)
                        comments_count = media.get('edge_media_to_comment', {}).get('count', 0)
                        views_count = media.get('video_view_count', 0)
                        
                        self.logger.info(f"Extracted from JSON - Likes: {likes_count}, Comments: {comments_count}")
                        
                except Exception as e:
                    self.logger.warning(f"JSON parsing failed, falling back to HTML parsing: {e}")
            
            # Always try HTML parsing (prioritize it)
            html_likes = self._extract_likes_from_html(html_content)
            if html_likes > 0:
                likes_count = html_likes
                self.logger.info(f"Using HTML extracted likes: {likes_count}")
            elif likes_count == 0:
                self.logger.warning("No likes found in HTML, trying Selenium...")
                # Try Selenium as fallback
                selenium_likes = self.scrape_with_selenium()
                if selenium_likes and selenium_likes > 0:
                    likes_count = selenium_likes
                    self.logger.info(f"Using Selenium extracted likes: {likes_count}")
                else:
                    self.logger.warning("No likes found in either HTML or Selenium")
            
            return {
                'likes_count': likes_count,
                'comments_count': comments_count,
                'views_count': views_count,
                'status': 'success'
            }
            
        except requests.exceptions.RequestException as e:
            self.logger.error(f"Request failed: {e}")
            return {
                'likes_count': 0,
                'comments_count': 0,
                'views_count': 0,
                'status': f'request_error: {str(e)}'
            }
        except Exception as e:
            self.logger.error(f"Scraping failed: {e}")
            return {
                'likes_count': 0,
                'comments_count': 0,
                'views_count': 0,
                'status': f'error: {str(e)}'
            }
    
    def save_data_point(self, data):
        """Save a data point to CSV file"""
        timestamp = time.time()
        datetime_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        row_data = [
            timestamp,
            datetime_str,
            data['likes_count'],
            data['comments_count'],
            data['views_count'],
            data['status']
        ]
        
        try:
            with open(self.output_file, 'a', newline='', encoding='utf-8') as file:
                writer = csv.writer(file)
                writer.writerow(row_data)
            
            self.logger.info(f"Data saved: {datetime_str} - Likes: {data['likes_count']}")
            
        except Exception as e:
            self.logger.error(f"Failed to save data: {e}")
    
    def track_likes(self, duration_hours=24, interval_minutes=5):
        """
        Track likes for specified duration
        
        Args:
            duration_hours: How long to track (in hours)
            interval_minutes: Interval between checks (in minutes)
        """
        interval_seconds = interval_minutes * 60
        total_iterations = (duration_hours * 3600) // interval_seconds
        
        self.logger.info(f"Starting tracking for {duration_hours} hours with {interval_minutes}-minute intervals")
        self.logger.info(f"Total iterations planned: {total_iterations}")
        
        for i in range(total_iterations):
            try:
                self.logger.info(f"Iteration {i+1}/{total_iterations}")
                
                # Scrape data
                data = self.scrape_reel_data()
                
                # Save data
                self.save_data_point(data)
                
                # Wait for next iteration (except for the last one)
                if i < total_iterations - 1:
                    self.logger.info(f"Waiting {interval_minutes} minutes until next check...")
                    time.sleep(interval_seconds)
                
            except KeyboardInterrupt:
                self.logger.info("Tracking stopped by user")
                break
            except Exception as e:
                self.logger.error(f"Error in iteration {i+1}: {e}")
                # Continue with next iteration after a short delay
                time.sleep(30)
        
        self.logger.info("Tracking completed!")

# Usage example
if __name__ == "__main__":
    # Replace with your Instagram reel URL
    REEL_URL = "https://www.instagram.com/reel/DK_-Sc5igRh/?igsh=cWY5cTN0cjNwaW4="
    
    # Create tracker instance
    tracker = InstagramLikesScraper(REEL_URL)
    
    # Start tracking for 24 hours, checking every 5 minutes
    tracker.track_likes(duration_hours=24, interval_minutes=5)
    
    print(f"Data saved to: {tracker.output_file}")      in this appriach i want to run tjjis through a webapge where we enetr the link and the strat capturing and we can see the cpatuerd results after presing continuosly for each time dealy and selected time period like 24 hours 5 min delay and then a export likes option button