from django.test import TestCase
from django.utils import timezone

from employers.forms import JobPostForm, ReactivateJobForm
from jobs.category_data import sync_pezajob_categories
from jobs.models import Job, JobCategory


class JobPostFormTestMixin:
    @classmethod
    def setUpTestData(cls):
        sync_pezajob_categories(deactivate_unknown=False)
        cls.job_category = JobCategory.objects.get(slug='information-technology-software')

    def _category_data(self):
        return {'category': self.job_category.pk}

    def _location_data(self):
        return {
            'location_scope': 'single',
            'location_country': 'ZM',
            'location_region': 'Lusaka Province',
            'location_city': 'Lusaka',
            'location_regions_multi': [],
            'location_city_text': '',
        }

    def _experience_data(self):
        return {
            'experience_years_min': 3,
            'experience_years_max': 5,
        }

    def _positions_data(self):
        return {'positions_total': 1}

    def _application_data(self):
        return {
            'application_methods': ['email'],
            'how_to_apply': 'Send your CV to careers@example.com',
        }


class JobPostFormSalaryTests(JobPostFormTestMixin, TestCase):
    def _base_data(self):
        return {
            'title': 'Support Engineer',
            'company_industry': 'Information Technology & Software',
            **self._category_data(),
            **self._location_data(),
            'job_type': 'full_time',
            **self._experience_data(),
            **self._positions_data(),
            **self._application_data(),
            'description': 'Role summary.',
            'responsibilities': 'Support users',
            'requirements': 'IT experience',
            'benefits': '',
            'ongoing_vacancy': 'on',
        }

    def test_accepts_minimum_salary_only(self):
        data = {**self._base_data(), 'salary_min': 12000, 'salary_max': ''}
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)

    def test_accepts_maximum_salary_only(self):
        data = {**self._base_data(), 'salary_min': '', 'salary_max': 18000}
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)

    def test_accepts_no_salary(self):
        data = {**self._base_data(), 'salary_min': '', 'salary_max': ''}
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)

    def test_rejects_min_greater_than_max(self):
        data = {**self._base_data(), 'salary_min': 20000, 'salary_max': 10000}
        form = JobPostForm(data)
        self.assertFalse(form.is_valid())
        self.assertIn('salary_max', form.errors)


class JobPostFormExperienceTests(JobPostFormTestMixin, TestCase):
    def _base_data(self):
        return {
            'title': 'Support Engineer',
            'company_industry': 'Information Technology & Software',
            **self._category_data(),
            **self._location_data(),
            'job_type': 'full_time',
            **self._experience_data(),
            **self._positions_data(),
            **self._application_data(),
            'description': 'Role summary.',
            'responsibilities': 'Support users',
            'requirements': 'IT experience',
            'benefits': '',
            'ongoing_vacancy': 'on',
        }

    def test_rejects_experience_min_greater_than_max(self):
        data = {
            **self._base_data(),
            'experience_years_min': 8,
            'experience_years_max': 3,
        }
        form = JobPostForm(data)
        self.assertFalse(form.is_valid())
        self.assertIn('experience_years_max', form.errors)

    def test_saves_experience_range_and_display(self):
        form = JobPostForm(self._base_data())
        self.assertTrue(form.is_valid(), form.errors)
        job = form.save(commit=False)
        self.assertEqual(job.experience_years_min, 3)
        self.assertEqual(job.experience_years_max, 5)
        self.assertEqual(job.experience_required, '3–5 years')
        self.assertEqual(job.experience_level, Job.ExperienceLevel.MID)

    def test_accepts_multiple_positions(self):
        data = {**self._base_data(), 'positions_total': 5}
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)
        job = form.save(commit=False)
        self.assertEqual(job.positions_total, 5)
        self.assertEqual(job.positions_filled, 0)

    def test_external_application_requires_instructions(self):
        data = {
            **self._base_data(),
            'application_methods': ['email'],
            'how_to_apply': '',
        }
        form = JobPostForm(data)
        self.assertFalse(form.is_valid())
        self.assertIn('how_to_apply', form.errors)

    def test_accepts_both_application_methods(self):
        data = {
            **self._base_data(),
            'application_methods': ['ats', 'email'],
        }
        form = JobPostForm(data)
        self.assertFalse(form.is_valid())
        self.assertIn('application_methods', form.errors)

    def test_requires_at_least_one_application_method(self):
        data = {**self._base_data(), 'application_methods': []}
        form = JobPostForm(data)
        self.assertFalse(form.is_valid())
        self.assertIn('application_methods', form.errors)


