# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import datetime
import os

import PIL.Image
from django.conf import settings
from webtest import Upload

from authentic2.custom_user.models import User
from authentic2.models import Attribute

from .utils import get_link_from_mail


def test_string(db, app, admin, mailoutbox):
    Attribute.objects.create(
        name='nom_de_naissance', label='Nom de naissance', kind='string', asked_on_registration=True
    )
    qs = User.objects.filter(first_name='John')

    response = app.get('/accounts/register/')
    form = response.form
    form.set('email', 'john.doe@example.com')
    response = form.submit().follow()
    assert 'john.doe@example.com' in response
    url = get_link_from_mail(mailoutbox[0])
    response = app.get(url)

    form = response.form
    assert response.pyquery('#id_nom_de_naissance').attr('maxlength') == '256'
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('nom_de_naissance', '1234567890' * 30)
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert response.pyquery.find('.form-field-error #id_nom_de_naissance')

    form = response.form
    form.set('nom_de_naissance', 'Noël')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit().follow()
    assert qs.get().attributes.nom_de_naissance == 'Noël'
    qs.delete()

    app.authorization = ('Basic', (admin.username, admin.username))
    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'nom_de_naissance': '1234567890' * 30,
    }
    app.post_json('/api/users/', params=payload, status=400)

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'nom_de_naissance': 'Noël',
    }
    app.post_json('/api/users/', params=payload, status=201)
    assert qs.get().attributes.nom_de_naissance == 'Noël'
    qs.delete()


def test_fr_postcode(db, app, admin, mailoutbox):
    def register_john():
        response = app.get('/accounts/register/')
        form = response.form
        form.set('email', 'john.doe@example.com')
        response = form.submit().follow()
        assert 'john.doe@example.com' in response
        return get_link_from_mail(mailoutbox[-1])

    Attribute.objects.create(
        name='postcode', label='postcode', kind='fr_postcode', asked_on_registration=True
    )
    qs = User.objects.filter(first_name='John')

    url = register_john()
    response = app.get(url)

    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('postcode', 'abc')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert response.pyquery.find('.form-field-error #id_postcode')

    form = response.form
    form.set('postcode', '123')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert response.pyquery.find('.form-field-error #id_postcode')

    form = response.form
    form.set('postcode', '12345')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit().follow()
    assert qs.get().attributes.postcode == '12345'
    qs.delete()

    url = register_john()
    response = app.get(url)
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('postcode', ' 12345 ')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit().follow()
    assert qs.get().attributes.postcode == '12345'
    qs.delete()

    url = register_john()
    response = app.get(url)
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('postcode', '')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit().follow()
    assert qs.get().attributes.postcode == ''
    qs.delete()

    app.authorization = ('Basic', (admin.username, admin.username))

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'postcode': ' 1234abc ',
    }
    app.post_json('/api/users/', params=payload, status=400)

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'postcode': '1234',
    }
    app.post_json('/api/users/', params=payload, status=400)

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'postcode': '12345',
    }
    app.post_json('/api/users/', params=payload)
    assert qs.get().attributes.postcode == '12345'
    qs.delete()

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'postcode': None,
    }
    app.post_json('/api/users/', params=payload)
    assert qs.get().attributes.postcode is None
    qs.delete()

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'postcode': '',
    }
    app.post_json('/api/users/', params=payload)
    assert qs.get().attributes.postcode == ''
    qs.delete()


def test_phone_number(db, app, admin, mailoutbox, settings):
    settings.A2_EMAILS_ADDRESS_RATELIMIT = None

    def register_john():
        response = app.get('/accounts/register/')
        form = response.form
        form.set('email', 'john.doe@example.com')
        response = form.submit().follow()
        assert 'john.doe@example.com' in response
        return get_link_from_mail(mailoutbox[-1])

    Attribute.objects.create(
        name='phone_number', label='phone', kind='phone_number', asked_on_registration=True
    )
    qs = User.objects.filter(first_name='John')

    url = register_john()
    response = app.get(url)
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('phone_number', 'abc')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert response.pyquery.find('.form-field-error #id_phone_number')

    form = response.form
    assert response.pyquery('#id_phone_number').attr('maxlength') == '30'
    form.set('phone_number', '1234512345' * 10)
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert response.pyquery.find('.form-field-error #id_phone_number')

    form = response.form
    form.set('phone_number', '12345')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit().follow()
    assert qs.get().attributes.phone_number == '12345'
    qs.delete()

    url = register_john()
    response = app.get(url)
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('phone_number', '+12345')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit().follow()
    assert qs.get().attributes.phone_number == '+12345'
    qs.delete()

    url = register_john()
    response = app.get(url)
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('phone_number', '')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit().follow()
    assert qs.get().attributes.phone_number == ''
    qs.delete()

    url = register_john()
    response = app.get(url)
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('phone_number', ' +  1.2-3  4 5 ')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit().follow()
    assert qs.get().attributes.phone_number == '+12345'
    qs.delete()


