"""Advertisers Tests

This module contains comprehensive tests for the advertisers app.
Includes tests for models, views, API endpoints, and business logic.

Test Classes:
    - AgencyModelTest: Tests for Agency model
    - BrandModelTest: Tests for Brand model
    - BrandCategoryModelTest: Tests for BrandCategory model
    - UserAdvertiserModelTest: Tests for UserAdvertiser model
    - AgencyViewTest: Tests for Agency views
    - BrandViewTest: Tests for Brand views
    - AgencyAPITest: Tests for Agency API endpoints
    - BrandAPITest: Tests for Brand API endpoints
"""

import json
from decimal import Decimal
from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from django.utils import timezone
from datetime import timedelta

from .models import Agency, Brand, BrandCategory, UserAdvertiser
from .serializers import AgencySerializer, BrandSerializer


User = get_user_model()


class BaseTestCase(TestCase):
    """Base test case with common setup for all advertiser tests."""
    
    def setUp(self):
        """Set up test data."""
        # Create test users
        self.user1 = User.objects.create_user(
            username='testuser1',
            email='test1@example.com',
            password='testpass123',
            first_name='Test',
            last_name='User1'
        )
        
        self.user2 = User.objects.create_user(
            username='testuser2',
            email='test2@example.com',
            password='testpass123',
            first_name='Test',
            last_name='User2'
        )
        
        self.superuser = User.objects.create_superuser(
            username='admin',
            email='admin@example.com',
            password='adminpass123'
        )
        
        # Create test brand categories
        self.category1 = BrandCategory.objects.create(
            name='Technology',
            description='Tech companies and software services'
        )
        
        self.category2 = BrandCategory.objects.create(
            name='Automotive',
            description='Car manufacturers and automotive services'
        )
        
        # Create subcategory
        self.subcategory = BrandCategory.objects.create(
            name='Software',
            description='Software development companies',
            parent=self.category1
        )
        
        # Create test agencies
        self.agency1 = Agency.objects.create(
            name='Test Agency 1',
            description='First test agency',
            email='agency1@example.com',
            phone='+1234567890',
            website='https://agency1.com',
            city='New York',
            country='USA',
            contact_person='John Doe',
            owner=self.user1
        )
        
        self.agency2 = Agency.objects.create(
            name='Test Agency 2',
            description='Second test agency',
            email='agency2@example.com',
            phone='+0987654321',
            city='Los Angeles',
            country='USA',
            contact_person='Jane Smith',
            owner=self.user2
        )
        
        # Create test brands
        self.brand1 = Brand.objects.create(
            name='Test Brand 1',
            description='First test brand',
            category=self.category1,
            agency=self.agency1,
            industry='Technology',
            target_audience='Tech enthusiasts',
            annual_budget=Decimal('100000.00'),
            contact_email='brand1@example.com',
            website='https://brand1.com'
        )
        
        self.brand2 = Brand.objects.create(
            name='Test Brand 2',
            description='Second test brand',
            category=self.category2,
            agency=self.agency2,
            industry='Automotive',
            target_audience='Car buyers',
            annual_budget=Decimal('200000.00'),
            contact_email='brand2@example.com'
        )


# ============================================================================
# Model Tests
# ============================================================================

class AgencyModelTest(BaseTestCase):
    """Test cases for Agency model."""
    
    def test_agency_creation(self):
        """Test agency creation with all fields."""
        agency = Agency.objects.create(
            name='New Agency',
            description='A new test agency',
            email='new@example.com',
            phone='+1111111111',
            website='https://newagency.com',
            address='123 Main St',
            city='Chicago',
            country='USA',
            contact_person='Bob Johnson',
            owner=self.user1
        )
        
        self.assertEqual(agency.name, 'New Agency')
        self.assertEqual(agency.owner, self.user1)
        self.assertEqual(agency.status, 'active')  # Default status
        self.assertTrue(agency.created_at)
        self.assertTrue(agency.updated_at)
    
    def test_agency_str_representation(self):
        """Test agency string representation."""
        self.assertEqual(str(self.agency1), 'Test Agency 1')
    
    def test_agency_total_brands_property(self):
        """Test total_brands property."""
        # Initially should have 1 brand
        self.assertEqual(self.agency1.total_brands, 1)
        
        # Add another brand
        Brand.objects.create(
            name='Another Brand',
            agency=self.agency1,
            category=self.category1
        )
        
        # Should now have 2 brands
        self.assertEqual(self.agency1.total_brands, 2)
    
    def test_agency_email_uniqueness(self):
        """Test that agency emails must be unique."""
        with self.assertRaises(Exception):
            Agency.objects.create(
                name='Duplicate Email Agency',
                email='agency1@example.com',  # Same as agency1
                owner=self.user2
            )


