# Adtlas TV Advertising Platform - Channel Views Tests
# Comprehensive tests for channel-related views and API endpoints

from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth.models import User
from django.utils import timezone
from django.http import JsonResponse
from decimal import Decimal
from datetime import datetime, timedelta
import json

from channels.models import (
    GeographicZone, BroadcastNetwork, TVChannel, 
    ChannelCoverage, ContentSchedule, AudienceDemographics
)
from channels.views import (
    ChannelListView, ChannelDetailView, NetworkListView, 
    NetworkDetailView, CoverageMapView
)


class ChannelListViewTest(TestCase):
    """
    Test cases for the ChannelListView.
    
    Tests channel listing functionality including pagination,
    filtering, searching, and sorting capabilities.
    """
    
    def setUp(self):
        """Set up test data for channel list view tests."""
        self.client = Client()
        
        # Create test networks
        self.network1 = BroadcastNetwork.objects.create(
            name='ABC Network',
            description='American Broadcasting Company',
            is_active=True
        )
        
        self.network2 = BroadcastNetwork.objects.create(
            name='CBS Network',
            description='Columbia Broadcasting System',
            is_active=True
        )
        
        # Create test channels
        self.channel1 = TVChannel.objects.create(
            name='WABC-TV',
            call_sign='WABC',
            frequency=Decimal('7.1'),
            network=self.network1,
            description='ABC affiliate in New York',
            is_active=True
        )
        
        self.channel2 = TVChannel.objects.create(
            name='WCBS-TV',
            call_sign='WCBS',
            frequency=Decimal('2.1'),
            network=self.network2,
            description='CBS affiliate in New York',
            is_active=True
        )
        
        self.channel3 = TVChannel.objects.create(
            name='WIND-TV',
            call_sign='WIND',
            frequency=Decimal('50.1'),
            network=None,  # Independent station
            description='Independent station',
            is_active=False  # Inactive channel
        )
        
        self.url = reverse('channels:channel_list')
    
    def test_channel_list_view_get(self):
        """Test GET request to channel list view."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'WABC-TV')
        self.assertContains(response, 'WCBS-TV')
        # Inactive channel should not appear by default
        self.assertNotContains(response, 'WIND-TV')
    
    def test_channel_list_pagination(self):
        """Test pagination functionality."""
        # Create additional channels to test pagination
        for i in range(15):
            TVChannel.objects.create(
                name=f'Test Channel {i}',
                call_sign=f'TEST{i}',
                frequency=Decimal(f'{i+10}.1'),
                is_active=True
            )
        
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, 200)
        
        # Check if pagination is working (assuming 10 items per page)
        self.assertContains(response, 'page')
        
        # Test second page
        response = self.client.get(self.url + '?page=2')
        self.assertEqual(response.status_code, 200)
    
    def test_channel_list_search(self):
        """Test search functionality."""
        # Search by channel name
        response = self.client.get(self.url + '?search=WABC')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'WABC-TV')
        self.assertNotContains(response, 'WCBS-TV')
        
        # Search by call sign
        response = self.client.get(self.url + '?search=WCBS')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'WCBS-TV')
        self.assertNotContains(response, 'WABC-TV')
    
    def test_channel_list_network_filter(self):
        """Test filtering by network."""
        response = self.client.get(self.url + f'?network={self.network1.id}')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'WABC-TV')
        self.assertNotContains(response, 'WCBS-TV')
    
    def test_channel_list_active_filter(self):
        """Test filtering by active status."""
        # Show all channels including inactive
        response = self.client.get(self.url + '?show_inactive=true')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'WABC-TV')
        self.assertContains(response, 'WCBS-TV')
        self.assertContains(response, 'WIND-TV')
    
    def test_channel_list_sorting(self):
        """Test sorting functionality."""
        # Sort by name
        response = self.client.get(self.url + '?sort=name')
        self.assertEqual(response.status_code, 200)
        
        # Sort by frequency
        response = self.client.get(self.url + '?sort=frequency')
        self.assertEqual(response.status_code, 200)
        
        # Sort by network
        response = self.client.get(self.url + '?sort=network')
        self.assertEqual(response.status_code, 200)


class ChannelDetailViewTest(TestCase):
    """
    Test cases for the ChannelDetailView.
    """
    
    def setUp(self):
        """Set up test data for channel detail view tests."""
        self.client = Client()
        
        self.network = BroadcastNetwork.objects.create(
            name='Test Network',
            is_active=True
        )
        
        self.channel = TVChannel.objects.create(
            name='Test Channel',
            call_sign='TEST',
            frequency=Decimal('10.1'),
            network=self.network,
            description='Test channel description',
            is_active=True
        )
        
        self.zone = GeographicZone.objects.create(
            name='Test Zone',
            zone_type='city',
            population=100000,
            is_active=True
        )
        
        # Create coverage data
        self.coverage = ChannelCoverage.objects.create(
            channel=self.channel,
            zone=self.zone,
            coverage_percentage=Decimal('85.0'),
            signal_strength='strong'
        )
        
        # Create schedule data
        self.schedule = ContentSchedule.objects.create(
            channel=self.channel,
            program_title='Test Program',
            start_time=timezone.now(),
            end_time=timezone.now() + timedelta(hours=1),
            description='Test program description',
            genre='entertainment'
        )
        
        # Create demographics data
        self.demographics = AudienceDemographics.objects.create(
            channel=self.channel,
            measurement_date=timezone.now().date(),
            total_viewers=150000,
            age_18_34=30000,
            age_35_54=60000,
            age_55_plus=60000,
            male_viewers=75000,
            female_viewers=75000,
            household_income_high=45000,
            household_income_medium=75000,
            household_income_low=30000
        )
        
        self.url = reverse('channels:channel_detail', kwargs={'pk': self.channel.pk})
    
    def test_channel_detail_view_get(self):
        """Test GET request to channel detail view."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, self.channel.name)
        self.assertContains(response, self.channel.call_sign)
        self.assertContains(response, self.channel.description)
        self.assertContains(response, self.network.name)
    
    def test_channel_detail_coverage_data(self):
        """Test that coverage data is displayed."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, self.zone.name)
        self.assertContains(response, '85.0%')  # Coverage percentage
        self.assertContains(response, 'strong')  # Signal strength
    
    def test_channel_detail_schedule_data(self):
        """Test that schedule data is displayed."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, self.schedule.program_title)
        self.assertContains(response, self.schedule.description)
    
    def test_channel_detail_demographics_data(self):
        """Test that demographics data is displayed."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '150,000')  # Total viewers
    
    def test_channel_detail_nonexistent(self):
        """Test accessing non-existent channel."""
        url = reverse('channels:channel_detail', kwargs={'pk': 99999})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 404)
    
    def test_channel_detail_inactive_channel(self):
        """Test accessing inactive channel."""
        self.channel.is_active = False
        self.channel.save()
        
        response = self.client.get(self.url)
        # Should still be accessible but might show warning
        self.assertEqual(response.status_code, 200)


class NetworkListViewTest(TestCase):
    """
    Test cases for the NetworkListView.
    """
    
    def setUp(self):
        """Set up test data for network list view tests."""
        self.client = Client()
        
        self.network1 = BroadcastNetwork.objects.create(
            name='ABC Network',
            description='American Broadcasting Company',
            website_url='https://abc.com',
            is_active=True
        )
        
        self.network2 = BroadcastNetwork.objects.create(
            name='CBS Network',
            description='Columbia Broadcasting System',
            website_url='https://cbs.com',
            is_active=True
        )
        
        self.network3 = BroadcastNetwork.objects.create(
            name='Inactive Network',
            description='This network is inactive',
            is_active=False
        )
        
        self.url = reverse('channels:network_list')
    
    def test_network_list_view_get(self):
        """Test GET request to network list view."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'ABC Network')
        self.assertContains(response, 'CBS Network')
        # Inactive network should not appear by default
        self.assertNotContains(response, 'Inactive Network')
    
    def test_network_list_search(self):
        """Test search functionality."""
        response = self.client.get(self.url + '?search=ABC')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'ABC Network')
        self.assertNotContains(response, 'CBS Network')
    
    def test_network_list_show_inactive(self):
        """Test showing inactive networks."""
        response = self.client.get(self.url + '?show_inactive=true')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'ABC Network')
        self.assertContains(response, 'CBS Network')
        self.assertContains(response, 'Inactive Network')


