import io
from unittest.mock import patch

from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import Client, TestCase
from django.urls import reverse

from companies.models import Company
from jobs.category_data import sync_pezajob_categories
from jobs.models import JobCategory
from seekers.cv_library import filter_cv_records, index_seeker_cv, sync_all_seeker_cvs
from seekers.models import SeekerCVRecord, SeekerProfile
from subscriptions.models import CompanySubscription, SubscriptionPlan

User = get_user_model()


def _talent_cv_plan():
    plan, _ = SubscriptionPlan.objects.get_or_create(
        slug='talent-cv-basic',
        defaults={
            'name': 'CV Search Basic',
            'audience': SubscriptionPlan.Audience.COMPANY,
            'category': SubscriptionPlan.Category.TALENT_DATABASE,
            'price_monthly': 500,
            'features': 'CV library',
        },
    )
    plan.category = SubscriptionPlan.Category.TALENT_DATABASE
    plan.save(update_fields=['category'])
    return plan


def _recruitment_plan():
    plan, _ = SubscriptionPlan.objects.get_or_create(
        slug='company-professional',
        defaults={
            'name': 'Professional',
            'audience': SubscriptionPlan.Audience.COMPANY,
            'category': SubscriptionPlan.Category.RECRUITMENT,
            'price_monthly': 1000,
            'features': 'CV downloads',
        },
    )
    plan.category = SubscriptionPlan.Category.RECRUITMENT
    plan.save(update_fields=['category'])
    return plan


class CVLibraryTestCase(TestCase):
    def setUp(self):
        sync_pezajob_categories(deactivate_unknown=False)
        self.it_category = JobCategory.objects.get(slug='information-technology-software')
        self.admin = User.objects.create_user(
            username='cv_admin',
            email='cvadmin@test.com',
            password='pass12345',
            role='admin',
        )
        self.employer = User.objects.create_user(
            username='cv_employer',
            email='employer@test.com',
            password='pass12345',
            role='company',
        )
        self.seeker_user = User.objects.create_user(
            username='cv_seeker',
            email='seeker@test.com',
            password='pass12345',
            role='seeker',
        )
        self.company = Company.objects.create(
            owner=self.employer,
            name='Test Co',
            slug='test-co',
            industry='Technology',
            industry_slug='technology',
            location='Lusaka',
            is_active=True,
        )
        self.profile = SeekerProfile.objects.create(
            user=self.seeker_user,
            slug='cv-seeker-test',
            full_name='CV Tester',
            headline='Python Developer',
            job_category=self.it_category,
            category=self.it_category.name,
            category_slug=self.it_category.slug,
            location='Lusaka',
            skills='Python\nDjango',
            cv_file=SimpleUploadedFile('tester_cv.pdf', b'%PDF-1.4 cv content', content_type='application/pdf'),
        )
        self.talent_plan = _talent_cv_plan()
        CompanySubscription.objects.create(company=self.company, plan=self.talent_plan, is_active=True)
        self.client = Client()

    def test_upload_auto_indexes_cv_record(self):
        self.assertEqual(SeekerCVRecord.objects.filter(seeker_profile=self.profile, is_current=True).count(), 1)
        record = SeekerCVRecord.objects.get(seeker_profile=self.profile, is_current=True)
        self.assertEqual(record.full_name, 'CV Tester')
        self.assertEqual(record.category_slug, 'information-technology-software')

    def test_sync_all_backfills_existing_profiles(self):
        SeekerCVRecord.objects.all().delete()
        count = sync_all_seeker_cvs()
        self.assertEqual(count, 1)
        self.assertTrue(SeekerCVRecord.objects.filter(seeker_profile=self.profile, is_current=True).exists())

    def test_filter_cv_records_by_search_and_category(self):
        index_seeker_cv(self.profile)
        qs = filter_cv_records(q='Python', category_slug='information-technology-software')
        self.assertEqual(qs.count(), 1)
        qs = filter_cv_records(q='nonexistent')
        self.assertEqual(qs.count(), 0)

    def test_admin_cv_library_page_loads(self):
        self.client.login(username='cv_admin', password='pass12345')
        response = self.client.get(reverse('platform_admin:cv_library'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'CV Library')
        self.assertContains(response, 'CV Tester')
        self.assertContains(response, 'Download all matching')
        self.assertContains(response, 'Platform admin - no monthly download limit')

    def test_admin_cv_categories_page_loads(self):
        self.client.login(username='cv_admin', password='pass12345')
        response = self.client.get(reverse('platform_admin:cv_categories'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Information Technology')

    def test_admin_bulk_download_zip(self):
        self.client.login(username='cv_admin', password='pass12345')
        record = SeekerCVRecord.objects.get(seeker_profile=self.profile, is_current=True)
        with patch('seekers.cv_library.default_storage.exists', return_value=True), patch(
            'seekers.cv_library.default_storage.open',
            return_value=io.BytesIO(b'%PDF-1.4 cv content'),
        ):
            response = self.client.post(
                reverse('platform_admin:cv_library_download'),
                {'record_ids': [str(record.pk)]},
            )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/zip')

    def test_admin_download_all_matching(self):
        self.client.login(username='cv_admin', password='pass12345')
        with patch('seekers.cv_library.default_storage.exists', return_value=True), patch(
            'seekers.cv_library.default_storage.open',
            return_value=io.BytesIO(b'%PDF-1.4 cv content'),
        ):
            response = self.client.post(
                reverse('platform_admin:cv_library_download'),
                {'limit': 'all'},
            )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/zip')

    def test_employer_without_cv_subscription_blocked(self):
        CompanySubscription.objects.filter(company=self.company).delete()
        self.client.login(username='cv_employer', password='pass12345')
        response = self.client.get(reverse('employers:cv_library'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'CV Search subscription')

    def test_recruitment_plan_cannot_access_cv_library(self):
        CompanySubscription.objects.filter(company=self.company).update(
            plan=_recruitment_plan(),
        )
        self.client.login(username='cv_employer', password='pass12345')
        response = self.client.get(reverse('employers:cv_library'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'CV Search subscriptions')
        self.assertContains(response, 'Talent Database')

    def test_cv_search_subscriber_can_access_cv_library(self):
        self.client.login(username='cv_employer', password='pass12345')
        response = self.client.get(reverse('employers:cv_library'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'CV Tester')
        self.assertNotContains(response, 'Subscribe to CV Search')

    def test_cv_search_subscriber_bulk_download(self):
        self.client.login(username='cv_employer', password='pass12345')
        record = SeekerCVRecord.objects.get(seeker_profile=self.profile, is_current=True)
        with patch('seekers.cv_library.default_storage.exists', return_value=True), patch(
            'seekers.cv_library.default_storage.open',
            return_value=io.BytesIO(b'%PDF-1.4 cv content'),
        ):
            response = self.client.post(
                reverse('employers:cv_library_download'),
                {'record_ids': [str(record.pk)]},
            )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/zip')
