from rest_framework import viewsets, status
from rest_framework.decorators import api_view, permission_classes, action
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from django.shortcuts import redirect
from django.conf import settings
from urllib.parse import quote
import logging

from .models import InfluencerProfile, MediaPost
from .serializers import (
    InfluencerProfileSerializer,
    InfluencerProfileListSerializer,
    MediaPostSerializer,
)
from .services import OAuthService, ProfileService
from .constants import ErrorMessages, SuccessMessages
from .tasks import process_influencer_data
from businesses.models import CollaborationRequest
from businesses.serializers import CollaborationRequestSerializer, CollaborationResponseSerializer

logger = logging.getLogger(__name__)


# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

def get_frontend_url() -> str:
    """Get the frontend URL from settings."""
    return settings.CORS_ALLOWED_ORIGINS[0] if settings.CORS_ALLOWED_ORIGINS else 'http://localhost:3000'


def trigger_data_processing(profile_id: int) -> None:
    """
    Trigger background data processing task.

    With CELERY_TASK_ALWAYS_EAGER=True (default in DEBUG), runs inline so metrics
    update without Redis/worker. Otherwise requires broker + celery worker.
    Logs errors but doesn't fail the HTTP response if enqueue fails.
    """
    try:
        process_influencer_data.delay(profile_id)
        logger.info(f"Triggered data fetching task for profile {profile_id}")
    except Exception as e:
        logger.error(
            "Failed to trigger data fetching task for profile %s: %s. "
            "If CELERY_TASK_ALWAYS_EAGER is false, start Redis and run: "
            "celery -A sync_backend worker -l info",
            profile_id,
            e,
            exc_info=True,
        )


# ============================================================================
# PART 2: INSTAGRAM OAUTH FLOW
# ============================================================================

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def instagram_connect_url(request):
    """
    Get the Instagram OAuth URL for the user to connect their account.

    GET /api/influencers/instagram/connect-url/

    Returns:
        {
            "oauth_url": "https://www.facebook.com/v18.0/dialog/oauth?..."
        }
    """
    # Validate user is an influencer
    is_valid, error = OAuthService.validate_user_is_influencer(request.user)
    if not is_valid:
        return Response({'error': error}, status=status.HTTP_403_FORBIDDEN)

    # Generate OAuth URL
    oauth_url = OAuthService.generate_oauth_url(request.user.id)

    return Response({'oauth_url': oauth_url})


@api_view(['GET'])
@permission_classes([AllowAny])
def instagram_callback(request):
    """
    Instagram OAuth callback endpoint.

    GET /api/influencers/instagram/callback/?code=...&state=...

    This is called by Facebook/Instagram after the user grants permissions.
    Uses OAuthService to handle the complete OAuth flow and profile creation.
    """
    code = request.GET.get('code')
    state = request.GET.get('state')
    error = request.GET.get('error')
    frontend_url = get_frontend_url()

    # Handle OAuth errors from Instagram
    if error:
        logger.error(f"OAuth error from Instagram: {error}")
        return redirect(f"{frontend_url}/influencer/dashboard?instagram=error&error={error}")

    # Validate required parameters
    if not code:
        logger.error("No authorization code provided")
        return redirect(f"{frontend_url}/influencer/dashboard?instagram=error&error=no_code")

    if not state:
        logger.error("No state parameter provided")
        return redirect(f"{frontend_url}/influencer/dashboard?instagram=error&error=no_state")

    # Process OAuth callback using service layer
    success, profile, error_message = OAuthService.process_oauth_callback(code, state)

    if not success:
        error_param = quote(error_message) if error_message else 'unknown_error'
        return redirect(f"{frontend_url}/influencer/dashboard?instagram=error&error={error_param}")

    # OAuth successful - trigger background data fetching
    trigger_data_processing(profile.id)

    # Redirect to frontend success page
    return redirect(f"{frontend_url}/influencer/dashboard?instagram=success&handle={profile.instagram_handle}")





# ============================================================================
# INFLUENCER PROFILE ENDPOINTS
# ============================================================================

