import os
import time
import random
from typing import Any, Dict, List, Optional
from datetime import datetime
from pathlib import Path
import instaloader
from src.base_scraper import BaseScraper, ScraperConfig


class InstagramScraper(BaseScraper):
    """Instagram scraper using instaloader with aggressive rate limiting."""
    
    def __init__(self, config: ScraperConfig):
        super().__init__(config)
        self.username = os.getenv('INSTAGRAM_USERNAME')
        self.password = os.getenv('INSTAGRAM_PASSWORD')
        self.target_account = os.getenv('INSTAGRAM_TARGET', 'hkia')
        
        # Session file for persistence (needs .session extension)
        self.session_file = self.config.data_dir / '.sessions' / f'{self.username}.session'
        self.session_file.parent.mkdir(parents=True, exist_ok=True)
        
        # Initialize loader
        self.loader = self._setup_loader()
        self._login()
        
        # Request counter for rate limiting
        self.request_count = 0
        self.max_requests_per_hour = 200  # Updated to 200 requests per hour for faster fetching

    def _setup_loader(self) -> instaloader.Instaloader:
        """Setup Instaloader with conservative settings."""
        loader = instaloader.Instaloader(
            quiet=True,
            user_agent='Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
            dirname_pattern=str(self.config.data_dir / 'media' / 'Instagram'),
            filename_pattern='{date_utc}_UTC_{shortcode}',
            download_pictures=False,  # Don't download by default
            download_videos=False,
            download_video_thumbnails=False,
            download_geotags=False,
            download_comments=False,
            save_metadata=False,
            compress_json=False,
            post_metadata_txt_pattern='',
            storyitem_metadata_txt_pattern='',
            max_connection_attempts=3,
            request_timeout=30.0
            # Removed rate_controller - it was causing context issues
        )
        return loader

    def _login(self) -> None:
        """Login to Instagram or load existing session."""
        try:
            # Try to load existing session
            if self.session_file.exists():
                # Fixed: username comes first, then filename
                self.loader.load_session_from_file(self.username, str(self.session_file))
                self.logger.info("Loaded existing Instagram session")
                
                # Verify context is loaded
                if not self.loader.context:
                    self.logger.warning("Session loaded but context is None, re-logging in")
                    self.session_file.unlink()  # Remove bad session
                    self.loader.login(self.username, self.password)
                    self.loader.save_session_to_file(str(self.session_file))
            else:
                # Login with credentials
                self.logger.info("Logging in to Instagram...")
                self.loader.login(self.username, self.password)
                self.loader.save_session_to_file(str(self.session_file))
                self.logger.info("Instagram login successful, session saved")
                
        except Exception as e:
            self.logger.error(f"Instagram login error: {e}")
            # Try to ensure we have a context even if login fails
            if not hasattr(self.loader, 'context') or self.loader.context is None:
                # Create a new loader instance which should have context
                self.loader = instaloader.Instaloader()

    def _aggressive_delay(self, min_seconds: float = 10, max_seconds: float = 20) -> None:
        """Add aggressive random delay for Instagram."""
        delay = random.uniform(min_seconds, max_seconds)
        self.logger.debug(f"Waiting {delay:.2f} seconds (Instagram rate limiting)...")
        time.sleep(delay)

    def _check_rate_limit(self) -> None:
        """Check and enforce rate limiting."""
        self.request_count += 1
        
        if self.request_count >= self.max_requests_per_hour:
            self.logger.warning(f"Rate limit reached ({self.max_requests_per_hour} requests), pausing for 1 hour...")
            time.sleep(3600)  # Wait 1 hour
            self.request_count = 0
        elif self.request_count % 10 == 0:
            # Take a longer break every 10 requests  
            self.logger.info("Taking extended break after 10 requests...")
            self._aggressive_delay(30, 60)  # 30-60 second break

    def _get_post_type(self, post) -> str:
        """Determine post type from Instagram post object."""
        typename = getattr(post, 'typename', '')
        is_video = getattr(post, 'is_video', False)
        
        if typename == 'GraphStoryImage' or typename == 'GraphStoryVideo':
            return 'story'
        elif 'Video' in typename or is_video:
            return 'reel'
        else:
            return 'post'

    def fetch_posts(self, max_posts: int = 20) -> List[Dict[str, Any]]:
        """Fetch posts from Instagram profile."""
        posts_data = []
        
        try:
            # Ensure we have a valid context
            if not self.loader.context:
                self.logger.warning("Instagram context not initialized, attempting re-login")
                self._login()
                
            if not self.loader.context:
                self.logger.error("Failed to initialize Instagram context")
                return posts_data
            
            self.logger.info(f"Fetching posts from @{self.target_account}")
            
            # Get profile
            profile = instaloader.Profile.from_username(self.loader.context, self.target_account)
            self._check_rate_limit()
            
            # Get posts
            posts = profile.get_posts()
            
            count = 0
            for post in posts:
                if count >= max_posts:
                    break
                
                try:
                    # Extract post data
                    post_data = {
                        'id': post.shortcode,
                        'type': self._get_post_type(post),
                        'caption': post.caption if post.caption else '',
                        'author': post.owner_username,
                        'publish_date': post.date_utc.isoformat(),
                        'link': f'https://www.instagram.com/p/{post.shortcode}/',
                        'likes': post.likes,
                        'comments': post.comments,
                        'views': post.video_view_count if hasattr(post, 'video_view_count') else None,
                        'media_count': post.mediacount if hasattr(post, 'mediacount') else 1,
                        'hashtags': list(post.caption_hashtags) if post.caption else [],
                        'mentions': list(post.caption_mentions) if post.caption else [],
                        'is_video': getattr(post, 'is_video', False)
                    }
                    
                    posts_data.append(post_data)
                    count += 1
                    
                    # Aggressive rate limiting between posts
                    self._aggressive_delay()
                    self._check_rate_limit()
                    
                    # Log progress
                    if count % 5 == 0:
                        self.logger.info(f"Fetched {count}/{max_posts} posts")
                    
                except Exception as e:
                    self.logger.error(f"Error processing post: {e}")
                    continue
            
            self.logger.info(f"Successfully fetched {len(posts_data)} posts")
            
        except Exception as e:
            self.logger.error(f"Error fetching posts: {e}")
        
        return posts_data

    def fetch_stories(self) -> List[Dict[str, Any]]:
        """Fetch stories from Instagram profile."""
        stories_data = []
        
        try:
            # Ensure we have a valid context
            if not self.loader.context:
                self.logger.warning("Instagram context not initialized, attempting re-login")
                self._login()
                
            if not self.loader.context:
                self.logger.error("Failed to initialize Instagram context")
                return stories_data
            
            self.logger.info(f"Fetching stories from @{self.target_account}")
            
            # Get profile
            profile = instaloader.Profile.from_username(self.loader.context, self.target_account)
            self._check_rate_limit()
            
            # Get user ID for stories
            userid = profile.userid
            
            # Get stories
            for story in self.loader.get_stories(userids=[userid]):
                for item in story:
                    try:
                        story_data = {
                            'id': item.mediaid,
                            'type': 'story',
                            'caption': '',  # Stories usually don't have captions
                            'author': item.owner_username,
                            'publish_date': item.date_utc.isoformat(),
                            'link': f'https://www.instagram.com/stories/{item.owner_username}/{item.mediaid}/',
                            'is_video': item.is_video if hasattr(item, 'is_video') else False
                        }
                        
                        stories_data.append(story_data)
                        
                        # Rate limiting
                        self._aggressive_delay()
                        self._check_rate_limit()
                        
                    except Exception as e:
                        self.logger.error(f"Error processing story: {e}")
                        continue
            
            self.logger.info(f"Successfully fetched {len(stories_data)} stories")
            
        except Exception as e:
            self.logger.error(f"Error fetching stories: {e}")
        
        return stories_data

    def fetch_reels(self, max_reels: int = 10) -> List[Dict[str, Any]]:
        """Fetch reels (videos) from Instagram profile."""
        reels_data = []
        
        try:
            self.logger.info(f"Fetching reels from @{self.target_account}")
            
            # Get profile
            profile = instaloader.Profile.from_username(self.loader.context, self.target_account)
            self._check_rate_limit()
            
            # Get posts and filter for videos/reels
            posts = profile.get_posts()
            
            count = 0
            for post in posts:
                if count >= max_reels:
                    break
                
                # Check if it's a video/reel
                if not getattr(post, 'is_video', False):
                    continue
                
                try:
                    reel_data = {
                        'id': post.shortcode,
                        'type': 'reel',
                        'caption': post.caption if post.caption else '',
                        'author': post.owner_username,
                        'publish_date': post.date_utc.isoformat(),
                        'link': f'https://www.instagram.com/reel/{post.shortcode}/',
                        'likes': post.likes,
                        'comments': post.comments,
                        'views': post.video_view_count if hasattr(post, 'video_view_count') else None,
                        'duration': post.video_duration if hasattr(post, 'video_duration') else None,
                        'hashtags': list(post.caption_hashtags) if post.caption else [],
                        'mentions': list(post.caption_mentions) if post.caption else []
                    }
                    
                    reels_data.append(reel_data)
                    count += 1
                    
                    # Aggressive rate limiting
                    self._aggressive_delay()
                    self._check_rate_limit()
                    
                except Exception as e:
                    self.logger.error(f"Error processing reel: {e}")
                    continue
            
            self.logger.info(f"Successfully fetched {len(reels_data)} reels")
            
        except Exception as e:
            self.logger.error(f"Error fetching reels: {e}")
        
        return reels_data

    def fetch_content(self, max_posts: int = 20) -> List[Dict[str, Any]]:
        """Fetch all content types from Instagram."""
        all_content = []
        
        # Fetch posts
        posts = self.fetch_posts(max_posts=max_posts)
        all_content.extend(posts)
        
        # Take a break between content types
        self.logger.info("Taking break before fetching stories...")
        self._aggressive_delay(15, 30)
        
        # Fetch stories
        stories = self.fetch_stories()
        all_content.extend(stories)
        
        # Note: Reels are included in posts (videos)
        # so we don't need to fetch them separately
        
        self.logger.info(f"Total content fetched: {len(all_content)} items")
        return all_content

    def format_markdown(self, items: List[Dict[str, Any]]) -> str:
        """Format Instagram content as markdown."""
        markdown_sections = []
        
        for item in items:
            section = []
            
            # ID
            item_id = item.get('id', 'N/A')
            section.append(f"# ID: {item_id}")
            section.append("")
            
            # Type
            item_type = item.get('type', 'post')
            section.append(f"## Type: {item_type}")
            section.append("")
            
            # Author
            author = item.get('author', 'Unknown')
            section.append(f"## Author: {author}")
            section.append("")
            
            # Publish Date
            pub_date = item.get('publish_date', '')
            section.append(f"## Publish Date: {pub_date}")
            section.append("")
            
            # Link
            link = item.get('link', '')
            section.append(f"## Link: {link}")
            section.append("")
            
            # Engagement metrics
            likes = item.get('likes')
            if likes is not None:
                section.append(f"## Likes: {likes}")
                section.append("")
            
            comments = item.get('comments')
            if comments is not None:
                section.append(f"## Comments: {comments}")
                section.append("")
            
            views = item.get('views')
            if views is not None:
                section.append(f"## Views: {views}")
                section.append("")
            
            # Hashtags
            hashtags = item.get('hashtags', [])
            if hashtags:
                hashtags_str = ', '.join(hashtags)
                section.append(f"## Hashtags: {hashtags_str}")
                section.append("")
            
            # Mentions
            mentions = item.get('mentions', [])
            if mentions:
                mentions_str = ', '.join(mentions)
                section.append(f"## Mentions: {mentions_str}")
                section.append("")
            
            # Caption/Description
            section.append("## Description:")
            caption = item.get('caption', '')
            if caption:
                # Limit caption to first 500 characters
                if len(caption) > 500:
                    caption = caption[:500] + "..."
                section.append(caption)
            section.append("")
            
            # Separator
            section.append("-" * 50)
            section.append("")
            
            markdown_sections.append('\n'.join(section))
        
        return '\n'.join(markdown_sections)

    def get_incremental_items(self, items: List[Dict[str, Any]], state: Dict[str, Any]) -> List[Dict[str, Any]]:
        """Get only new posts since last sync."""
        if not state:
            return items
        
        last_post_id = state.get('last_post_id')
        
        if not last_post_id:
            return items
        
        # Filter for posts newer than the last synced
        new_items = []
        for item in items:
            if item.get('id') == last_post_id:
                break  # Found the last synced post
            new_items.append(item)
        
        return new_items

    def update_state(self, state: Dict[str, Any], items: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Update state with latest post information."""
        if not items:
            return state
        
        # Get the first item (most recent)
        latest_item = items[0]
        
        state['last_post_id'] = latest_item.get('id')
        state['last_post_date'] = latest_item.get('publish_date')
        state['last_sync'] = datetime.now(self.tz).isoformat()
        state['post_count'] = len([i for i in items if i.get('type') == 'post'])
        state['story_count'] = len([i for i in items if i.get('type') == 'story'])
        state['reel_count'] = len([i for i in items if i.get('type') == 'reel'])
        
        return state