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.core import mail
from .models import (
    Event, Speaker, Agenda, CallForProposal,
    Gallery, Registration, UserProfile, Role, Proposal
)
from .forms import (
    UserRegistrationForm, UserProfileForm,
    ProposalForm, EventSearchForm
)

class ModelTests(TestCase):
    def setUp(self):
        # Create test users
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123',
            email='test@example.com'
        )
        self.admin_user = User.objects.create_user(
            username='adminuser',
            password='admin123',
            email='admin@example.com',
            is_staff=True
        )
        
        # Create roles
        self.attendee_role = Role.objects.create(name='Attendee')
        self.speaker_role = Role.objects.create(name='Speaker')
        
        # Create user profiles
        self.profile = UserProfile.objects.create(
            user=self.user,
            role=self.attendee_role,
            bio='Test bio',
            organization='Test Org',
            phone_number='+260123456789'
        )
        self.admin_profile = UserProfile.objects.create(
            user=self.admin_user,
            role=self.speaker_role,
            bio='Admin bio',
            organization='Admin Org'
        )
        
        # Create event with future date
        self.event = Event.objects.create(
            title='Test Event',
            description='Test Description',
            date_time=timezone.now() + timezone.timedelta(days=30),
            venue='Test Venue',
            status='upcoming'
        )
        
        # Create speaker with enhanced fields
        self.speaker = Speaker.objects.create(
            name='Test Speaker',
            bio='Speaker Bio',
            organization='Speaker Org',
            website='https://example.com',
            social_media={'twitter': '@testspeaker'}
        )
        self.speaker.events.add(self.event)
        
        # Create Call for Proposal
        self.cfp = CallForProposal.objects.create(
            event=self.event,
            title='Test CFP',
            description='Test CFP Description',
            submission_deadline=timezone.now() + timezone.timedelta(days=30),
            status='open',
            created_by=self.admin_user
        )

    def test_user_profile_creation(self):
        self.assertEqual(self.profile.user.username, 'testuser')
        self.assertEqual(self.profile.role.name, 'Attendee')
        self.assertEqual(self.profile.bio, 'Test bio')
        self.assertEqual(self.profile.phone_number, '+260123456789')
        self.assertIsNotNone(self.profile.created_at)

    def test_event_creation(self):
        self.assertEqual(self.event.title, 'Test Event')
        self.assertEqual(self.event.status, 'upcoming')
        self.assertTrue(self.event.can_register())

    def test_speaker_creation(self):
        self.assertEqual(self.speaker.name, 'Test Speaker')
        self.assertEqual(self.speaker.organization, 'Speaker Org')
        self.assertEqual(self.speaker.website, 'https://example.com')
        self.assertEqual(self.speaker.social_media['twitter'], '@testspeaker')
        self.assertIn(self.event, self.speaker.events.all())

    def test_registration(self):
        registration = Registration.objects.create(
            event=self.event,
            user=self.user,
            notes='Test registration'
        )
        self.assertEqual(registration.event, self.event)
        self.assertEqual(registration.user, self.user)
        self.assertIsNotNone(registration.registration_date)
        self.assertIsNone(registration.attendance_date)

    def test_call_for_proposal(self):
        self.assertEqual(self.cfp.title, 'Test CFP')
        self.assertEqual(self.cfp.status, 'open')
        self.assertEqual(self.cfp.created_by, self.admin_user)
        self.assertTrue(self.cfp.is_open())

    def test_proposal_submission(self):
        proposal = Proposal.objects.create(
            cfp=self.cfp,
            title='Test Proposal',
            abstract='Test Abstract',
            presentation_type='talk',
            submitter=self.user
        )
        self.assertEqual(proposal.status, 'submitted')
        self.assertEqual(proposal.cfp, self.cfp)
        self.assertIsNotNone(proposal.submitted_at)

class FormTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123',
            email='test@example.com'
        )
        self.event = Event.objects.create(
            title='Test Event',
            description='Test Description',
            date_time=timezone.now(),
            venue='Test Venue',
            status='upcoming'
        )
        self.cfp = CallForProposal.objects.create(
            event=self.event,
            title='Test CFP',
            description='Test CFP Description',
            submission_deadline=timezone.now() + timezone.timedelta(days=30),
            status='open',
            created_by=self.user
        )
        self.user_data = {
            'username': 'newuser',
            'email': 'new@example.com',
            'password1': 'complex_password123',
            'password2': 'complex_password123',
            'first_name': 'John',
            'last_name': 'Doe'
        }

    def test_user_registration_form(self):
        form = UserRegistrationForm(data=self.user_data)
        self.assertTrue(form.is_valid())

    def test_user_registration_form_invalid(self):
        # Test with mismatched passwords
        data = self.user_data.copy()
        data['password2'] = 'different_password'
        form = UserRegistrationForm(data=data)
        self.assertFalse(form.is_valid())

    def test_event_search_form(self):
        data = {
            'query': 'test event',
            'date_from': '2025-01-01',
            'date_to': '2025-12-31',
            'status': 'upcoming'
        }
        form = EventSearchForm(data=data)
        self.assertTrue(form.is_valid())

    def test_proposal_form(self):
        data = {
            'title': 'Test Proposal',
            'abstract': 'Test Abstract',
            'presentation_type': 'talk',
            'additional_notes': 'Test notes'
        }
        form = ProposalForm(data=data, cfp=self.cfp, user=self.user)
        self.assertTrue(form.is_valid())

    def test_proposal_form_invalid(self):
        # Test with missing required fields
        data = {
            'title': '',  # Required field
            'abstract': 'Test Abstract',
            'presentation_type': 'talk'
        }
        form = ProposalForm(data=data, cfp=self.cfp, user=self.user)
        self.assertFalse(form.is_valid())
        self.assertIn('title', form.errors)

