"""
OAuth API Views for Email Integration

Provides endpoints for initiating OAuth flows and handling callbacks
for Google and Microsoft OAuth authentication.
"""

import logging
import secrets
from urllib.parse import urljoin

from django.conf import settings
from django.shortcuts import redirect
from django.http import HttpResponse
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

from rest_framework.permissions import AllowAny

from smoothschedule.platform.admin.permissions import IsPlatformAdmin
from .models import OAuthCredential
from .oauth_service import (
    GoogleOAuthService,
    MicrosoftOAuthService,
    get_oauth_service,
)
from .permissions import HasFeaturePermission

logger = logging.getLogger(__name__)


class OAuthProvidersView(APIView):
    """
    Public endpoint to get available OAuth login providers.

    GET /api/auth/oauth/providers/

    Returns list of OAuth providers available for login.
    This endpoint is public (no auth required) for the login page.
    """
    permission_classes = [AllowAny]

    def get(self, request):
        # For now, return empty list since OAuth login isn't fully implemented
        # In the future, this would check tenant settings for enabled providers
        providers = []

        # Check if Google OAuth is configured at platform level
        google_service = GoogleOAuthService()
        if google_service.is_configured():
            providers.append({
                'name': 'google',
                'display_name': 'Google',
                'icon': 'google',
            })

        # Check if Microsoft OAuth is configured at platform level
        microsoft_service = MicrosoftOAuthService()
        if microsoft_service.is_configured():
            providers.append({
                'name': 'microsoft',
                'display_name': 'Microsoft',
                'icon': 'microsoft',
            })

        return Response({'providers': providers})


def get_oauth_redirect_uri(request, provider: str) -> str:
    """
    Build the OAuth callback URL.

    For local development, uses lvh.me domain.
    For production, uses the request's host.
    """
    # Get the base URL from settings or request
    if settings.DEBUG:
        base_url = 'http://platform.lvh.me:8000'
    else:
        scheme = 'https' if request.is_secure() else 'http'
        base_url = f'{scheme}://{request.get_host()}'

    return f'{base_url}/api/oauth/{provider}/callback/'


class OAuthStatusView(APIView):
    """
    Check OAuth configuration status.

    GET /api/oauth/status/

    Returns which OAuth providers are configured and available.
    """
    permission_classes = [IsPlatformAdmin]

    def get(self, request):
        google_service = GoogleOAuthService()
        microsoft_service = MicrosoftOAuthService()

        return Response({
            'google': {
                'configured': google_service.is_configured(),
            },
            'microsoft': {
                'configured': microsoft_service.is_configured(),
            },
        })