class NetworkDetailViewTest(TestCase):
    """
    Test cases for the NetworkDetailView.
    """
    
    def setUp(self):
        """Set up test data for network detail view tests."""
        self.client = Client()
        
        self.network = BroadcastNetwork.objects.create(
            name='Test Network',
            description='Test network description',
            website_url='https://testnetwork.com',
            headquarters_location='New York, NY',
            is_active=True
        )
        
        # Create channels for this network
        self.channel1 = TVChannel.objects.create(
            name='Test Channel 1',
            call_sign='TEST1',
            frequency=Decimal('10.1'),
            network=self.network,
            is_active=True
        )
        
        self.channel2 = TVChannel.objects.create(
            name='Test Channel 2',
            call_sign='TEST2',
            frequency=Decimal('20.1'),
            network=self.network,
            is_active=True
        )
        
        self.url = reverse('channels:network_detail', kwargs={'pk': self.network.pk})
    
    def test_network_detail_view_get(self):
        """Test GET request to network detail view."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, self.network.name)
        self.assertContains(response, self.network.description)
        self.assertContains(response, self.network.website_url)
        self.assertContains(response, self.network.headquarters_location)
    
    def test_network_detail_channels_list(self):
        """Test that network's channels are displayed."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Test Channel 1')
        self.assertContains(response, 'Test Channel 2')
        self.assertContains(response, 'TEST1')
        self.assertContains(response, 'TEST2')
    
    def test_network_detail_nonexistent(self):
        """Test accessing non-existent network."""
        url = reverse('channels:network_detail', kwargs={'pk': 99999})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 404)


