from django.test import TestCase, RequestFactory
from django.contrib.auth.models import User
from django.contrib.sessions.middleware import SessionMiddleware
from django.http import HttpResponse
from unittest.mock import Mock, patch

from oauth2_capture.views import oauth2_callback, initiate_oauth2


class OAuthViewsTestCase(TestCase):
    def setUp(self):
        self.factory = RequestFactory()
        self.user = User.objects.create_user(username='testuser', password='testpass')

    def _add_session_to_request(self, request):
        """Helper method to add session support to request"""
        middleware = SessionMiddleware(get_response=lambda req: HttpResponse())
        middleware.process_request(request)
        request.session.save()

    @patch('oauth2_capture.services.oauth2.OAuth2ProviderFactory.get_provider')
    def test_oauth2_callback_success(self, mock_get_provider):
        """Test successful OAuth2 callback processing."""
        # Mock provider
        mock_provider = Mock()
        mock_provider.exchange_code_for_token.return_value = {
            'access_token': 'test_access_token',
            'refresh_token': 'test_refresh_token',
            'expires_in': 3600
        }
        mock_provider.get_user_info.return_value = {
            'id': '12345',
            'name': 'Test User',
            'email': 'test@example.com'
        }
        mock_get_provider.return_value = mock_provider

        # Create request with session
        request = self.factory.get('/oauth2_capture/twitter/callback/', {
            'code': 'authorization_code_123',
            'state': 'state_value_123'
        })
        request.user = self.user
        self._add_session_to_request(request)
        request.session['twitter_oauth_state'] = 'state_value_123'

        # Call the view
        response = oauth2_callback(request, 'twitter')

        # Verify success response (returns 200 with success message, not redirect)
        self.assertEqual(response.status_code, 200)
        self.assertIn('Twitter account connected successfully', response.content.decode())

        # Verify provider methods were called
        mock_provider.exchange_code_for_token.assert_called_once()
        mock_provider.get_user_info.assert_called_once()

    @patch('oauth2_capture.services.oauth2.OAuth2ProviderFactory.get_provider')
    def test_oauth2_callback_state_mismatch(self, mock_get_provider):
        """Test OAuth2 callback with state parameter mismatch."""
        request = self.factory.get('/oauth2_capture/twitter/callback/', {
            'code': 'authorization_code_123',
            'state': 'wrong_state'
        })
        request.user = self.user
        self._add_session_to_request(request)
        request.session['twitter_oauth_state'] = 'expected_state'

        response = oauth2_callback(request, 'twitter')

        # Should return error response
        self.assertEqual(response.status_code, 400)
        self.assertIn('Invalid OAuth state', response.content.decode())

    def test_oauth2_callback_missing_code(self):
        """Test OAuth2 callback without authorization code."""
        request = self.factory.get('/oauth2_capture/twitter/callback/', {
            'state': 'state_value_123'
        })
        request.user = self.user
        self._add_session_to_request(request)
        request.session['twitter_oauth_state'] = 'state_value_123'

        response = oauth2_callback(request, 'twitter')

        # Should return error response
        self.assertEqual(response.status_code, 400)
        self.assertIn('No authorization code', response.content.decode())

    @patch('oauth2_capture.services.oauth2.OAuth2ProviderFactory.get_provider')
    def test_initiate_oauth2_redirect(self, mock_get_provider):
        """Test OAuth2 initiation redirects to provider."""
        # Mock provider
        mock_provider = Mock()
        mock_provider.get_authorization_url.return_value = 'https://api.twitter.com/oauth/authorize?client_id=123'
        mock_get_provider.return_value = mock_provider

        request = self.factory.get('/oauth2_capture/twitter/authorize/')
        request.user = self.user
        self._add_session_to_request(request)

        response = initiate_oauth2(request, 'twitter')

        # Should redirect to provider
        self.assertEqual(response.status_code, 302)
        self.assertIn('api.twitter.com', response.url)

        # State should be stored in session (generated by the view)
        self.assertIn('twitter_oauth_state', request.session)
        self.assertTrue(len(request.session['twitter_oauth_state']) > 0)