class JobPostFormOngoingVacancyTests(JobPostFormTestMixin, TestCase):
    def _base_data(self):
        return {
            'title': 'Support Engineer',
            'company_industry': 'Information Technology & Software',
            **self._category_data(),
            **self._location_data(),
            'job_type': 'full_time',
            **self._experience_data(),
            **self._positions_data(),
            **self._application_data(),
            'description': 'Role summary.',
            'responsibilities': 'Support users',
            'requirements': 'IT experience',
            'benefits': '',
        }

    def test_ongoing_vacancy_clears_deadline(self):
        data = {**self._base_data(), 'ongoing_vacancy': 'on'}
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)
        job = form.save(commit=False)
        self.assertIsNone(job.deadline)
        self.assertEqual(job.location, 'Lusaka, Lusaka Province, Zambia')

    def test_deadline_job_requires_date_or_ongoing(self):
        form = JobPostForm(self._base_data())
        self.assertFalse(form.is_valid())
        self.assertIn('ongoing_vacancy', form.errors)

    def test_accepts_future_deadline_without_ongoing(self):
        deadline = (timezone.localdate() + timezone.timedelta(days=14)).isoformat()
        data = {**self._base_data(), 'deadline': deadline}
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)

    def test_rejects_deadline_and_ongoing_together(self):
        deadline = (timezone.localdate() + timezone.timedelta(days=14)).isoformat()
        data = {**self._base_data(), 'deadline': deadline, 'ongoing_vacancy': 'on'}
        form = JobPostForm(data)
        self.assertFalse(form.is_valid())
        self.assertIn('deadline', form.errors)

    def test_remote_job_saves_location(self):
        data = {
            **self._base_data(),
            'location_scope': 'single',
            'job_type': 'remote',
            'ongoing_vacancy': 'on',
        }
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)
        job = form.save(commit=False)
        self.assertEqual(job.job_type, Job.JobType.REMOTE)
        self.assertEqual(job.location, 'Lusaka, Lusaka Province, Zambia')
        self.assertEqual(job.location_regions, ['Lusaka Province'])

    def test_multi_region_job_with_optional_city_text(self):
        data = {
            **self._base_data(),
            'location_scope': 'multi',
            'location_regions_multi': ['Lusaka Province', 'Copperbelt Province'],
            'location_city_text': 'Head office',
            'location_region': '',
            'location_city': '',
            'job_type': 'remote',
            'ongoing_vacancy': 'on',
        }
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)
        job = form.save(commit=False)
        self.assertEqual(job.location, 'Head office · Lusaka Province & Copperbelt Province · Zambia')
        self.assertEqual(job.location_regions, ['Lusaka Province', 'Copperbelt Province'])
        self.assertEqual(job.location_city, 'Head office')

    def test_multi_region_job_without_city(self):
        data = {
            **self._base_data(),
            'location_scope': 'multi',
            'location_regions_multi': ['Lusaka Province', 'Southern Province'],
            'location_city_text': '',
            'location_region': '',
            'location_city': '',
            'ongoing_vacancy': 'on',
        }
        form = JobPostForm(data)
        self.assertTrue(form.is_valid(), form.errors)
        job = form.save(commit=False)
        self.assertEqual(job.location, 'Lusaka Province & Southern Province · Zambia')
        self.assertEqual(job.location_city, '')

    def test_edit_remote_job_prefills_location_fields(self):
        job = Job.objects.create(
            title='Remote Analyst',
            company_name='Acme',
            company_initials='AC',
            location='Lusaka, Lusaka, Zambia',
            job_type=Job.JobType.REMOTE,
            experience_level=Job.ExperienceLevel.MID,
            description='Desc',
            responsibilities='Work',
            requirements='Skills',
            deadline=None,
            is_active=True,
        )
        form = JobPostForm(instance=job)
        self.assertEqual(form.fields['location_country'].initial, 'ZM')
        self.assertEqual(form.fields['location_region'].initial, 'Lusaka Province')
        self.assertEqual(form.fields['location_city'].initial, 'Lusaka')

    def test_location_not_validated_without_job_type(self):
        data = {**self._base_data(), 'job_type': ''}
        form = JobPostForm(data)
        self.assertFalse(form.is_valid())
        self.assertIn('job_type', form.errors)
        self.assertNotIn('location_region', form.errors)


class ReactivateJobFormOngoingTests(TestCase):
    def test_expired_job_can_reopen_as_ongoing(self):
        job = Job.objects.create(
            title='Expired Role',
            company_name='Acme',
            company_initials='AC',
            location='Lusaka',
            job_type=Job.JobType.FULL_TIME,
            experience_level=Job.ExperienceLevel.MID,
            description='Desc',
            responsibilities='Work',
            requirements='Skills',
            deadline=timezone.localdate() - timezone.timedelta(days=3),
            is_active=True,
        )
        form = ReactivateJobForm({'ongoing_vacancy': 'on'}, instance=job)
        self.assertTrue(form.is_valid(), form.errors)
        updated = form.save(commit=False)
        self.assertIsNone(updated.deadline)