class ViewTests(TestCase):
    def setUp(self):
        self.client = Client()
        # Create users
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123',
            email='test@example.com'
        )
        self.admin_user = User.objects.create_user(
            username='adminuser',
            password='admin123',
            email='admin@example.com',
            is_staff=True
        )
        
        # Create role
        self.role = Role.objects.create(name='Attendee')
        
        # Create user profile
        self.profile = UserProfile.objects.create(
            user=self.user,
            role=self.role,
            bio='Test bio',
            organization='Test Org'
        )
        
        # Create event with future date
        self.event = Event.objects.create(
            title='Test Event',
            description='Test Description',
            date_time=timezone.now() + timezone.timedelta(days=7),
            venue='Test Venue',
            status='upcoming'
        )
        
        # Create CFP
        self.cfp = CallForProposal.objects.create(
            event=self.event,
            title='Test CFP',
            description='Test CFP Description',
            submission_deadline=timezone.now() + timezone.timedelta(days=30),
            status='open',
            created_by=self.admin_user
        )

    def test_event_list_view(self):
        response = self.client.get(reverse('events:event_list'))
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'events/event_list.html')
        self.assertContains(response, 'Test Event')

    def test_event_detail_view(self):
        response = self.client.get(
            reverse('events:event_detail', args=[self.event.id])
        )
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'events/event_detail.html')
        self.assertContains(response, self.event.title)

    def test_user_registration(self):
        response = self.client.post(
            reverse('events:register'),
            {
                'username': 'newuser',
                'email': 'new@example.com',
                'password1': 'complex_password123',
                'password2': 'complex_password123',
                'first_name': 'John',
                'last_name': 'Doe'
            }
        )
        self.assertEqual(response.status_code, 302)  # Redirect after success
        user = User.objects.get(username='newuser')
        self.assertTrue(user)
        self.assertEqual(user.email, 'new@example.com')
        self.assertTrue(UserProfile.objects.filter(user=user).exists())

    def test_user_login(self):
        response = self.client.post(
            reverse('events:login'),
            {'username': 'testuser', 'password': 'testpass123'}
        )
        self.assertEqual(response.status_code, 302)  # Redirect after success

    def test_protected_views(self):
        # Test accessing protected view without login
        response = self.client.get(reverse('events:user_profile'))
        self.assertEqual(response.status_code, 302)  # Redirect to login
        
        # Test accessing protected view with login
        self.client.login(username='testuser', password='testpass123')
        response = self.client.get(reverse('events:user_profile'))
        self.assertEqual(response.status_code, 200)

    def test_cfp_views(self):
        # Test CFP list view
        response = self.client.get(reverse('events:cfp_list'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Test CFP')

        # Test CFP detail view
        response = self.client.get(
            reverse('events:cfp_detail', args=[self.cfp.id])
        )
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, self.cfp.title)

    def test_proposal_submission(self):
        self.client.login(username='testuser', password='testpass123')
        response = self.client.post(
            reverse('events:submit_proposal', args=[self.cfp.id]),
            {
                'title': 'Test Proposal',
                'abstract': 'Test Abstract',
                'presentation_type': 'talk',
                'additional_notes': 'Test notes'
            }
        )
        self.assertEqual(response.status_code, 302)  # Redirect after success
        self.assertTrue(Proposal.objects.filter(title='Test Proposal').exists())

    def test_password_reset(self):
        response = self.client.post(
            reverse('events:password_reset'),
            {'email': 'test@example.com'}
        )
        self.assertEqual(response.status_code, 302)
        self.assertEqual(len(mail.outbox), 1)  # Check if email was sent

class SearchTests(TestCase):
    def setUp(self):
        self.client = Client()
        base_date = timezone.now()
        
        # Create test events with aware datetime
        self.python_event = Event.objects.create(
            title='Python Workshop',
            description='Learn Python',
            date_time=base_date + timezone.timedelta(days=14),
            venue='Room A',
            status='upcoming'
        )
        self.django_event = Event.objects.create(
            title='Django Meetup',
            description='Django Discussion',
            date_time=base_date + timezone.timedelta(days=21),
            venue='Room B',
            status='upcoming'
        )

    def test_search_functionality(self):
        response = self.client.get(
            reverse('events:search'),
            {'query': 'Python'}
        )
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Python Workshop')
        self.assertNotContains(response, 'Django Meetup')

    def test_date_range_search(self):
        # Test date range search with timezone-aware dates
        base_date = timezone.now()
        mid_date = base_date + timezone.timedelta(days=15)
        end_date = base_date + timezone.timedelta(days=30)
        
        response = self.client.get(
            reverse('events:search'),
            {
                'date_from': mid_date.date().isoformat(),
                'date_to': end_date.date().isoformat(),
                'status': 'upcoming'
            }
        )
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, self.django_event.title)
        self.assertNotContains(response, self.python_event.title)

    def test_advanced_search(self):
        # Create events with specific dates for testing
        future_date = timezone.datetime(2025, 6, 1, tzinfo=timezone.get_current_timezone())
        Event.objects.create(
            title='Future Workshop',
            description='Future Event',
            date_time=future_date,
            venue='Room C',
            status='upcoming'
        )
        
        # Create a speaker for testing
        speaker = Speaker.objects.create(
            name='Test Speaker',
            bio='Expert in Python',
            organization='Tech Corp'
        )
        
        # Test date range search with timezone-aware dates
        start_date = timezone.datetime(2025, 1, 1, tzinfo=timezone.get_current_timezone())
        end_date = timezone.datetime(2025, 12, 31, tzinfo=timezone.get_current_timezone())
        
        response = self.client.get(
            reverse('events:search'),
            {
                'query': 'Future',
                'status': 'upcoming',
                'date_from': start_date.date().isoformat(),
                'date_to': end_date.date().isoformat()
            }
        )
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Future Workshop')
        
        # Test speaker search
        response = self.client.get(
            reverse('events:search'),
            {'query': 'Python'}
        )
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Test Speaker')
        self.assertContains(response, 'Expert in Python')
