"""
API tests for streams app endpoints.
"""

import json
import tempfile
import shutil
from unittest.mock import patch, MagicMock
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework import status

from apps.streams.models import Channel, StreamSession, VideoConfiguration, AudioConfiguration
from apps.streams.services import StreamCaptureService

User = get_user_model()


class ChannelAPITest(TestCase):
    """Test cases for Channel API endpoints."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        self.temp_dir = tempfile.mkdtemp()
        
        self.channel = Channel.objects.create(
            name='Test Channel',
            slug='test-channel',
            hls_url='https://example.com/stream.m3u8',
            output_directory=self.temp_dir,
            created_by=self.user
        )
    
    def tearDown(self):
        shutil.rmtree(self.temp_dir, ignore_errors=True)
    
    def test_list_channels(self):
        """Test listing channels."""
        url = reverse('channel-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], 'Test Channel')
    
    def test_create_channel(self):
        """Test creating a new channel."""
        url = reverse('channel-list')
        data = {
            'name': 'New Channel',
            'slug': 'new-channel',
            'hls_url': 'https://example.com/new-stream.m3u8',
            'output_directory': '/tmp/new-channel',
            'segment_duration': 8,
            'max_segments': 15
        }
        
        response = self.client.post(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'New Channel')
        self.assertEqual(response.data['created_by'], self.user.id)
    
    def test_update_channel(self):
        """Test updating a channel."""
        url = reverse('channel-detail', kwargs={'pk': self.channel.pk})
        data = {
            'name': 'Updated Channel',
            'segment_duration': 10
        }
        
        response = self.client.patch(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['name'], 'Updated Channel')
        self.assertEqual(response.data['segment_duration'], 10)
    
    def test_delete_channel(self):
        """Test deleting a channel."""
        url = reverse('channel-detail', kwargs={'pk': self.channel.pk})
        response = self.client.delete(url)
        
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(Channel.objects.filter(pk=self.channel.pk).exists())
    
    @patch('apps.streams.tasks.start_stream_capture.delay')
    def test_start_stream_action(self, mock_task):
        """Test starting a stream via API."""
        mock_task.return_value = MagicMock(id='test-task-id')
        
        url = reverse('channel-start-stream', kwargs={'pk': self.channel.pk})
        response = self.client.post(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('task_id', response.data)
        mock_task.assert_called_once()
    
    @patch('apps.streams.tasks.stop_stream_capture.delay')
    def test_stop_stream_action(self, mock_task):
        """Test stopping a stream via API."""
        # Create active session
        session = StreamSession.objects.create(
            channel=self.channel,
            status='active',
            started_at=timezone.now()
        )
        
        mock_task.return_value = MagicMock(id='test-task-id')
        
        url = reverse('channel-stop-stream', kwargs={'pk': self.channel.pk})
        response = self.client.post(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('task_id', response.data)
        mock_task.assert_called_once()
    
    def test_start_stream_already_active(self):
        """Test starting a stream when already active."""
        # Create active session
        StreamSession.objects.create(
            channel=self.channel,
            status='active',
            started_at=timezone.now()
        )
        
        url = reverse('channel-start-stream', kwargs={'pk': self.channel.pk})
        response = self.client.post(url)
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('already active', response.data['error'].lower())
    
    def test_stop_stream_not_active(self):
        """Test stopping a stream when not active."""
        url = reverse('channel-stop-stream', kwargs={'pk': self.channel.pk})
        response = self.client.post(url)
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('not active', response.data['error'].lower())
    
    def test_channel_statistics(self):
        """Test channel statistics endpoint."""
        # Create some test data
        session = StreamSession.objects.create(
            channel=self.channel,
            status='completed',
            started_at=timezone.now() - timedelta(hours=2),
            ended_at=timezone.now() - timedelta(hours=1)
        )
        
        url = reverse('channel-statistics', kwargs={'pk': self.channel.pk})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('total_sessions', response.data)
        self.assertIn('active_sessions', response.data)
        self.assertIn('total_segments', response.data)


class StreamSessionAPITest(TestCase):
    """Test cases for StreamSession API endpoints."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        
        self.channel = Channel.objects.create(
            name='Test Channel',
            slug='test-channel',
            hls_url='https://example.com/stream.m3u8',
            output_directory='/tmp/test',
            created_by=self.user
        )
        
        self.session = StreamSession.objects.create(
            channel=self.channel,
            status='active',
            started_at=timezone.now()
        )
    
    def test_list_sessions(self):
        """Test listing stream sessions."""
        url = reverse('streamsession-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['channel'], self.channel.id)
    
    def test_session_detail(self):
        """Test retrieving session details."""
        url = reverse('streamsession-detail', kwargs={'pk': self.session.pk})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['id'], str(self.session.id))
        self.assertEqual(response.data['status'], 'active')
    
    def test_filter_sessions_by_status(self):
        """Test filtering sessions by status."""
        # Create additional session with different status
        StreamSession.objects.create(
            channel=self.channel,
            status='completed',
            started_at=timezone.now() - timedelta(hours=2),
            ended_at=timezone.now() - timedelta(hours=1)
        )
        
        url = reverse('streamsession-list')
        response = self.client.get(url, {'status': 'active'})
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['status'], 'active')
    
    def test_filter_sessions_by_channel(self):
        """Test filtering sessions by channel."""
        # Create another channel and session
        other_channel = Channel.objects.create(
            name='Other Channel',
            slug='other-channel',
            hls_url='https://example.com/other-stream.m3u8',
            output_directory='/tmp/other',
            created_by=self.user
        )
        
        StreamSession.objects.create(
            channel=other_channel,
            status='active',
            started_at=timezone.now()
        )
        
        url = reverse('streamsession-list')
        response = self.client.get(url, {'channel': self.channel.id})
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['channel'], self.channel.id)


