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 tjhis we need a interfcae where we can ingest the link nd then press strat and set the time of capturing like for 24 hours or 48 hoours with a time gap of 5 min 15 min 30 min 60 min