class BrandModelTest(BaseTestCase):
    """Test cases for Brand model."""
    
    def test_brand_creation(self):
        """Test brand creation with all fields."""
        brand = Brand.objects.create(
            name='New Brand',
            description='A new test brand',
            category=self.category1,
            agency=self.agency1,
            industry='Entertainment',
            target_audience='Young adults',
            annual_budget=Decimal('50000.00'),
            contact_email='newbrand@example.com',
            website='https://newbrand.com'
        )
        
        self.assertEqual(brand.name, 'New Brand')
        self.assertEqual(brand.agency, self.agency1)
        self.assertEqual(brand.category, self.category1)
        self.assertEqual(brand.status, 'active')  # Default status
    
    def test_brand_str_representation(self):
        """Test brand string representation."""
        expected = f"{self.brand1.name} ({self.brand1.agency.name})"
        self.assertEqual(str(self.brand1), expected)
    
    def test_brand_unique_constraint(self):
        """Test unique constraint for brand name per agency."""
        with self.assertRaises(Exception):
            Brand.objects.create(
                name='Test Brand 1',  # Same name as existing brand in same agency
                agency=self.agency1,
                category=self.category1
            )
    
    def test_brand_total_budget_spent_property(self):
        """Test total_budget_spent property."""
        # Initially should be 0 (no campaigns)
        self.assertEqual(self.brand1.total_budget_spent, 0)


class BrandCategoryModelTest(BaseTestCase):
    """Test cases for BrandCategory model."""
    
    def test_category_creation(self):
        """Test category creation."""
        category = BrandCategory.objects.create(
            name='Finance',
            description='Financial services and banking'
        )
        
        self.assertEqual(category.name, 'Finance')
        self.assertIsNone(category.parent)
    
    def test_category_hierarchy(self):
        """Test category parent-child relationship."""
        self.assertEqual(self.subcategory.parent, self.category1)
        self.assertIn(self.subcategory, self.category1.subcategories.all())
    
    def test_category_str_representation(self):
        """Test category string representation."""
        # Root category
        self.assertEqual(str(self.category1), 'Technology')
        
        # Subcategory
        expected = f"{self.category1.name} > {self.subcategory.name}"
        self.assertEqual(str(self.subcategory), expected)


class UserAdvertiserModelTest(BaseTestCase):
    """Test cases for UserAdvertiser model."""
    
    def test_user_advertiser_creation(self):
        """Test user-advertiser relationship creation."""
        relationship = UserAdvertiser.objects.create(
            user=self.user2,
            brand=self.brand1,
            role='editor',
            permissions={'allowed': ['view_campaigns', 'edit_campaigns']}
        )
        
        self.assertEqual(relationship.user, self.user2)
        self.assertEqual(relationship.brand, self.brand1)
        self.assertEqual(relationship.role, 'editor')
        self.assertTrue(relationship.is_active)
    
    def test_user_advertiser_unique_constraint(self):
        """Test unique constraint for user-brand relationship."""
        UserAdvertiser.objects.create(
            user=self.user2,
            brand=self.brand1,
            role='viewer'
        )
        
        with self.assertRaises(Exception):
            UserAdvertiser.objects.create(
                user=self.user2,
                brand=self.brand1,  # Same user-brand combination
                role='editor'
            )
    
    def test_has_permission_method(self):
        """Test has_permission method."""
        # Admin role should have all permissions
        admin_relationship = UserAdvertiser.objects.create(
            user=self.user2,
            brand=self.brand1,
            role='admin'
        )
        self.assertTrue(admin_relationship.has_permission('any_permission'))
        
        # Editor with specific permissions
        editor_relationship = UserAdvertiser.objects.create(
            user=self.user1,
            brand=self.brand2,
            role='editor',
            permissions={'allowed': ['view_campaigns', 'edit_campaigns']}
        )
        self.assertTrue(editor_relationship.has_permission('view_campaigns'))
        self.assertFalse(editor_relationship.has_permission('delete_campaigns'))


