"""Instagram site preset with API fallback and meta tag extraction."""
import time
import random
import re
import json
import logging

logger = logging.getLogger("iploop.sites.instagram")


class Instagram:
    RATE_LIMIT = 5
    _last_request = 0

    def __init__(self, client):
        self.client = client

    def _rate_limit(self):
        elapsed = time.time() - Instagram._last_request
        if elapsed < self.RATE_LIMIT:
            time.sleep(self.RATE_LIMIT - elapsed + random.uniform(0, 2))
        Instagram._last_request = time.time()

    def _extract_text(self, html: str) -> str:
        """Strip HTML tags and return clean text."""
        # Remove HTML tags
        text = re.sub(r'<[^>]+>', '', html)
        # Clean up whitespace
        text = re.sub(r'\s+', ' ', text).strip()
        return text

    def _validate_instagram_profile(self, html: str) -> bool:
        """Check if we got real profile data (not login wall)."""
        if not html:
            return False
        
        # Check for login wall
        login_indicators = [
            'loginForm',
            'Log In • Instagram',
            'data-testid="royal_login_form"',
            'window._sharedData = {};',  # Empty shared data indicates login wall
            '"require_login":true'
        ]
        
        if any(indicator in html for indicator in login_indicators):
            return False
        
        # Check for actual profile content
        profile_indicators = [
            '"ProfilePage"',
            '"user":{',
            'window._sharedData',
            'profilePage_',
            '"edge_followed_by"',
            '"edge_follow"'
        ]
        
        return any(indicator in html for indicator in profile_indicators)

    def _extract_from_meta_tags(self, html: str, username: str) -> dict:
        """Extract profile data from meta tags (limited but available without login)."""
        data = {"username": username}
        
        # Extract from og:description meta tag
        og_desc_match = re.search(r'<meta property="og:description" content="([^"]*)"', html)
        if og_desc_match:
            desc = og_desc_match.group(1)
            
            # Parse follower count using the required regex pattern
            followers_match = re.search(r'content="([\d,.]+[MKB]?)\s+Followers', html, re.IGNORECASE)
            if followers_match:
                data['followers'] = followers_match.group(1)
            
            # Also try the description content itself
            desc_followers_match = re.search(r'([\d,.]+[MKB]?)\s+Followers', desc, re.IGNORECASE)
            if desc_followers_match:
                data['followers'] = desc_followers_match.group(1)
            
            following_match = re.search(r'([\d,.]+[MKB]?)\s+Following', desc, re.IGNORECASE)
            if following_match:
                data['following'] = following_match.group(1)
            
            posts_match = re.search(r'([\d,.]+[MKB]?)\s+Posts', desc, re.IGNORECASE)
            if posts_match:
                data['posts'] = posts_match.group(1)
            
            # Extract bio text after the stats
            bio_match = re.search(r'Posts\s*[-•]\s*(.+)', desc)
            if bio_match:
                data['bio'] = bio_match.group(1).strip()
        
        # Also check regular description meta tag
        desc_match = re.search(r'<meta name="description" content="([^"]*)"', html)
        if desc_match and not og_desc_match:
            desc = desc_match.group(1)
            followers_match = re.search(r'([\d,.]+[MKB]?)\s+Followers', desc, re.IGNORECASE)
            if followers_match:
                data['followers'] = followers_match.group(1)
        
        # Extract from og:title
        title_match = re.search(r'<meta property="og:title" content="([^"]*)"', html)
        if title_match:
            title = title_match.group(1)
            # Extract display name (everything before " (@username)")
            display_name_match = re.search(r'^([^(]+)', title)
            if display_name_match:
                data['display_name'] = display_name_match.group(1).strip()
        
        # Extract from og:image (profile picture)
        image_match = re.search(r'<meta property="og:image" content="([^"]*)"', html)
        if image_match:
            data['profile_pic_url'] = image_match.group(1)
        
        return data

    def _extract_from_shared_data(self, html: str, username: str) -> dict:
        """Extract profile data from window._sharedData (full data if not blocked)."""
        data = {"username": username}
        
        # Find the _sharedData script
        shared_data_match = re.search(r'window\._sharedData\s*=\s*({.+?});', html)
        if not shared_data_match:
            return data
        
        try:
            shared_data = json.loads(shared_data_match.group(1))
            
            # Navigate to user data
            entry_data = shared_data.get('entry_data', {})
            profile_page = entry_data.get('ProfilePage', [])
            
            if not profile_page:
                return data
            
            user_data = profile_page[0].get('graphql', {}).get('user', {})
            
            if user_data:
                data.update({
                    'id': user_data.get('id'),
                    'display_name': user_data.get('full_name'),
                    'bio': user_data.get('biography'),
                    'followers': user_data.get('edge_followed_by', {}).get('count'),
                    'following': user_data.get('edge_follow', {}).get('count'),
                    'posts': user_data.get('edge_owner_to_timeline_media', {}).get('count'),
                    'profile_pic_url': user_data.get('profile_pic_url_hd'),
                    'is_verified': user_data.get('is_verified'),
                    'is_private': user_data.get('is_private'),
                    'external_url': user_data.get('external_url')
                })
        
        except (json.JSONDecodeError, KeyError) as e:
            logger.debug(f"Failed to parse Instagram shared data: {e}")
        
        return data

    def _try_api_endpoint(self, username: str, country: str = "US") -> dict:
        """Try Instagram's ?__a=1 API endpoint."""
        self._rate_limit()
        
        url = f"https://www.instagram.com/{username}/?__a=1&__d=dis"
        
        # Mobile-like headers for API endpoint
        headers = {
            'User-Agent': 'Instagram 219.0.0.12.117 Android',
            'Accept': 'application/json',
            'Accept-Language': 'en-US,en;q=0.9',
            'X-Requested-With': 'XMLHttpRequest'
        }
        
        try:
            resp = self.client.fetch(url, country=country, headers=headers)
            
            if resp.status_code == 200:
                try:
                    data = json.loads(resp.text)
                    user_data = data.get('graphql', {}).get('user', {})
                    
                    if user_data:
                        return {
                            'username': username,
                            'status': 200,
                            'source': 'api',
                            'data': {
                                'id': user_data.get('id'),
                                'display_name': user_data.get('full_name'),
                                'bio': user_data.get('biography'),
                                'followers': user_data.get('edge_followed_by', {}).get('count'),
                                'following': user_data.get('edge_follow', {}).get('count'),
                                'posts': user_data.get('edge_owner_to_timeline_media', {}).get('count'),
                                'profile_pic_url': user_data.get('profile_pic_url_hd'),
                                'is_verified': user_data.get('is_verified'),
                                'is_private': user_data.get('is_private'),
                                'external_url': user_data.get('external_url')
                            }
                        }
                except json.JSONDecodeError:
                    logger.debug("Instagram API returned invalid JSON")
        
        except Exception as e:
            logger.debug(f"Instagram API endpoint failed: {e}")
        
        return None

    def profile(self, username, country="US", extract=True):
        """
        Fetch Instagram profile with API fallback and meta tag extraction.
        Instagram has a login wall, so we try multiple approaches.
        """
        # First try the API endpoint
        api_result = self._try_api_endpoint(username, country)
        if api_result:
            return api_result
        
        # Fallback to regular profile page
        self._rate_limit()
        url = f"https://www.instagram.com/{username}/"
        
        from ..fingerprint import chrome_fingerprint
        resp = self.client.fetch(url, country=country, headers=chrome_fingerprint("US"))
        
        result = {
            "username": username,
            "url": url,
            "status": resp.status_code,
            "html": resp.text,
            "size_kb": len(resp.text) // 1024,
            "source": "http"
        }
        
        if resp.status_code == 200 and extract:
            # Try to extract from shared data first (more complete)
            if self._validate_instagram_profile(resp.text):
                result["data"] = self._extract_from_shared_data(resp.text, username)
            else:
                # Fall back to meta tags (limited but available)
                result["data"] = self._extract_from_meta_tags(resp.text, username)
                result["limited"] = True  # Indicate this is limited data due to login wall
        
        return result

    def hashtag(self, hashtag, country="US"):
        """Fetch Instagram hashtag page."""
        self._rate_limit()
        url = f"https://www.instagram.com/explore/tags/{hashtag}/"
        
        from ..fingerprint import chrome_fingerprint
        resp = self.client.fetch(url, country=country, headers=chrome_fingerprint("US"))
        
        return {
            "hashtag": hashtag,
            "url": url,
            "status": resp.status_code,
            "html": resp.text,
            "size_kb": len(resp.text) // 1024
        }