class GoogleOAuthInitiateView(APIView):
    """
    Initiate Google OAuth flow for email or calendar access.

    POST /api/oauth/google/initiate/
    Body: { "purpose": "email" | "calendar" }

    Returns authorization URL to redirect user to.

    Permission Requirements:
    - For "email" purpose: IsPlatformAdmin only
    - For "calendar" purpose: Requires can_use_calendar_sync feature permission
    """
    permission_classes = [IsPlatformAdmin]

    def post(self, request):
        purpose = request.data.get('purpose', 'email')

        # Check calendar sync permission if purpose is calendar
        if purpose == 'calendar':
            calendar_permission = HasFeaturePermission('can_use_calendar_sync')
            if not calendar_permission().has_permission(request, self):
                return Response({
                    'success': False,
                    'error': 'Your current plan does not include Calendar Sync. Please upgrade your subscription to access this feature.',
                }, status=status.HTTP_403_FORBIDDEN)

        service = GoogleOAuthService()
        if not service.is_configured():
            return Response({
                'success': False,
                'error': 'Google OAuth is not configured. Please add GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET to settings.',
            }, status=status.HTTP_400_BAD_REQUEST)

        # Generate state token for CSRF protection
        state = secrets.token_urlsafe(32)

        # Store state in session
        request.session['oauth_state'] = state
        request.session['oauth_purpose'] = purpose
        request.session['oauth_provider'] = 'google'

        redirect_uri = get_oauth_redirect_uri(request, 'google')

        try:
            auth_url = service.get_authorization_url(
                redirect_uri=redirect_uri,
                state=state,
            )

            return Response({
                'success': True,
                'authorization_url': auth_url,
            })

        except Exception as e:
            logger.error(f"Failed to generate Google OAuth URL: {e}")
            return Response({
                'success': False,
                'error': str(e),
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class GoogleOAuthCallbackView(APIView):
    """
    Handle Google OAuth callback.

    GET /api/oauth/google/callback/?code=...&state=...

    Exchanges code for tokens and saves credential.
    Redirects to frontend with success/error status.
    """
    permission_classes = []  # Public endpoint (callback from Google)

    def get(self, request):
        code = request.GET.get('code')
        state = request.GET.get('state')
        error = request.GET.get('error')

        # Frontend URL to redirect to after OAuth
        frontend_base = settings.FRONTEND_URL if hasattr(settings, 'FRONTEND_URL') else 'http://platform.lvh.me:5173'
        success_url = f'{frontend_base}/platform/settings?oauth=success&provider=google'
        error_url = f'{frontend_base}/platform/settings?oauth=error&provider=google'

        if error:
            logger.warning(f"Google OAuth error: {error}")
            return redirect(f'{error_url}&message={error}')

        # Verify state
        expected_state = request.session.get('oauth_state')
        if not state or state != expected_state:
            logger.warning("Google OAuth state mismatch")
            return redirect(f'{error_url}&message=invalid_state')

        if not code:
            return redirect(f'{error_url}&message=no_code')

        service = GoogleOAuthService()
        redirect_uri = get_oauth_redirect_uri(request, 'google')

        try:
            # Exchange code for tokens
            tokens = service.exchange_code_for_tokens(
                code=code,
                redirect_uri=redirect_uri,
            )

            email = tokens.get('email', '')
            if not email:
                return redirect(f'{error_url}&message=no_email')

            purpose = request.session.get('oauth_purpose', 'email')

            # Save or update credential
            credential, created = OAuthCredential.objects.update_or_create(
                tenant=None,  # Platform-level credential
                email=email,
                purpose=purpose,
                defaults={
                    'provider': 'google',
                    'access_token': tokens['access_token'],
                    'refresh_token': tokens.get('refresh_token', ''),
                    'token_expiry': tokens.get('expires_in'),
                    'scopes': tokens.get('scopes', []),
                    'is_valid': True,
                    'authorized_by': request.user if request.user.is_authenticated else None,
                }
            )

            # Set token expiry properly
            if tokens.get('expires_in'):
                from django.utils import timezone
                from datetime import timedelta
                credential.token_expiry = timezone.now() + timedelta(seconds=tokens['expires_in'])
                credential.save(update_fields=['token_expiry'])

            # Clear session
            request.session.pop('oauth_state', None)
            request.session.pop('oauth_purpose', None)
            request.session.pop('oauth_provider', None)

            logger.info(f"Successfully stored Google OAuth credential for {email}")
            return redirect(f'{success_url}&email={email}')

        except Exception as e:
            logger.error(f"Google OAuth callback error: {e}")
            return redirect(f'{error_url}&message={str(e)[:100]}')


class MicrosoftOAuthInitiateView(APIView):
    """
    Initiate Microsoft OAuth flow for email or calendar access.

    POST /api/oauth/microsoft/initiate/
    Body: { "purpose": "email" | "calendar" }

    Returns authorization URL to redirect user to.

    Permission Requirements:
    - For "email" purpose: IsPlatformAdmin only
    - For "calendar" purpose: Requires can_use_calendar_sync feature permission
    """
    permission_classes = [IsPlatformAdmin]

    def post(self, request):
        purpose = request.data.get('purpose', 'email')

        # Check calendar sync permission if purpose is calendar
        if purpose == 'calendar':
            calendar_permission = HasFeaturePermission('can_use_calendar_sync')
            if not calendar_permission().has_permission(request, self):
                return Response({
                    'success': False,
                    'error': 'Your current plan does not include Calendar Sync. Please upgrade your subscription to access this feature.',
                }, status=status.HTTP_403_FORBIDDEN)

        service = MicrosoftOAuthService()
        if not service.is_configured():
            return Response({
                'success': False,
                'error': 'Microsoft OAuth is not configured. Please add MICROSOFT_OAUTH_CLIENT_ID and MICROSOFT_OAUTH_CLIENT_SECRET to settings.',
            }, status=status.HTTP_400_BAD_REQUEST)

        # Generate state token for CSRF protection
        state = secrets.token_urlsafe(32)

        # Store state in session
        request.session['oauth_state'] = state
        request.session['oauth_purpose'] = purpose
        request.session['oauth_provider'] = 'microsoft'

        redirect_uri = get_oauth_redirect_uri(request, 'microsoft')

        try:
            auth_url = service.get_authorization_url(
                redirect_uri=redirect_uri,
                state=state,
            )

            return Response({
                'success': True,
                'authorization_url': auth_url,
            })

        except Exception as e:
            logger.error(f"Failed to generate Microsoft OAuth URL: {e}")
            return Response({
                'success': False,
                'error': str(e),
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class MicrosoftOAuthCallbackView(APIView):
    """
    Handle Microsoft OAuth callback.

    GET /api/oauth/microsoft/callback/?code=...&state=...

    Exchanges code for tokens and saves credential.
    Redirects to frontend with success/error status.
    """
    permission_classes = []  # Public endpoint (callback from Microsoft)

    def get(self, request):
        code = request.GET.get('code')
        state = request.GET.get('state')
        error = request.GET.get('error')
        error_description = request.GET.get('error_description', '')

        # Frontend URL to redirect to after OAuth
        frontend_base = settings.FRONTEND_URL if hasattr(settings, 'FRONTEND_URL') else 'http://platform.lvh.me:5173'
        success_url = f'{frontend_base}/platform/settings?oauth=success&provider=microsoft'
        error_url = f'{frontend_base}/platform/settings?oauth=error&provider=microsoft'

        if error:
            logger.warning(f"Microsoft OAuth error: {error} - {error_description}")
            return redirect(f'{error_url}&message={error}')

        # Verify state
        expected_state = request.session.get('oauth_state')
        if not state or state != expected_state:
            logger.warning("Microsoft OAuth state mismatch")
            return redirect(f'{error_url}&message=invalid_state')

        if not code:
            return redirect(f'{error_url}&message=no_code')

        service = MicrosoftOAuthService()
        redirect_uri = get_oauth_redirect_uri(request, 'microsoft')

        try:
            # Exchange code for tokens
            tokens = service.exchange_code_for_tokens(
                code=code,
                redirect_uri=redirect_uri,
            )

            email = tokens.get('email', '')
            if not email:
                return redirect(f'{error_url}&message=no_email')

            purpose = request.session.get('oauth_purpose', 'email')

            # Save or update credential
            credential, created = OAuthCredential.objects.update_or_create(
                tenant=None,  # Platform-level credential
                email=email,
                purpose=purpose,
                defaults={
                    'provider': 'microsoft',
                    'access_token': tokens['access_token'],
                    'refresh_token': tokens.get('refresh_token', ''),
                    'scopes': tokens.get('scopes', []),
                    'is_valid': True,
                    'authorized_by': request.user if request.user.is_authenticated else None,
                }
            )

            # Set token expiry properly
            if tokens.get('expires_in'):
                from django.utils import timezone
                from datetime import timedelta
                credential.token_expiry = timezone.now() + timedelta(seconds=tokens['expires_in'])
                credential.save(update_fields=['token_expiry'])

            # Clear session
            request.session.pop('oauth_state', None)
            request.session.pop('oauth_purpose', None)
            request.session.pop('oauth_provider', None)

            logger.info(f"Successfully stored Microsoft OAuth credential for {email}")
            return redirect(f'{success_url}&email={email}')

        except Exception as e:
            logger.error(f"Microsoft OAuth callback error: {e}")
            return redirect(f'{error_url}&message={str(e)[:100]}')


class OAuthCredentialListView(APIView):
    """
    List OAuth credentials.

    GET /api/oauth/credentials/

    Returns list of stored OAuth credentials (tokens are masked).
    """
    permission_classes = [IsPlatformAdmin]

    def get(self, request):
        credentials = OAuthCredential.objects.filter(
            tenant=None,  # Platform-level only
            purpose='email',
        ).order_by('-created_at')

        return Response([
            {
                'id': cred.id,
                'provider': cred.provider,
                'email': cred.email,
                'purpose': cred.purpose,
                'is_valid': cred.is_valid,
                'is_expired': cred.is_expired(),
                'last_used_at': cred.last_used_at,
                'last_error': cred.last_error,
                'created_at': cred.created_at,
            }
            for cred in credentials
        ])


class OAuthCredentialDeleteView(APIView):
    """
    Delete/revoke an OAuth credential.

    DELETE /api/oauth/credentials/{id}/

    Removes the stored credential.
    """
    permission_classes = [IsPlatformAdmin]

    def delete(self, request, credential_id):
        try:
            credential = OAuthCredential.objects.get(
                id=credential_id,
                tenant=None,  # Platform-level only
            )
            email = credential.email
            credential.delete()

            logger.info(f"Deleted OAuth credential for {email}")
            return Response({
                'success': True,
                'message': f'Credential for {email} deleted',
            })

        except OAuthCredential.DoesNotExist:
            return Response({
                'success': False,
                'error': 'Credential not found',
            }, status=status.HTTP_404_NOT_FOUND)
