"""
OAuth Service for Instagram Authentication

This service handles the complete Instagram OAuth flow:
1. Generating OAuth URLs with state management
2. Exchanging authorization codes for tokens
3. Managing token lifecycle (short-lived to long-lived)
4. Fetching Instagram Business Account information

Separation of Concerns:
- This service focuses ONLY on OAuth and authentication
- Data fetching is handled by DataService
- Profile management is handled in views/models
"""

import logging
from typing import Dict, Optional, Tuple
from django.conf import settings
from django.contrib.auth import get_user_model

from ..instagram_api import (
    get_oauth_url,
    decode_oauth_state,
    exchange_code_for_token,
    get_long_lived_token,
    get_facebook_pages,
    get_instagram_business_account,
    get_instagram_account_info,
    InstagramAPIError
)
from ..models import InfluencerProfile
from ..constants import ErrorMessages

User = get_user_model()
logger = logging.getLogger(__name__)


class OAuthService:
    """
    Service class for handling Instagram OAuth operations.
    
    This class encapsulates all OAuth-related business logic, making it
    reusable and testable independent of Django views.
    """
    
    @staticmethod
    def generate_oauth_url(user_id: int) -> str:
        """
        Generate Instagram OAuth URL for a specific user.
        
        Args:
            user_id: The ID of the user initiating OAuth
            
        Returns:
            str: Complete OAuth URL with encoded state parameter
            
        Flow:
            1. Encode user_id in state parameter for security
            2. Generate OAuth URL with required scopes
            3. Return URL for frontend to redirect user
        """
        logger.info(f"Generating OAuth URL for user {user_id}")
        return get_oauth_url(user_id=user_id)
    
    @staticmethod
    def process_oauth_callback(code: str, state: str) -> Tuple[bool, Optional[InfluencerProfile], Optional[str]]:
        """
        Process the OAuth callback and create/update influencer profile.
        
        This is the CORE method that handles the entire OAuth callback flow:
        1. Decode state to get user_id
        2. Exchange code for short-lived token
        3. Exchange for long-lived token (60 days)
        4. Get Facebook Pages
        5. Get Instagram Business Account ID
        6. Get Instagram account info (username, etc.)
        7. Create/update InfluencerProfile
        8. Store encrypted access token
        
        Args:
            code: Authorization code from Instagram
            state: State parameter containing user_id
            
        Returns:
            Tuple of (success: bool, profile: InfluencerProfile or None, error: str or None)
            
        Example:
            success, profile, error = OAuthService.process_oauth_callback(code, state)
            if success:
                # Trigger background data fetching
                process_influencer_data.delay(profile.id)
            else:
                # Handle error
                logger.error(f"OAuth failed: {error}")
        """
        try:
            # Step 1: Decode state to get user_id
            state_data = decode_oauth_state(state)
            if not state_data or 'user_id' not in state_data:
                logger.error(f"Invalid state parameter: {state}")
                return False, None, ErrorMessages.INVALID_STATE
            
            user_id = state_data['user_id']
            logger.info(f"Processing OAuth callback for user {user_id}")
            
            # Step 2: Get the user
            try:
                user = User.objects.get(id=user_id)
            except User.DoesNotExist:
                logger.error(f"User with id {user_id} not found")
                return False, None, ErrorMessages.USER_NOT_FOUND
            
            # Step 3: Exchange code for short-lived token
            try:
                token_data = exchange_code_for_token(code)
                short_lived_token = token_data['access_token']
                logger.info(f"Obtained short-lived token for user {user_id}")
            except InstagramAPIError as e:
                logger.error(f"Token exchange failed: {e}")
                return False, None, ErrorMessages.TOKEN_EXCHANGE_FAILED
            
            # Step 4: Exchange for long-lived token (60 days)
            try:
                long_lived_data = get_long_lived_token(short_lived_token)
                access_token = long_lived_data['access_token']
                logger.info(f"Obtained long-lived token for user {user_id}")
            except InstagramAPIError as e:
                logger.error(f"Long-lived token exchange failed: {e}")
                return False, None, ErrorMessages.LONG_TOKEN_FAILED
            
            # Step 5: Get Facebook Pages
            try:
                pages = get_facebook_pages(access_token)
                if not pages:
                    return False, None, ErrorMessages.NO_FACEBOOK_PAGES
            except InstagramAPIError as e:
                logger.error(f"Failed to get Facebook pages: {e}")
                return False, None, ErrorMessages.PAGES_FETCH_FAILED
            
            # Step 6: Get Instagram Business Account ID from pages
            instagram_business_id = None
            page_access_token = None
            
            for page in pages:
                ig_id = get_instagram_business_account(page['id'], page['access_token'])
                if ig_id:
                    instagram_business_id = ig_id
                    page_access_token = page['access_token']
                    logger.info(f"Found Instagram Business Account: {ig_id}")
                    break
            
            if not instagram_business_id:
                return False, None, ErrorMessages.NO_INSTAGRAM_ACCOUNT
            
            # Step 7: Get Instagram account info (username, etc.)
            try:
                account_info = get_instagram_account_info(instagram_business_id, page_access_token)
                instagram_handle = account_info.get('username', '')
                
                if not instagram_handle:
                    return False, None, ErrorMessages.EMPTY_INSTAGRAM_HANDLE
                    
            except Exception as e:
                logger.error(f"Failed to get Instagram account info: {e}")
                return False, None, "Failed to get Instagram account information"

            # Step 8: Check if handle already exists for another user
            existing_profile = InfluencerProfile.objects.filter(
                instagram_handle=instagram_handle
            ).exclude(user=user).first()

            if existing_profile:
                logger.error(f"Instagram handle @{instagram_handle} already connected to user {existing_profile.user.username}")
                return False, None, ErrorMessages.HANDLE_ALREADY_CONNECTED

            # Step 9: Create or update InfluencerProfile
            try:
                profile, created = InfluencerProfile.objects.get_or_create(
                    user=user,
                    defaults={
                        'instagram_handle': instagram_handle,
                        'instagram_business_id': instagram_business_id,
                    }
                )

                if not created:
                    # Update existing profile
                    profile.instagram_handle = instagram_handle
                    profile.instagram_business_id = instagram_business_id

                # Store encrypted access token (use page access token - longer lived)
                profile.set_access_token(page_access_token)
                profile.data_processing_status = 'PENDING'
                profile.error_message = None  # Clear any previous errors
                profile.save()

                logger.info(f"Saved Instagram profile for user {user_id}: @{instagram_handle} (created={created})")

                return True, profile, None

            except Exception as e:
                logger.error(f"Error saving profile: {e}")
                return False, None, f"{ErrorMessages.PROFILE_SAVE_FAILED}: {str(e)}"

        except Exception as e:
            logger.error(f"Unexpected error in OAuth callback: {e}", exc_info=True)
            return False, None, str(e)

    @staticmethod
    def validate_user_is_influencer(user) -> Tuple[bool, Optional[str]]:
        """
        Validate that a user has the influencer role.

        Args:
            user: User instance

        Returns:
            Tuple of (is_valid: bool, error_message: str or None)
        """
        if not user.is_influencer:
            return False, ErrorMessages.NOT_INFLUENCER
        return True, None