# ============================================================================
# View Tests
# ============================================================================

class AgencyViewTest(BaseTestCase):
    """Test cases for Agency views."""
    
    def setUp(self):
        super().setUp()
        self.client = Client()
    
    def test_agency_list_view_authenticated(self):
        """Test agency list view for authenticated users."""
        self.client.login(username='testuser1', password='testpass123')
        response = self.client.get(reverse('advertisers:agency_list'))
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Test Agency 1')
    
    def test_agency_list_view_unauthenticated(self):
        """Test agency list view redirects for unauthenticated users."""
        response = self.client.get(reverse('advertisers:agency_list'))
        self.assertEqual(response.status_code, 302)  # Redirect to login
    
    def test_agency_detail_view(self):
        """Test agency detail view."""
        self.client.login(username='testuser1', password='testpass123')
        response = self.client.get(
            reverse('advertisers:agency_detail', kwargs={'agency_id': self.agency1.id})
        )
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, self.agency1.name)
        self.assertContains(response, self.agency1.description)
    
    def test_agency_detail_view_permission_denied(self):
        """Test agency detail view with insufficient permissions."""
        self.client.login(username='testuser2', password='testpass123')
        response = self.client.get(
            reverse('advertisers:agency_detail', kwargs={'agency_id': self.agency1.id})
        )
        
        # Should redirect due to lack of permissions
        self.assertEqual(response.status_code, 302)


class BrandViewTest(BaseTestCase):
    """Test cases for Brand views."""
    
    def setUp(self):
        super().setUp()
        self.client = Client()
    
    def test_brand_list_view(self):
        """Test brand list view."""
        self.client.login(username='testuser1', password='testpass123')
        response = self.client.get(reverse('advertisers:brand_list'))
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Test Brand 1')
    
    def test_brand_detail_view(self):
        """Test brand detail view."""
        self.client.login(username='testuser1', password='testpass123')
        response = self.client.get(
            reverse('advertisers:brand_detail', kwargs={'brand_id': self.brand1.id})
        )
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, self.brand1.name)
        self.assertContains(response, self.brand1.description)
    
    def test_get_agency_brands_ajax(self):
        """Test AJAX endpoint for getting agency brands."""
        self.client.login(username='testuser1', password='testpass123')
        response = self.client.get(
            reverse('advertisers:ajax_agency_brands', kwargs={'agency_id': self.agency1.id})
        )
        
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn('brands', data)
        self.assertEqual(len(data['brands']), 1)
        self.assertEqual(data['brands'][0]['name'], 'Test Brand 1')


# ============================================================================
# API Tests
# ============================================================================