class InfluencerProfileViewSet(viewsets.ModelViewSet):
    """
    ViewSet for InfluencerProfile management.

    - List: GET /api/influencers/profiles/ (for businesses to search)
    - Retrieve: GET /api/influencers/profiles/{id}/
    - My Profile: GET /api/influencers/profiles/me/
    - Refresh Data: POST /api/influencers/profiles/{id}/refresh-data/
    """
    serializer_class = InfluencerProfileSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        """
        Filter queryset based on user role using ProfileService.

        - Businesses can see all verified influencers with filters
        - Influencers can only see their own profile
        """
        if self.request.user.is_business:
            # Get filter parameters
            niche = self.request.query_params.get('niche')
            min_followers = self.request.query_params.get('min_followers')
            min_engagement = self.request.query_params.get('min_engagement')

            # Convert to appropriate types
            min_followers = int(min_followers) if min_followers else None
            min_engagement = float(min_engagement) if min_engagement else None

            # Use service to get filtered profiles
            return ProfileService.get_profiles_for_business(
                niche=niche,
                min_followers=min_followers,
                min_engagement=min_engagement
            )

        elif self.request.user.is_influencer:
            # Influencers can only see their own profile
            return InfluencerProfile.objects.filter(user=self.request.user)

        return InfluencerProfile.objects.none()
    
    def get_serializer_class(self):
        """Use simplified serializer for list view"""
        if self.action == 'list':
            return InfluencerProfileListSerializer
        return InfluencerProfileSerializer
    
    @action(detail=False, methods=['get'])
    def me(self, request):
        """
        Get current user's influencer profile.

        GET /api/influencers/me/
        """
        # Validate user is an influencer
        is_valid, error = OAuthService.validate_user_is_influencer(request.user)
        if not is_valid:
            return Response({'error': error}, status=status.HTTP_403_FORBIDDEN)

        # Get profile using service
        profile = ProfileService.get_profile_for_user(request.user)

        if not profile:
            return Response({
                'error': ErrorMessages.PROFILE_NOT_FOUND
            }, status=status.HTTP_404_NOT_FOUND)

        serializer = self.get_serializer(profile)
        return Response(serializer.data)

    @action(detail=True, methods=['post'], url_path='refresh-data')
    def refresh_data(self, request, pk=None):
        """
        Manually trigger data refresh for an influencer profile.

        POST /api/influencers/profiles/{id}/refresh-data/
        """
        profile = self.get_object()

        # Only allow influencer to refresh their own data
        if profile.user != request.user:
            return Response({'error': ErrorMessages.REFRESH_OWN_DATA_ONLY},
                          status=status.HTTP_403_FORBIDDEN)

        # Check if profile can be refreshed
        can_refresh, error = ProfileService.can_refresh_profile(profile)
        if not can_refresh:
            return Response({'error': error}, status=status.HTTP_400_BAD_REQUEST)

        # Mark profile for refresh and trigger background task
        ProfileService.mark_profile_for_refresh(profile)
        trigger_data_processing(profile.id)

        return Response({
            'message': SuccessMessages.DATA_REFRESH_STARTED,
            'profile': InfluencerProfileSerializer(profile).data
        })


class MediaPostViewSet(viewsets.ReadOnlyModelViewSet):
    """
    ViewSet for viewing MediaPost data.

    - List: GET /api/media-posts/ (filtered by influencer)
    - Retrieve: GET /api/media-posts/{id}/
    """
    serializer_class = MediaPostSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        """Filter media posts by influencer using ProfileService"""
        # Filter by influencer profile ID if provided
        profile_id = self.request.query_params.get('profile_id')
        if profile_id:
            return ProfileService.get_media_posts_for_profile(int(profile_id))

        # If user is an influencer, only show their posts
        if self.request.user.is_influencer:
            return ProfileService.get_media_posts_for_user(self.request.user)

        # Businesses can see all posts
        return MediaPost.objects.all()


# ============================================================================
# UTILITY ENDPOINTS
# ============================================================================

@api_view(['GET'])
@permission_classes([AllowAny])
def get_available_niches(request):  # request required by DRF
    """
    Get list of all available niches.

    GET /api/influencers/niches/
    """
    niches = ProfileService.get_available_niches()
    return Response({'niches': niches})


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def influencer_collaborations(request):
    """List collaboration requests received by the current influencer."""
    if not request.user.is_influencer:
        return Response({'error': 'Only influencers can access this.'}, status=status.HTTP_403_FORBIDDEN)

    try:
        profile = request.user.influencer_profile
    except InfluencerProfile.DoesNotExist:
        return Response({'error': 'Connect your Instagram account first.'}, status=status.HTTP_400_BAD_REQUEST)

    collabs = CollaborationRequest.objects.filter(influencer=profile)
    serializer = CollaborationRequestSerializer(collabs, many=True)
    return Response(serializer.data)


@api_view(['PATCH'])
@permission_classes([IsAuthenticated])
def respond_to_collaboration(request, pk):
    """Accept or reject a collaboration request."""
    if not request.user.is_influencer:
        return Response({'error': 'Only influencers can respond.'}, status=status.HTTP_403_FORBIDDEN)

    try:
        collab = CollaborationRequest.objects.get(pk=pk, influencer__user=request.user)
    except CollaborationRequest.DoesNotExist:
        return Response({'error': 'Collaboration request not found.'}, status=status.HTTP_404_NOT_FOUND)

    if collab.status != CollaborationRequest.Status.PENDING:
        return Response({'error': 'This request has already been responded to.'}, status=status.HTTP_400_BAD_REQUEST)

    serializer = CollaborationResponseSerializer(data=request.data)
    if not serializer.is_valid():
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    collab.status = serializer.validated_data['status']
    collab.response_message = serializer.validated_data.get('response_message', '')
    collab.save()

    return Response(CollaborationRequestSerializer(collab).data)