class ConfigurationAPITest(TestCase):
    """Test cases for Video and Audio Configuration API endpoints."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
    
    def test_create_video_configuration(self):
        """Test creating video configuration via API."""
        url = reverse('videoconfiguration-list')
        data = {
            'name': 'Test Video Config',
            'codec': 'h264',
            'bitrate': 1500000,
            'width': 1280,
            'height': 720,
            'framerate': 25.0
        }
        
        response = self.client.post(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'Test Video Config')
        self.assertEqual(response.data['created_by'], self.user.id)
    
    def test_create_audio_configuration(self):
        """Test creating audio configuration via API."""
        url = reverse('audioconfiguration-list')
        data = {
            'name': 'Test Audio Config',
            'codec': 'aac',
            'bitrate': 96000,
            'sample_rate': 44100,
            'channels': 2
        }
        
        response = self.client.post(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'Test Audio Config')
        self.assertEqual(response.data['created_by'], self.user.id)
    
    def test_unauthorized_access(self):
        """Test API access without authentication."""
        self.client.force_authenticate(user=None)
        
        url = reverse('channel-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class ThrottlingTest(TestCase):
    """Test cases for API rate limiting."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        
        self.channel = Channel.objects.create(
            name='Test Channel',
            slug='test-channel',
            hls_url='https://example.com/stream.m3u8',
            output_directory='/tmp/test',
            created_by=self.user
        )
    
    @patch('apps.streams.tasks.start_stream_capture.delay')
    def test_stream_control_throttling(self, mock_task):
        """Test rate limiting on stream control actions."""
        mock_task.return_value = MagicMock(id='test-task-id')
        
        url = reverse('channel-start-stream', kwargs={'pk': self.channel.pk})
        
        # First request should succeed
        response = self.client.post(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        
        # Rapid subsequent requests should be throttled
        # Note: In real testing, you'd need to configure test throttle rates
        # or use time manipulation to test throttling behavior
    
    def test_api_throttling_headers(self):
        """Test that throttling headers are present."""
        url = reverse('channel-list')
        response = self.client.get(url)
        
        # Check for throttling headers (may not be present if under limit)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # Headers like X-RateLimit-Limit, X-RateLimit-Remaining would be checked
        # if throttling is active