def test_birthdate(db, app, admin, mailoutbox, freezer):
    def register_john():
        response = app.get('/accounts/register/')
        form = response.form
        form.set('email', 'john.doe@example.com')
        response = form.submit().follow()
        assert 'john.doe@example.com' in response
        return get_link_from_mail(mailoutbox[-1])

    freezer.move_to('2018-01-01')
    Attribute.objects.create(
        name='birthdate', label='birthdate', kind='birthdate', asked_on_registration=True
    )
    qs = User.objects.filter(first_name='John')

    url = register_john()
    response = app.get(url)

    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('birthdate', '2018-01-01')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert response.pyquery.find('.form-field-error #id_birthdate')

    form.set('birthdate', '2017-12-31')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    form.submit().follow()
    assert qs.get().attributes.birthdate == datetime.date(2017, 12, 31)
    qs.delete()

    url = register_john()
    response = app.get(url)
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('birthdate', '1899-12-31')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert response.pyquery.find('.form-field-error #id_birthdate')

    form.set('birthdate', '1900-01-01')
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    form.submit().follow()
    assert qs.get().attributes.birthdate == datetime.date(1900, 1, 1)
    qs.delete()


def test_birthdate_api(db, app, admin, mailoutbox, freezer):
    freezer.move_to('2018-01-01')
    Attribute.objects.create(
        name='birthdate', label='birthdate', kind='birthdate', asked_on_registration=True
    )
    qs = User.objects.filter(first_name='John')
    app.authorization = ('Basic', (admin.username, admin.username))

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'birthdate': '2018-01-01',
    }
    app.post_json('/api/users/', params=payload, status=400)

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'birthdate': '2017-12-31',
    }
    response = app.post_json('/api/users/', params=payload)
    assert qs.get().attributes.birthdate == datetime.date(2017, 12, 31)
    qs.delete()

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'birthdate': '1899-12-31',
    }
    app.post_json('/api/users/', params=payload, status=400)

    payload = {
        'first_name': 'John',
        'last_name': 'Doe',
        'birthdate': '1900-01-01',
    }
    app.post_json('/api/users/', params=payload)
    assert qs.get().attributes.birthdate == datetime.date(1900, 1, 1)
    qs.delete()


def test_profile_image(db, app, admin, mailoutbox):
    Attribute.objects.create(
        name='cityscape_image',
        label='cityscape',
        kind='profile_image',
        asked_on_registration=True,
        required=False,
        user_visible=True,
        user_editable=True,
    )

    def john():
        return User.objects.get(first_name='John')

    response = app.get('/accounts/register/')
    form = response.form
    form.set('email', 'john.doe@example.com')
    response = form.submit().follow()
    assert 'john.doe@example.com' in response
    url = get_link_from_mail(mailoutbox[0])
    response = app.get(url)

    # verify empty file is refused
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('cityscape_image', Upload('/dev/null'))
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert response.pyquery.find('.form-field-error #id_cityscape_image')

    # verify 200x200 image is accepted
    form = response.form
    form.set('cityscape_image', Upload('tests/200x200.jpg'))
    form.set('password1', '12345abcdA')
    form.set('password2', '12345abcdA')
    response = form.submit()
    assert john().attributes.cityscape_image
    profile_filename = john().attributes.cityscape_image.name
    assert profile_filename.endswith('.jpeg')

    # verify API serves absolute URL for profile images
    app.authorization = ('Basic', (admin.username, admin.username))
    response = app.get('/api/users/%s/' % john().uuid)
    assert (
        response.json['cityscape_image']
        == 'http://testserver/media/%s' % john().attributes.cityscape_image.name
    )
    app.authorization = None

    # verify we can clear the image
    response = app.get('/accounts/edit/')
    form = response.form
    form.set('first_name', 'John')
    form.set('last_name', 'Doe')
    form.set('cityscape_image-clear', True)
    response = form.submit()
    assert john().attributes.cityscape_image == None

    # verify API serves None for empty profile images
    app.authorization = ('Basic', (admin.username, admin.username))
    response = app.get('/api/users/%s/' % john().uuid)
    assert response.json['cityscape_image'] is None

    # verify 201x201 image is accepted and resized
    response = app.get('/accounts/edit/')
    form = response.form
    form.set('cityscape_image', Upload('tests/201x201.jpg'))
    response = form.submit()
    with PIL.Image.open(os.path.join(settings.MEDIA_ROOT, john().attributes.cityscape_image.name)) as image:
        assert image.width == 200
        assert image.height == 200
    assert john().attributes.cityscape_image.name != profile_filename

    # verify file input mentions image files
    response = app.get('/accounts/edit/')
    form = response.form
    assert form['cityscape_image'].attrs['accept'] == 'image/*'


def test_multiple_attribute_setter(db, app, simple_user):
    nicks = Attribute.objects.create(
        name='nicknames',
        label='Nicknames',
        kind='string',
        required=False,
        multiple=True,
        user_visible=True,
        user_editable=True,
    )

    simple_user.attributes.nicknames = ['Roger', 'Tony', 'Robie']
    simple_user.save()
    assert 'Tony' in [atv.content for atv in simple_user.attribute_values.filter(attribute=nicks)]

    simple_user.attributes.nicknames = ['Roger', 'Timmy', 'Robie']
    simple_user.save()
    assert not 'Tony' in [atv.content for atv in simple_user.attribute_values.filter(attribute=nicks)]
