# social/services/meta.py - FIXED VERSION
import requests
import time
import hmac
import hashlib
import logging
import datetime
from urllib.parse import urlencode
from django.conf import settings
from django.utils import timezone
from apps.social.utils.meta import BASE_GRAPH_URL, META_SCOPES, BASE_AUTH_URL

logger = logging.getLogger(__name__)

class MetaAPIError(Exception):
    pass

class MetaService:

    # --- AUTHENTICATION ---

    @staticmethod
    def get_auth_url():
        params = {
            "client_id": settings.META_APP_ID,
            "redirect_uri": settings.META_REDIRECT_URI,
            "scope": ",".join(META_SCOPES),
            "response_type": "code",
        }
        return f"{BASE_AUTH_URL}/dialog/oauth?{urlencode(params)}"

    @staticmethod
    def exchange_code_for_tokens(code):
        """Exchanges code for a long-lived User Access Token"""
        # Step 1: Get short-lived token
        res = requests.post(f"{BASE_GRAPH_URL}/oauth/access_token", data={
            "client_id": settings.META_APP_ID,
            "client_secret": settings.META_APP_SECRET,
            "code": code,
            "redirect_uri": settings.META_REDIRECT_URI,
        })
        
        data = MetaService._handle_api_response(res)
        
        # Step 2: Exchange for long-lived token
        long_res = requests.post(f"{BASE_GRAPH_URL}/oauth/access_token", data={
            "grant_type": "fb_exchange_token",
            "client_id": settings.META_APP_ID,
            "client_secret": settings.META_APP_SECRET,
            "fb_exchange_token": data['access_token']
        })
        
        long_data = MetaService._handle_api_response(long_res)
        
        expires_in = long_data.get('expires_in', 5184000)
        return {
            "access_token": long_data['access_token'],
            "expires_at": timezone.now() + datetime.timedelta(seconds=expires_in)
        }

    # --- API HELPER ---
    
    @staticmethod
    def _handle_api_response(response):
        """Handle API response with proper error checking and rate limit handling"""
        try:
            data = response.json()
        except:
            raise MetaAPIError(f"Invalid JSON response: {response.text}")
        
        if 'error' in data:
            error_code = data['error'].get('code')
            error_msg = data['error'].get('message', 'Unknown error')
            
            # Handle rate limits
            if error_code in [4, 17, 32]:
                logger.warning(f"Rate limit hit (code {error_code}). Waiting 60 seconds...")
                time.sleep(60)
                raise MetaAPIError(f"Rate limited: {error_msg}")
            
            # Handle permission errors
            if error_code in [200, 190, 102]:
                raise MetaAPIError(f"Permission error: {error_msg}")
            
            raise MetaAPIError(f"API Error (code {error_code}): {error_msg}")
        
        return data

    # --- DISCOVERY ---

    @staticmethod
    def discover_pages_and_ig(user_access_token):
        """
        Returns a list of manageable entities (FB Pages & IG Business Accounts).
        Each dict contains: platform ('FB'|'IG'), native_id, name, access_token
        """
        entities = []
        next_page = None
        
        while True:
            params = {
                "access_token": user_access_token,
                "fields": "id,name,access_token,instagram_business_account{id,username}",
                "limit": 100
            }
            if next_page: 
                params['after'] = next_page
            
            try:
                res = requests.get(f"{BASE_GRAPH_URL}/me/accounts", params=params)
                data = MetaService._handle_api_response(res)
                
                for page in data.get('data', []):
                    # 1. Add Facebook Page
                    entities.append({
                        'platform': 'FB', 
                        'native_id': page['id'], 
                        'name': page['name'],
                        'access_token': page['access_token'],
                        'is_permanent': True  # Page tokens don't expire if app is active
                    })
                    
                    # 2. Add Linked Instagram Business Account (if exists)
                    ig_data = page.get('instagram_business_account')
                    if ig_data:
                        entities.append({
                            'platform': 'IG', 
                            'native_id': ig_data['id'], 
                            'name': f"IG: {ig_data.get('username', page['name'])}",
                            'access_token': page['access_token'],
                            'is_permanent': True,
                            'parent_page_id': page['id']
                        })
                
                next_page = data.get('paging', {}).get('cursors', {}).get('after')
                if not next_page: 
                    break
                    
            except MetaAPIError as e:
                logger.error(f"Discovery Error: {e}")
                break
            except Exception as e:
                logger.error(f"Discovery Exception: {e}")
                break
        
        return entities

    # --- DATA FETCHING ---

    @staticmethod
    def fetch_posts(entity_id, access_token, platform_type):
        """
        Fetches posts from a specific FB Page or IG Account.
        """
        posts = []
        next_page = None
        
        # Determine endpoint and fields based on platform
        if platform_type == "FB":
            if entity_id == 'me':
                endpoint = f"{entity_id}/feed"
            else:
                endpoint = f"{entity_id}/feed"
            
            fields = "id,message,created_time,permalink_url"
        else:  # Instagram
            endpoint = f"{entity_id}/media"
            fields = "id,caption,timestamp,permalink,media_type,media_url,thumbnail_url"
        
        while True:
            params = {
                "access_token": access_token, 
                "limit": 25, 
                "fields": fields
            }
            if next_page: 
                params['after'] = next_page
            
            try:
                res = requests.get(f"{BASE_GRAPH_URL}/{endpoint}", params=params)
                res_json = res.json()
                
                if 'error' in res_json:
                    error_msg = res_json.get('error', {}).get('message', 'Unknown error')
                    logger.warning(f"API Error fetching posts for {entity_id}: {error_msg}")
                    break
                
                posts_data = res_json.get('data', [])
                posts.extend(posts_data)
                
                paging = res_json.get('paging', {})
                next_page = paging.get('cursors', {}).get('after')
                
                if not next_page: 
                    break
                    
                time.sleep(0.5)  # Rate limiting
                
            except requests.exceptions.RequestException as e:
                logger.error(f"Network error fetching posts for {entity_id}: {e}")
                break
            except Exception as e:
                logger.error(f"Exception fetching posts for {entity_id}: {e}", exc_info=True)
                break
        
        logger.info(f"Fetched total of {len(posts)} posts for {entity_id}")
        return posts

    @staticmethod
    def fetch_comments_for_post(post_id, access_token, since_timestamp=None):
        """
        Fetches comments for a specific post (works for both FB and IG).
        
        FIXED: Dynamically selects fields based on platform detection to avoid
        Error #100 (nonexisting field 'name' on IGCommentFromUser).
        """
        url = f"{BASE_GRAPH_URL}/{post_id}/comments"
        comments = []
        next_page = None
        
        # --- Platform Detection ---
        # Instagram IDs typically start with 17 or 18.
        str_post_id = str(post_id)
        is_instagram = str_post_id.startswith('17') or str_post_id.startswith('18')
        
        # --- Field Selection ---
        if is_instagram:
            # IG: Use 'username' if available, but NEVER 'name' on the user object
            # Note: 'username' is usually available on IGCommentFromUser
            request_fields = "id,from{id,username},message,text,created_time,post,like_count,comment_count,attachment"
        else:
            # FB: 'name' is standard
            request_fields = "id,from{id,name},message,text,created_time,post,like_count,comment_count,attachment"
        
        while True:
            params = {
                "access_token": access_token, 
                "limit": 50, 
                "fields": request_fields,  # Use the selected fields
                "order": "reverse_chronological"
            }
            
            if since_timestamp:
                if isinstance(since_timestamp, datetime.datetime):
                    since_timestamp = int(since_timestamp.timestamp())
                params['since'] = since_timestamp
            
            if next_page: 
                params['after'] = next_page
            
            try:
                res = requests.get(url, params=params)
                data = MetaService._handle_api_response(res)
                
                new_comments = data.get('data', [])
                if not new_comments: 
                    break
                
                comments.extend(new_comments)
                next_page = data.get('paging', {}).get('cursors', {}).get('after')
                
                if not next_page: 
                    break
                
                time.sleep(0.5)  # Rate limiting
                
            except MetaAPIError as e:
                logger.warning(f"Error fetching comments for {post_id}: {e}")
                break
            except Exception as e: 
                logger.error(f"Exception fetching comments for {e}")
                break
        
        return comments

    @staticmethod
    def fetch_single_comment(comment_id, access_token):
        """Fetch a single comment by ID (works for both FB and IG)"""
        url = f"{BASE_GRAPH_URL}/{comment_id}"
        
        # Safe fallback fields usually work for both, but IG might reject 'name'
        # We'll default to username for safety if it looks like IG
        str_id = str(comment_id)
        if str_id.startswith('17') or str_id.startswith('18'):
             fields = "id,from{id,username},message,text,created_time,post,like_count,attachment"
        else:
             fields = "id,from{id,name},message,text,created_time,post,like_count,attachment"

        params = {
            "fields": fields, 
            "access_token": access_token
        }
        
        res = requests.get(url, params=params)
        data = MetaService._handle_api_response(res)
        
        return data

    # @staticmethod
    # def post_reply(comment_id, access_token, text):
    #     """
    #     Post a reply to a comment (works for both FB and IG).
    #     """
    #     url = f"{BASE_GRAPH_URL}/{comment_id}/comments"
        
    #     try:
    #         res = requests.post(
    #             url, 
    #             params={"access_token": access_token}, 
    #             json={"message": text}
    #         )
            
    #         data = MetaService._handle_api_response(res)
            
    #         # Graceful handling for Error 100 (Unsupported operation)
    #         error = data.get('error', {})
    #         if error and error.get('code') == 100 and "Unsupported" in error.get('message', ''):
    #              logger.warning(f"Reply failed for {comment_id}: Comment might be deleted, private, or restricted.")
    #              return data
            
    #         return data
            
    #     except MetaAPIError as e:
    #         raise MetaAPIError(f"Reply failed: {str(e)}")
    #     except requests.exceptions.RequestException as e:
    #         raise MetaAPIError(f"Network error posting reply: {str(e)}")
    @staticmethod
    def post_reply(comment_id, access_token, platform='FB', text=None):
        """
        Post a reply to a comment (Handle FB vs IG endpoints).
        
        Args:
            platform (str): 'facebook' or 'instagram' (default: 'facebook')
        """
        # STEP 1: Choose the correct endpoint
        if platform.lower() == 'ig':
            # Instagram requires /replies endpoint for comments
            url = f"{BASE_GRAPH_URL}/{comment_id}/replies"
        else:
            # Facebook treats replies as 'comments on a comment'
            url = f"{BASE_GRAPH_URL}/{comment_id}/comments"
        
        try:
            res = requests.post(
                url, 
                params={"access_token": access_token}, 
                json={"message": text}
            )
            
            data = MetaService._handle_api_response(res)
            return data
            
        except MetaAPIError as e:
            # Check for Error 100 (Unsupported operation)
            # This often happens if you try to reply to an IG comment that is ALREADY a reply
            # (Instagram only supports 1 level of nesting)
            error_code = e.args[0] if isinstance(e, tuple) and len(e.args) > 0 else None
            
            if error_code == 100 or "Unsupported" in str(e):
                 logger.warning(f"Reply failed for {comment_id} ({platform}): Object might be deleted, restricted, or you are trying to reply to a reply (nested) which IG blocks.")
                 raise e
            
            # Re-raise other errors
            raise e
            
        except requests.exceptions.RequestException as e:
            raise MetaAPIError(f"Network error posting reply: {str(e)}")
    @staticmethod
    def subscribe_webhook(page_id, access_token):
        """
        Subscribes a specific page to the app's webhook.
        """
        url = f"{BASE_GRAPH_URL}/{page_id}/subscribed_apps"
        res = requests.post(
            url, 
            json={
                "access_token": access_token, 
                "subscribed_fields": ["comments", "feed"] 
            }
        )
        
        data = MetaService._handle_api_response(res)
        return True

    # --- WEBHOOK UTILS ---

    @staticmethod
    def verify_webhook_signature(received_signature, body_raw, client_secret):
        """Verify webhook signature from Meta"""
        if not received_signature or not body_raw: 
            return False
        
        calculated_digest = hmac.new(
            client_secret.encode('utf-8'), 
            body_raw, 
            hashlib.sha256
        ).hexdigest()
        
        expected_signature = f"sha256={calculated_digest}"
        return hmac.compare_digest(received_signature, expected_signature)
    
    # --- HELPER METHODS ---
    
    @staticmethod
    def detect_source_platform(comment_id, post_id=None):
        """
        Reliably detect if comment is from FB or IG based on ID format.
        """
        if comment_id and comment_id.startswith('17') and comment_id.isdigit():
            return 'IG'
        elif comment_id and '_' in comment_id:
            return 'FB'
        elif post_id:
            # Fallback: Check post ID format
            if str(post_id).startswith('17') and str(post_id).isdigit():
                return 'IG'
        
        return 'FB'  # Default to Facebook