class CoverageMapViewTest(TestCase):
    """
    Test cases for the CoverageMapView.
    """
    
    def setUp(self):
        """Set up test data for coverage map view tests."""
        self.client = Client()
        
        self.network = BroadcastNetwork.objects.create(
            name='Test Network',
            is_active=True
        )
        
        self.channel = TVChannel.objects.create(
            name='Test Channel',
            call_sign='TEST',
            frequency=Decimal('10.1'),
            network=self.network,
            is_active=True
        )
        
        self.zone1 = GeographicZone.objects.create(
            name='Zone 1',
            zone_type='city',
            population=100000,
            is_active=True
        )
        
        self.zone2 = GeographicZone.objects.create(
            name='Zone 2',
            zone_type='city',
            population=200000,
            is_active=True
        )
        
        # Create coverage data
        ChannelCoverage.objects.create(
            channel=self.channel,
            zone=self.zone1,
            coverage_percentage=Decimal('85.0'),
            signal_strength='strong'
        )
        
        ChannelCoverage.objects.create(
            channel=self.channel,
            zone=self.zone2,
            coverage_percentage=Decimal('70.0'),
            signal_strength='good'
        )
        
        self.url = reverse('channels:coverage_map')
    
    def test_coverage_map_view_get(self):
        """Test GET request to coverage map view."""
        response = self.client.get(self.url)
        
        self.assertEqual(response.status_code, 200)
        # Should contain map-related content
        self.assertContains(response, 'coverage')
    
    def test_coverage_map_with_channel_filter(self):
        """Test coverage map filtered by channel."""
        response = self.client.get(self.url + f'?channel={self.channel.id}')
        
        self.assertEqual(response.status_code, 200)
        # Should show coverage for the specific channel
    
    def test_coverage_map_with_zone_filter(self):
        """Test coverage map filtered by zone."""
        response = self.client.get(self.url + f'?zone={self.zone1.id}')
        
        self.assertEqual(response.status_code, 200)
        # Should show coverage in the specific zone


