import json

from django.core import mail
from django.contrib.auth.models import User
from django.test import TestCase, Client, override_settings

from contribmap.models import Contrib
from contribmap.forms import PublicContribForm


class APITestClient(Client):
    def json_get(self, *args, **kwargs):
        """ Annotate the response with a .data containing parsed JSON
        """
        response = super().get(*args, **kwargs)
        response.data = json.loads(response.content.decode('utf-8'))
        return response


class APITestCase(TestCase):
    def setUp(self):
        super().setUp()
        self.client = APITestClient()


class TestContrib(TestCase):
    def test_comma_separatedcharfield(self):
        co = Contrib(name='foo', orientations=['SO', 'NE'],
                     contrib_type=Contrib.CONTRIB_CONNECT)
        co.save()
        self.assertEqual(
            Contrib.objects.get(name='foo').orientations,
            ['SO', 'NE'])
        co.orientations = ['S']
        co.save()


class TestContribPrivacy(TestCase):
    def test_always_private_field(self):
        c = Contrib.objects.create(
            name='John',
            phone='010101010101',
            contrib_type=Contrib.CONTRIB_CONNECT,
        )
        self.assertEqual(c.get_public_field('phone'), None)

    def test_public_field(self):
        c = Contrib.objects.create(
            name='John',
            phone='010101010101',
            contrib_type=Contrib.CONTRIB_CONNECT,
            privacy_name=True,
        )
        self.assertEqual(c.get_public_field('name'), 'John')

    def test_public_callable_field(self):
        c = Contrib.objects.create(
            name='John',
            phone='010101010101',
            orientations=['N'],
            contrib_type=Contrib.CONTRIB_CONNECT,
            privacy_name=True,
        )
        self.assertEqual(c.get_public_field('angles'), [[-23, 22]])

    def test_private_field(self):
        c = Contrib.objects.create(
            name='John',
            phone='010101010101',
            contrib_type=Contrib.CONTRIB_CONNECT,
        )
        self.assertEqual(c.privacy_name, False)
        self.assertEqual(c.get_public_field('name'), None)


class TestViews(APITestCase):
    def test_public_json(self):
        response = self.client.json_get('/map/public.json')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data['features']), 0)

        Contrib.objects.create(
            name='John',
            phone='010101010101',
            contrib_type=Contrib.CONTRIB_CONNECT,
            privacy_coordinates=False,
        )
        response = self.client.json_get('/map/public.json')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data['features']), 1)

    def test_private_json(self):
        self.client.force_login(
            User.objects.create(username='foo', is_staff=False))

        response = self.client.get('/map/private.json')
        self.assertEqual(response.status_code, 403)

    def test_private_json_staff(self):
        self.client.force_login(
            User.objects.create(username='foo', is_staff=True))
        response = self.client.get('/map/private.json')
        self.assertEqual(response.status_code, 200)

    @override_settings(NOTIFICATION_EMAILS=['foo@example.com'])
    def test_add_contrib_sends_email(self):
        response = self.client.post('/map/contribute', {
            'roof': True,
            'privacy_place_details': True,
            'privacy_coordinates': True,
            'phone': '0202020202',
            'orientations': 'N',
            'orientations': 'NO',
            'orientations': 'O',
            'orientations': 'SO',
            'orientations': 'S',
            'orientations': 'SE',
            'orientations': 'E',
            'orientations': 'NE',
            'orientation': 'all',
            'name': 'JohnCleese',
            'longitude': -1.553621,
            'latitude': 47.218371,
            'floor_total': '2',
            'floor': 1,
            'email': 'coucou@example.com',
            'contrib_type': 'connect',
            'connect_local': 'on',
        })
        self.assertEqual(response.status_code, 302)

        self.assertEqual(len(mail.outbox), 1)
        self.assertIn('JohnCleese', mail.outbox[0].subject)
        self.assertIn('JohnCleese', mail.outbox[0].body)

class TestForms(TestCase):
    valid_data = {
        'roof': True,
        'privacy_place_details': True,
        'privacy_coordinates': True,
        'orientations': ['N'],
        'orientation': 'all',
        'name': 'JohnCleese',
        'longitude': -1.553621,
        'email': 'foo@example.com',
        'phone': '0202020202',
        'latitude': 47.218371,
        'floor_total': '2',
        'floor': 1,
        'contrib_type': 'connect',
        'connect_local': 'on',
    }

    def test_contact_validation(self):
        no_contact, phone_contact, email_contact, both_contact = [
            self.valid_data.copy() for i in range(4)]

        del phone_contact['email']
        del email_contact['phone']
        del no_contact['phone']
        del no_contact['email']

        both_contact.update(phone_contact)
        both_contact.update(email_contact)

        self.assertFalse(PublicContribForm(no_contact).is_valid())
        self.assertTrue(PublicContribForm(phone_contact).is_valid())
        self.assertTrue(PublicContribForm(email_contact).is_valid())
        self.assertTrue(PublicContribForm(both_contact).is_valid())


class TestDataImport(TestCase):
    fixtures = ['bottle_data.yaml']

    def test_re_save(self):
        for contrib in Contrib.objects.all():
            contrib.full_clean()
            contrib.save()