# -*- coding: utf-8 -*-

from typing import Union, List, Dict, Text, Any
from mock import patch

from zerver.lib.actions import get_realm, try_add_realm_custom_profile_field, \
    do_update_user_custom_profile_data, do_remove_realm_custom_profile_field
from zerver.lib.test_classes import ZulipTestCase
from zerver.models import CustomProfileField, \
    custom_profile_fields_for_realm, get_user
import ujson


class CustomProfileFieldTest(ZulipTestCase):

    def test_list(self) -> None:
        self.login(self.example_email("iago"))
        realm = get_realm('zulip')
        try_add_realm_custom_profile_field(realm, u"Phone",
                                           CustomProfileField.SHORT_TEXT)
        result = self.client_get("/json/realm/profile_fields")
        self.assert_json_success(result)
        self.assertEqual(200, result.status_code)
        content = result.json()
        self.assertEqual(len(content["custom_fields"]), 1)

    def test_create(self) -> None:
        self.login(self.example_email("iago"))
        data = {"name": u"Phone", "field_type": "text id"}  # type: Dict[str, Any]
        result = self.client_post("/json/realm/profile_fields", info=data)
        self.assert_json_error(result, u'Argument "field_type" is not valid JSON.')

        data["name"] = ""
        data["field_type"] = 100
        result = self.client_post("/json/realm/profile_fields", info=data)
        self.assert_json_error(result, u'Name cannot be blank.')

        data["name"] = "Phone"
        result = self.client_post("/json/realm/profile_fields", info=data)
        self.assert_json_error(result, u'Invalid field type.')

        data["name"] = "Phone"
        data["field_type"] = CustomProfileField.SHORT_TEXT
        result = self.client_post("/json/realm/profile_fields", info=data)
        self.assert_json_success(result)

        result = self.client_post("/json/realm/profile_fields", info=data)
        self.assert_json_error(result,
                               u'A field with that name already exists.')

    def test_not_realm_admin(self) -> None:
        self.login(self.example_email("hamlet"))
        result = self.client_post("/json/realm/profile_fields")
        self.assert_json_error(result, u'Must be an organization administrator')
        result = self.client_delete("/json/realm/profile_fields/1")
        self.assert_json_error(result, 'Must be an organization administrator')

    def test_delete(self) -> None:
        self.login(self.example_email("iago"))
        realm = get_realm('zulip')
        field = try_add_realm_custom_profile_field(
            realm,
            "Phone",
            CustomProfileField.SHORT_TEXT
        )
        result = self.client_delete("/json/realm/profile_fields/100")
        self.assert_json_error(result, 'Field id 100 not found.')

        self.assertEqual(CustomProfileField.objects.count(), 1)
        result = self.client_delete(
            "/json/realm/profile_fields/{}".format(field.id))
        self.assert_json_success(result)
        self.assertEqual(CustomProfileField.objects.count(), 0)

    def test_update(self) -> None:
        self.login(self.example_email("iago"))
        realm = get_realm('zulip')
        result = self.client_patch(
            "/json/realm/profile_fields/100",
            info={'name': '',
                  'field_type': CustomProfileField.SHORT_TEXT}
        )
        self.assert_json_error(result, u'Name cannot be blank.')

        result = self.client_patch(
            "/json/realm/profile_fields/100",
            info={'name': 'Phone Number',
                  'field_type': CustomProfileField.SHORT_TEXT}
        )
        self.assert_json_error(result, u'Field id 100 not found.')

        field = try_add_realm_custom_profile_field(
            realm,
            u"Phone",
            CustomProfileField.SHORT_TEXT
        )

        self.assertEqual(CustomProfileField.objects.count(), 1)
        result = self.client_patch(
            "/json/realm/profile_fields/{}".format(field.id),
            info={'name': 'Phone Number',
                  'field_type': CustomProfileField.SHORT_TEXT})
        self.assert_json_success(result)
        self.assertEqual(CustomProfileField.objects.count(), 1)
        field = CustomProfileField.objects.first()
        self.assertEqual(field.name, 'Phone Number')
        self.assertEqual(field.field_type, CustomProfileField.SHORT_TEXT)

    def test_update_is_aware_of_uniqueness(self) -> None:
        self.login(self.example_email("iago"))
        realm = get_realm('zulip')
        try_add_realm_custom_profile_field(realm, u"Phone",
                                           CustomProfileField.SHORT_TEXT)

        field = try_add_realm_custom_profile_field(
            realm,
            u"Phone 1",
            CustomProfileField.SHORT_TEXT
        )

        self.assertEqual(CustomProfileField.objects.count(), 2)
        result = self.client_patch(
            "/json/realm/profile_fields/{}".format(field.id),
            info={'name': 'Phone', 'field_type': CustomProfileField.SHORT_TEXT})
        self.assert_json_error(
            result, u'A field with that name already exists.')

