from io import BytesIO

from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings
from django.urls import reverse
from PIL import Image

from accounts.models import User
from companies.completion import completion
from companies.models import Company, CompanyPhoto, CompanyProduct


SHORT = 'A Zambian retail company selling everyday goods and point of sale equipment to businesses across the country.'


def _png(name='x.png'):
    buf = BytesIO()
    Image.new('RGB', (40, 30), 'navy').save(buf, 'PNG')
    return SimpleUploadedFile(name, buf.getvalue(), content_type='image/png')


@override_settings(MEDIA_ROOT='/tmp/pezajob-test-media')
class CompanyProfileEditorTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username='emp', email='emp@x.com', password='pw12345!', role=User.Role.COMPANY)
        self.company = Company.objects.create(
            name='Acme Ltd', slug='acme', initials='A', industry='Retail', location='Lusaka',
            description='We sell things.', owner=self.user)
        self.client.force_login(self.user)
        self.url = reverse('employers:edit_company')

    def _post(self, section, data, **extra):
        return self.client.post(self.url, {'section': section, **data}, **extra)

    def test_every_section_renders(self):
        for section in ('info', 'about', 'products', 'locations', 'contact', 'careers', 'brand', 'gallery', 'verification', 'preview'):
            self.assertEqual(self.client.get(self.url, {'section': section}).status_code, 200, section)

    def test_seeker_cannot_open_editor(self):
        seeker = User.objects.create_user(username='s', email='s@x.com', password='pw12345!', role=User.Role.SEEKER)
        self.client.force_login(seeker)
        self.assertNotEqual(self.client.get(self.url).status_code, 200)

    def test_info_saves_industry_type_size_and_logo(self):
        r = self._post('info', {'name': 'Acme Limited', 'industry': 'Mining & Minerals', 'company_type': 'private',
                                'company_size': '51-200', 'founded_year': '2010', 'is_public': 'on', 'logo': _png('logo.png')})
        self.assertEqual(r.status_code, 302)
        self.company.refresh_from_db(); self.user.refresh_from_db()
        self.assertEqual((self.company.name, self.company.industry, self.company.company_type, self.company.company_size),
                         ('Acme Limited', 'Mining & Minerals', 'private', '51-200'))
        self.assertEqual(self.company.founded_year, 2010)
        self.assertTrue(self.user.avatar)

    def test_changing_industry_and_sub_industry_together_in_one_save(self):
        # Regression: sub-industry choices used to be built from the company's
        # already-saved industry, so switching both fields in the same submit
        # failed validation even though the pair was valid.
        r = self._post('info', {'name': 'Acme', 'industry': 'Information Technology & Software',
                                'sub_industry': 'IT Services', 'company_type': 'private'})
        self.assertEqual(r.status_code, 302, r.context['form'].errors if r.status_code == 200 else None)
        self.company.refresh_from_db()
        self.assertEqual((self.company.industry, self.company.sub_industry), ('Information Technology & Software', 'IT Services'))

    def test_info_requires_industry_and_type(self):
        r = self._post('info', {'name': 'Acme', 'industry': '', 'company_type': ''})
        self.assertEqual(r.status_code, 200)
        self.assertContains(r, 'This field is required')

    def test_visibility_switch_off(self):
        self._post('info', {'name': 'Acme', 'industry': 'Retail & Wholesale', 'company_type': 'private'})
        self.company.refresh_from_db()
        self.assertFalse(self.company.is_public)

    def test_about_requires_short_description(self):
        self.assertEqual(self._post('about', {'short_description': '', 'description': 'x'}).status_code, 200)
        self._post('about', {'short_description': SHORT, 'description': 'Long text'})
        self.company.refresh_from_db()
        self.assertEqual(self.company.short_description, SHORT)

    def test_locations_sync_card_location(self):
        self._post('locations', {'country': 'Zambia', 'province': 'Copperbelt', 'city': 'Kitwe',
                                 'address': 'Plot 1', 'other_locations': 'Ndola\nSolwezi'})
        self.company.refresh_from_db()
        self.assertEqual(self.company.location, 'Kitwe')
        self.assertEqual(self.company.other_location_list, ['Ndola', 'Solwezi'])

    def test_contact_and_social(self):
        self._post('contact', {'email': 'hello@acme.com', 'recruitment_email': 'hr@acme.com',
                               'facebook_url': 'https://facebook.com/acme', 'website': 'https://acme.com'})
        self.company.refresh_from_db()
        self.assertEqual((self.company.email, self.company.recruitment_email, self.company.facebook_url),
                         ('hello@acme.com', 'hr@acme.com', 'https://facebook.com/acme'))

    def test_careers_yes_no(self):
        self._post('careers', {'careers_overview': 'Many roles', 'offers_internships': 'yes', 'offers_graduate_roles': 'no',
                               'offers_trainee_programmes': 'yes', 'offers_apprenticeships': 'no', 'recruits_experienced': 'yes'})
        self.company.refresh_from_db()
        self.assertEqual(self.company.hiring_programmes, ['Internships', 'Graduate trainee programmes', 'Experienced professionals'])

    def test_brand_benefits_merge_presets_and_custom(self):
        self._post('brand', {'culture': 'Open', 'values': 'Integrity\nTeamwork', 'benefit_options': ['Pension', 'Medical Insurance'],
                             'benefits_other': 'Free lunch'})
        self.company.refresh_from_db()
        self.assertEqual(self.company.benefit_list, ['Pension', 'Medical Insurance', 'Free lunch'])
        self.assertEqual(self.company.value_list, ['Integrity', 'Teamwork'])
        page = self.client.get(self.url, {'section': 'brand'})
        self.assertContains(page, 'Free lunch')

    def test_products_add_update_delete_and_isolation(self):
        self._post('products', {'action': 'product_add', 'name': 'Payroll', 'description': 'Runs payroll', 'image': _png()})
        product = CompanyProduct.objects.get()
        self.assertEqual(product.company, self.company)
        self._post('products', {'action': 'product_update', 'product': product.pk, 'name': 'Payroll Pro', 'description': ''})
        product.refresh_from_db()
        self.assertEqual(product.name, 'Payroll Pro')
        other_user = User.objects.create_user(username='o', email='o@x.com', password='pw12345!', role=User.Role.COMPANY)
        Company.objects.create(name='Other', slug='other', initials='O', industry='Retail', location='X', description='d', owner=other_user)
        self.client.force_login(other_user)
        self.assertEqual(self._post('products', {'action': 'product_delete', 'product': product.pk}).status_code, 404)
        self.client.force_login(self.user)
        self._post('products', {'action': 'product_delete', 'product': product.pk})
        self.assertFalse(CompanyProduct.objects.exists())

    def test_gallery_upload_skips_non_images_and_deletes(self):
        bad = SimpleUploadedFile('notes.png', b'not an image', content_type='image/png')
        self.client.post(self.url, {'section': 'gallery', 'photos': [_png('a.png'), bad]})
        self.assertEqual(CompanyPhoto.objects.count(), 1)
        self._post('gallery', {'action': 'photo_delete', 'photo': CompanyPhoto.objects.get().pk})
        self.assertEqual(CompanyPhoto.objects.count(), 0)

    def test_verification_details_are_private(self):
        self._post('verification', {'registered_name': 'Acme Ltd', 'registration_number': 'PACRA-1', 'registration_country': 'Zambia',
                                    'rep_name': 'Jo', 'rep_position': 'HR', 'rep_email': 'jo@acme.com'})
        self.company.refresh_from_db()
        self.assertEqual(self.company.registration_number, 'PACRA-1')
        page = self.client.get(self.company.get_absolute_url())
        self.assertNotContains(page, 'PACRA-1')
        self.assertNotContains(page, 'jo@acme.com')

    def test_save_and_continue_later_goes_to_dashboard(self):
        r = self._post('about', {'short_description': SHORT, 'description': 'Long', 'then': 'later'})
        self.assertRedirects(r, reverse('employers:dashboard'), fetch_redirect_response=False)

    def test_save_and_continue_moves_to_next_section(self):
        r = self._post('about', {'short_description': SHORT, 'description': 'Long', 'then': 'next'})
        self.assertIn('section=locations', r['Location'])

    def test_save_and_continue_skips_already_finished_sections(self):
        # 'locations' is already complete (set up below); saving 'about' should
        # skip straight past it to the next section that still needs work,
        # not just land on the next one in the fixed order.
        self.company.country = 'Zambia'
        self.company.city = 'Lusaka'
        self.company.location = 'Lusaka'
        self.company.save()
        r = self._post('about', {'short_description': SHORT, 'description': 'Long', 'then': 'next'})
        self.assertNotIn('section=locations', r['Location'])
        self.assertIn('section=careers', r['Location'])

    def test_completion_grows_as_information_is_added(self):
        before = completion(self.company)['percent']
        self._post('about', {'short_description': SHORT, 'description': 'Long'})
        self._post('contact', {'email': 'hello@acme.com'})
        self.company.refresh_from_db()
        self.assertGreater(completion(self.company)['percent'], before)

    def test_public_page_shows_entered_information(self):
        self._post('about', {'short_description': SHORT, 'description': 'Long'})
        self._post('careers', {'careers_overview': 'We hire cashiers.', 'offers_internships': 'yes', 'why_work_with_us': 'Growth.'})
        self._post('products', {'action': 'product_add', 'name': 'Point of Sale', 'description': 'Tills'})
        page = self.client.get(self.company.get_absolute_url())
        for text in (SHORT, 'We hire cashiers.', 'Internships', 'Point of Sale', 'Growth.'):
            self.assertContains(page, text)

    def test_short_description_length_limits(self):
        self.assertEqual(self._post('about', {'short_description': 'Too short', 'description': 'x'}).status_code, 200)
        self.assertEqual(self._post('about', {'short_description': 'x' * 301, 'description': 'x'}).status_code, 200)

    def test_about_formatting_renders_safely(self):
        from companies.templatetags.company_tags import company_html
        html = str(company_html('## Who we are' + chr(10) + 'We <b>mine</b>.' + chr(10) * 2 + '- Copper' + chr(10) + '- Cobalt' + chr(10) * 2 + 'Second paragraph'))
        self.assertIn('<h3 class="company-about-heading">Who we are</h3>', html)
        self.assertIn('<li>Copper</li>', html)
        self.assertIn('&lt;b&gt;mine&lt;/b&gt;', html)
        self.assertNotIn('<b>', html)

    def test_short_description_shows_on_company_card(self):
        self._post('about', {'short_description': SHORT, 'description': 'Long'})
        page = self.client.get(reverse('companies:list'))
        self.assertContains(page, SHORT)