class ChannelAPIViewTest(TestCase):
    """
    Test cases for channel-related API endpoints.
    """
    
    def setUp(self):
        """Set up test data for API tests."""
        self.client = Client()
        
        self.network = BroadcastNetwork.objects.create(
            name='Test Network',
            is_active=True
        )
        
        self.channel = TVChannel.objects.create(
            name='Test Channel',
            call_sign='TEST',
            frequency=Decimal('10.1'),
            network=self.network,
            is_active=True
        )
        
        self.zone = GeographicZone.objects.create(
            name='Test Zone',
            zone_type='city',
            population=100000,
            is_active=True
        )
        
        # Create schedule data
        self.schedule = ContentSchedule.objects.create(
            channel=self.channel,
            program_title='Test Program',
            start_time=timezone.now(),
            end_time=timezone.now() + timedelta(hours=1),
            description='Test program',
            genre='entertainment'
        )
        
        # Create coverage data
        ChannelCoverage.objects.create(
            channel=self.channel,
            zone=self.zone,
            coverage_percentage=Decimal('85.0'),
            signal_strength='strong'
        )
    
    def test_channel_schedule_api(self):
        """Test the channel schedule API endpoint."""
        url = reverse('channels:channel_schedule_api', kwargs={'channel_id': self.channel.id})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/json')
        
        data = json.loads(response.content)
        self.assertIn('schedule', data)
        self.assertEqual(len(data['schedule']), 1)
        self.assertEqual(data['schedule'][0]['program_title'], 'Test Program')
    
    def test_channel_schedule_api_with_date_filter(self):
        """Test channel schedule API with date filtering."""
        url = reverse('channels:channel_schedule_api', kwargs={'channel_id': self.channel.id})
        today = timezone.now().date()
        response = self.client.get(url + f'?date={today}')
        
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn('schedule', data)
    
    def test_channel_schedule_api_nonexistent_channel(self):
        """Test channel schedule API with non-existent channel."""
        url = reverse('channels:channel_schedule_api', kwargs={'channel_id': 99999})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 404)
    
    def test_zone_channels_api(self):
        """Test the zone channels API endpoint."""
        url = reverse('channels:zone_channels_api', kwargs={'zone_id': self.zone.id})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/json')
        
        data = json.loads(response.content)
        self.assertIn('channels', data)
        self.assertEqual(len(data['channels']), 1)
        self.assertEqual(data['channels'][0]['name'], 'Test Channel')
    
    def test_zone_channels_api_with_signal_filter(self):
        """Test zone channels API with signal strength filtering."""
        url = reverse('channels:zone_channels_api', kwargs={'zone_id': self.zone.id})
        response = self.client.get(url + '?min_signal=good')
        
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn('channels', data)
    
    def test_zone_channels_api_nonexistent_zone(self):
        """Test zone channels API with non-existent zone."""
        url = reverse('channels:zone_channels_api', kwargs={'zone_id': 99999})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 404)
    
    def test_network_coverage_api(self):
        """Test the network coverage API endpoint."""
        url = reverse('channels:network_coverage_api', kwargs={'network_id': self.network.id})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/json')
        
        data = json.loads(response.content)
        self.assertIn('coverage', data)
        self.assertIn('total_channels', data)
        self.assertIn('total_zones', data)
    
    def test_network_coverage_api_nonexistent_network(self):
        """Test network coverage API with non-existent network."""
        url = reverse('channels:network_coverage_api', kwargs={'network_id': 99999})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 404)


class ViewPermissionTest(TestCase):
    """
    Test cases for view permissions and authentication.
    """
    
    def setUp(self):
        """Set up test data for permission tests."""
        self.client = Client()
        
        # Create test user
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.network = BroadcastNetwork.objects.create(
            name='Test Network',
            is_active=True
        )
        
        self.channel = TVChannel.objects.create(
            name='Test Channel',
            call_sign='TEST',
            frequency=Decimal('10.1'),
            network=self.network,
            is_active=True
        )
    
    def test_public_views_access(self):
        """Test that public views are accessible without authentication."""
        public_urls = [
            reverse('channels:channel_list'),
            reverse('channels:channel_detail', kwargs={'pk': self.channel.pk}),
            reverse('channels:network_list'),
            reverse('channels:network_detail', kwargs={'pk': self.network.pk}),
            reverse('channels:coverage_map'),
        ]
        
        for url in public_urls:
            response = self.client.get(url)
            self.assertIn(response.status_code, [200, 302])  # 200 OK or 302 redirect
    
    def test_api_endpoints_access(self):
        """Test that API endpoints are accessible."""
        api_urls = [
            reverse('channels:channel_schedule_api', kwargs={'channel_id': self.channel.id}),
            reverse('channels:network_coverage_api', kwargs={'network_id': self.network.id}),
        ]
        
        for url in api_urls:
            response = self.client.get(url)
            self.assertIn(response.status_code, [200, 401, 403])  # Various acceptable responses
    
    def test_authenticated_user_access(self):
        """Test access with authenticated user."""
        self.client.login(username='testuser', password='testpass123')
        
        response = self.client.get(reverse('channels:channel_list'))
        self.assertEqual(response.status_code, 200)
        
        response = self.client.get(reverse('channels:channel_detail', kwargs={'pk': self.channel.pk}))
        self.assertEqual(response.status_code, 200)