class AgencyAPITest(APITestCase):
    """Test cases for Agency API endpoints."""
    
    def setUp(self):
        """Set up test data for API tests."""
        # Create test users
        self.user1 = User.objects.create_user(
            username='apiuser1',
            email='api1@example.com',
            password='apipass123'
        )
        
        self.user2 = User.objects.create_user(
            username='apiuser2',
            email='api2@example.com',
            password='apipass123'
        )
        
        # Create test agency
        self.agency = Agency.objects.create(
            name='API Test Agency',
            description='Agency for API testing',
            email='apiagency@example.com',
            owner=self.user1
        )
        
        self.client = APIClient()
    
    def test_agency_list_api_authenticated(self):
        """Test agency list API for authenticated users."""
        self.client.force_authenticate(user=self.user1)
        response = self.client.get('/advertisers/api/agencies/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], 'API Test Agency')
    
    def test_agency_list_api_unauthenticated(self):
        """Test agency list API for unauthenticated users."""
        response = self.client.get('/advertisers/api/agencies/')
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
    
    def test_agency_create_api(self):
        """Test agency creation via API."""
        self.client.force_authenticate(user=self.user2)
        
        data = {
            'name': 'New API Agency',
            'description': 'Created via API',
            'email': 'newapi@example.com',
            'phone': '+1234567890',
            'city': 'San Francisco',
            'country': 'USA'
        }
        
        response = self.client.post('/advertisers/api/agencies/', data)
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'New API Agency')
        self.assertEqual(response.data['owner'], self.user2.id)
        
        # Verify agency was created in database
        agency = Agency.objects.get(name='New API Agency')
        self.assertEqual(agency.owner, self.user2)
    
    def test_agency_update_api(self):
        """Test agency update via API."""
        self.client.force_authenticate(user=self.user1)
        
        data = {
            'name': 'Updated API Agency',
            'description': 'Updated via API',
            'email': 'updated@example.com'
        }
        
        response = self.client.patch(f'/advertisers/api/agencies/{self.agency.id}/', data)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['name'], 'Updated API Agency')
        
        # Verify agency was updated in database
        self.agency.refresh_from_db()
        self.assertEqual(self.agency.name, 'Updated API Agency')
    
    def test_agency_delete_api(self):
        """Test agency deletion via API."""
        self.client.force_authenticate(user=self.user1)
        
        response = self.client.delete(f'/advertisers/api/agencies/{self.agency.id}/')
        
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        
        # Verify agency was deleted
        self.assertFalse(Agency.objects.filter(id=self.agency.id).exists())
    
    def test_agency_stats_api(self):
        """Test agency statistics API endpoint."""
        self.client.force_authenticate(user=self.user1)
        
        response = self.client.get(f'/advertisers/api/agencies/{self.agency.id}/stats/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('total_brands', response.data)
        self.assertIn('total_campaigns', response.data)
        self.assertIn('active_campaigns', response.data)
        self.assertIn('total_budget', response.data)


class BrandAPITest(APITestCase):
    """Test cases for Brand API endpoints."""
    
    def setUp(self):
        """Set up test data for API tests."""
        # Create test user and agency
        self.user = User.objects.create_user(
            username='branduser',
            email='brand@example.com',
            password='brandpass123'
        )
        
        self.agency = Agency.objects.create(
            name='Brand Test Agency',
            email='brandagency@example.com',
            owner=self.user
        )
        
        self.category = BrandCategory.objects.create(
            name='Test Category',
            description='Category for testing'
        )
        
        self.brand = Brand.objects.create(
            name='API Test Brand',
            description='Brand for API testing',
            agency=self.agency,
            category=self.category,
            annual_budget=Decimal('75000.00')
        )
        
        self.client = APIClient()
    
    def test_brand_list_api(self):
        """Test brand list API."""
        self.client.force_authenticate(user=self.user)
        response = self.client.get('/advertisers/api/brands/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], 'API Test Brand')
    
    def test_brand_create_api(self):
        """Test brand creation via API."""
        self.client.force_authenticate(user=self.user)
        
        data = {
            'name': 'New API Brand',
            'description': 'Created via API',
            'agency': self.agency.id,
            'category': self.category.id,
            'industry': 'Technology',
            'annual_budget': '50000.00'
        }
        
        response = self.client.post('/advertisers/api/brands/', data)
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'New API Brand')
        
        # Verify brand was created in database
        brand = Brand.objects.get(name='New API Brand')
        self.assertEqual(brand.agency, self.agency)
    
    def test_brand_stats_api(self):
        """Test brand statistics API endpoint."""
        self.client.force_authenticate(user=self.user)
        
        response = self.client.get(f'/advertisers/api/brands/{self.brand.id}/stats/')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('total_campaigns', response.data)
        self.assertIn('active_campaigns', response.data)
        self.assertIn('total_budget_spent', response.data)
        self.assertIn('annual_budget', response.data)


# ============================================================================
# Serializer Tests
# ============================================================================

class SerializerTest(BaseTestCase):
    """Test cases for serializers."""
    
    def test_agency_serializer(self):
        """Test AgencySerializer."""
        serializer = AgencySerializer(instance=self.agency1)
        data = serializer.data
        
        self.assertEqual(data['name'], self.agency1.name)
        self.assertEqual(data['email'], self.agency1.email)
        self.assertIn('brands_count', data)
        self.assertIn('campaigns_count', data)
    
    def test_brand_serializer(self):
        """Test BrandSerializer."""
        serializer = BrandSerializer(instance=self.brand1)
        data = serializer.data
        
        self.assertEqual(data['name'], self.brand1.name)
        self.assertEqual(data['agency'], self.brand1.agency.id)
        self.assertEqual(data['category'], self.brand1.category.id)
        self.assertIn('campaigns_count', data)
    
    def test_agency_serializer_validation(self):
        """Test AgencySerializer validation."""
        # Test duplicate email validation
        data = {
            'name': 'Test Agency',
            'email': 'agency1@example.com',  # Duplicate email
            'owner': self.user2.id
        }
        
        serializer = AgencySerializer(data=data)
        self.assertFalse(serializer.is_valid())
        self.assertIn('email', serializer.errors)
    
    def test_brand_serializer_validation(self):
        """Test BrandSerializer validation."""
        # Test duplicate name per agency validation
        data = {
            'name': 'Test Brand 1',  # Duplicate name in same agency
            'agency': self.agency1.id,
            'category': self.category1.id
        }
        
        serializer = BrandSerializer(data=data)
        self.assertFalse(serializer.is_valid())
        self.assertIn('name', serializer.errors)


# ============================================================================
# Integration Tests
# ============================================================================

class IntegrationTest(BaseTestCase):
    """Integration tests for complex workflows."""
    
    def test_complete_agency_brand_workflow(self):
        """Test complete workflow from agency creation to brand management."""
        client = APIClient()
        client.force_authenticate(user=self.user1)
        
        # 1. Create agency
        agency_data = {
            'name': 'Integration Test Agency',
            'description': 'Agency for integration testing',
            'email': 'integration@example.com'
        }
        
        agency_response = client.post('/advertisers/api/agencies/', agency_data)
        self.assertEqual(agency_response.status_code, status.HTTP_201_CREATED)
        agency_id = agency_response.data['id']
        
        # 2. Create brand for the agency
        brand_data = {
            'name': 'Integration Test Brand',
            'description': 'Brand for integration testing',
            'agency': agency_id,
            'category': self.category1.id,
            'industry': 'Technology'
        }
        
        brand_response = client.post('/advertisers/api/brands/', brand_data)
        self.assertEqual(brand_response.status_code, status.HTTP_201_CREATED)
        brand_id = brand_response.data['id']
        
        # 3. Create user-brand relationship
        relationship_data = {
            'user': self.user2.id,
            'brand': brand_id,
            'role': 'editor',
            'permissions': {'allowed': ['view_campaigns', 'edit_campaigns']}
        }
        
        relationship_response = client.post('/advertisers/api/user-relationships/', relationship_data)
        self.assertEqual(relationship_response.status_code, status.HTTP_201_CREATED)
        
        # 4. Verify the complete setup
        agency = Agency.objects.get(id=agency_id)
        brand = Brand.objects.get(id=brand_id)
        relationship = UserAdvertiser.objects.get(user=self.user2, brand=brand)
        
        self.assertEqual(agency.owner, self.user1)
        self.assertEqual(brand.agency, agency)
        self.assertEqual(relationship.role, 'editor')
        self.assertTrue(relationship.has_permission('view_campaigns'))
        
        # 5. Test agency statistics
        stats_response = client.get(f'/advertisers/api/agencies/{agency_id}/stats/')
        self.assertEqual(stats_response.status_code, status.HTTP_200_OK)
        self.assertEqual(stats_response.data['total_brands'], 1)