class CustomProfileDataTest(ZulipTestCase):

    def test_update_invalid(self) -> None:
        self.login(self.example_email("iago"))
        data = [{'id': 1234, 'value': '12'}]
        result = self.client_patch("/json/users/me/profile_data", {
            'data': ujson.dumps(data)
        })
        self.assert_json_error(result,
                               u"Field id 1234 not found.")

    def test_update_invalid_value(self) -> None:
        self.login(self.example_email("iago"))
        realm = get_realm('zulip')
        age_field = try_add_realm_custom_profile_field(
            realm,
            u"age",
            CustomProfileField.INTEGER
        )

        data = [{'id': age_field.id, 'value': 'text'}]
        result = self.client_patch("/json/users/me/profile_data", {
            'data': ujson.dumps(data)
        })
        self.assert_json_error(
            result,
            u"value[{}] is not an integer".format(age_field.id))

    def test_update_invalid_double(self) -> None:
        self.login(self.example_email("iago"))
        realm = get_realm('zulip')
        field = try_add_realm_custom_profile_field(
            realm,
            u"distance",
            CustomProfileField.FLOAT
        )

        data = [{'id': field.id, 'value': 'text'}]
        result = self.client_patch("/json/users/me/profile_data", {
            'data': ujson.dumps(data)
        })
        self.assert_json_error(
            result,
            u"value[{}] is not a float".format(field.id))

    def test_update_invalid_short_text(self) -> None:
        self.login(self.example_email("iago"))
        realm = get_realm('zulip')
        field = try_add_realm_custom_profile_field(
            realm,
            u"description",
            CustomProfileField.SHORT_TEXT
        )

        data = [{'id': field.id, 'value': 't' * 201}]
        result = self.client_patch("/json/users/me/profile_data", {
            'data': ujson.dumps(data)
        })
        self.assert_json_error(
            result,
            u"value[{}] is longer than 200.".format(field.id))

    def test_update_profile_data(self) -> None:
        self.login(self.example_email("iago"))
        realm = get_realm('zulip')
        fields = [
            (CustomProfileField.SHORT_TEXT, 'name 1', 'short text data'),
            (CustomProfileField.LONG_TEXT, 'name 2', 'long text data'),
            (CustomProfileField.INTEGER, 'name 3', 1),
            (CustomProfileField.FLOAT, 'name 4', 2.0),
        ]

        data = []
        for i, field_value in enumerate(fields):
            field_type, name, value = field_value
            field = try_add_realm_custom_profile_field(realm, name, field_type)
            data.append({
                'id': field.id,
                'value': value,
            })

        result = self.client_patch("/json/users/me/profile_data",
                                   {'data': ujson.dumps(data)})
        self.assert_json_success(result)

        iago = self.example_user('iago')
        expected_value = {f['id']: f['value'] for f in data}

        for field_dict in iago.profile_data:
            self.assertEqual(field_dict['value'], expected_value[field_dict['id']])
            for k in ['id', 'type', 'name']:
                self.assertIn(k, field_dict)

            self.assertEqual(len(iago.profile_data), 4)

        # Update value of field
        field = CustomProfileField.objects.get(name='name 1', realm=realm)
        data = [{
            'id': field.id,
            'value': 'foobar',
        }]

        result = self.client_patch("/json/users/me/profile_data",
                                   {'data': ujson.dumps(data)})
        self.assert_json_success(result)
        for f in iago.profile_data:
            if f['id'] == field.id:
                self.assertEqual(f['value'], 'foobar')

    def test_delete(self) -> None:
        user_profile = self.example_user('iago')
        realm = user_profile.realm
        field = try_add_realm_custom_profile_field(
            realm,
            u"Phone",
            CustomProfileField.SHORT_TEXT
        )
        data = [{'id': field.id, 'value': u'123456'}]  # type: List[Dict[str, Union[int, Text]]]
        do_update_user_custom_profile_data(user_profile, data)

        self.assertEqual(len(custom_profile_fields_for_realm(realm.id)), 1)
        self.assertEqual(user_profile.customprofilefieldvalue_set.count(), 1)

        do_remove_realm_custom_profile_field(realm, field)

        self.assertEqual(len(custom_profile_fields_for_realm(realm.id)), 0)
        self.assertEqual(user_profile.customprofilefieldvalue_set.count(), 0)