class ViewContextTest(TestCase):
    """
    Test cases for view context data.
    """
    
    def setUp(self):
        """Set up test data for context tests."""
        self.client = Client()
        
        self.network = BroadcastNetwork.objects.create(
            name='Test Network',
            is_active=True
        )
        
        self.channel = TVChannel.objects.create(
            name='Test Channel',
            call_sign='TEST',
            frequency=Decimal('10.1'),
            network=self.network,
            is_active=True
        )
    
    def test_channel_list_context(self):
        """Test that channel list view provides correct context."""
        response = self.client.get(reverse('channels:channel_list'))
        
        self.assertEqual(response.status_code, 200)
        self.assertIn('channels', response.context)
        self.assertIn('networks', response.context)
        self.assertIn('search_form', response.context)
    
    def test_channel_detail_context(self):
        """Test that channel detail view provides correct context."""
        response = self.client.get(reverse('channels:channel_detail', kwargs={'pk': self.channel.pk}))
        
        self.assertEqual(response.status_code, 200)
        self.assertIn('channel', response.context)
        self.assertEqual(response.context['channel'], self.channel)
    
    def test_network_list_context(self):
        """Test that network list view provides correct context."""
        response = self.client.get(reverse('channels:network_list'))
        
        self.assertEqual(response.status_code, 200)
        self.assertIn('networks', response.context)
    
    def test_network_detail_context(self):
        """Test that network detail view provides correct context."""
        response = self.client.get(reverse('channels:network_detail', kwargs={'pk': self.network.pk}))
        
        self.assertEqual(response.status_code, 200)
        self.assertIn('network', response.context)
        self.assertEqual(response.context['network'], self.network)
        self.assertIn('channels', response.context)


class ViewErrorHandlingTest(TestCase):
    """
    Test cases for view error handling.
    """
    
    def setUp(self):
        """Set up test data for error handling tests."""
        self.client = Client()
    
    def test_invalid_channel_id(self):
        """Test handling of invalid channel ID."""
        response = self.client.get(reverse('channels:channel_detail', kwargs={'pk': 99999}))
        self.assertEqual(response.status_code, 404)
    
    def test_invalid_network_id(self):
        """Test handling of invalid network ID."""
        response = self.client.get(reverse('channels:network_detail', kwargs={'pk': 99999}))
        self.assertEqual(response.status_code, 404)
    
    def test_invalid_api_parameters(self):
        """Test API endpoints with invalid parameters."""
        # Test with invalid channel ID
        response = self.client.get(reverse('channels:channel_schedule_api', kwargs={'channel_id': 99999}))
        self.assertEqual(response.status_code, 404)
        
        # Test with invalid zone ID
        response = self.client.get(reverse('channels:zone_channels_api', kwargs={'zone_id': 99999}))
        self.assertEqual(response.status_code, 404)
        
        # Test with invalid network ID
        response = self.client.get(reverse('channels:network_coverage_api', kwargs={'network_id': 99999}))
        self.assertEqual(response.status_code, 404)
    
    def test_malformed_query_parameters(self):
        """Test handling of malformed query parameters."""
        # Create a valid channel for testing
        network = BroadcastNetwork.objects.create(name='Test Network', is_active=True)
        channel = TVChannel.objects.create(
            name='Test Channel',
            call_sign='TEST',
            frequency=Decimal('10.1'),
            network=network,
            is_active=True
        )
        
        # Test with invalid date format
        url = reverse('channels:channel_schedule_api', kwargs={'channel_id': channel.id})
        response = self.client.get(url + '?date=invalid-date')
        # Should handle gracefully, either 400 or ignore invalid parameter
        self.assertIn(response.status_code, [200, 400])
        
        # Test with invalid page number
        response = self.client.get(reverse('channels:channel_list') + '?page=invalid')
        # Should handle gracefully, typically showing first page
        self.assertIn(response.status_code, [200, 404])