"""Unit tests for the lumi2.usermodel module."""

import pytest
from PIL import Image, JpegImagePlugin

from lumi2.usermodel import User, Group
from lumi2.exceptions import InvalidStringFormatException


def test_is_valid_username():
    for invalid_type in [None, 0, True, ("x",), ["x"]]:
        with pytest.raises(TypeError):
            User.assert_is_valid_username(invalid_type)

    invalid_usernames = [
        "", " ", "\u1337", "\t", "\n",
        "alice?", "alice=", "alice*",
        "1alice", "alice bob",
    ]
    for username in invalid_usernames:
        with pytest.raises(InvalidStringFormatException):
            User.assert_is_valid_username(username)

    valid_usernames = [
        "alice", "Al1ce",
        "Aa0-_.", "a",
    ]
    for username in valid_usernames:
        User.assert_is_valid_username(username)


def test_is_valid_password_hash():
    for invalid_type in [None, 0, True, ("x",), ["x"]]:
        with pytest.raises(TypeError):
            User.assert_is_valid_password_hash(invalid_type)

    invalid_hashes = [
        "", " ", "\t", "\n",
        "foobar===", "ou=foobar", "foobar*",
        "foobar$", "foo bar",
    ]
    for invalid_hash in invalid_hashes:
        with pytest.raises(InvalidStringFormatException):
            User.assert_is_valid_password_hash(invalid_hash)

    valid_hashes = [
        # can contain [A-Za-z0-9+/] and up to two '=' at the end
        "EzM3", "abcABC123+/=",
    ]
    for valid_hash in valid_hashes:
        User.assert_is_valid_password_hash(valid_hash)


def test_is_valid_email():
    for invalid_type in [None, 0, True, ("x",), ["x"]]:
        with pytest.raises(TypeError):
            User.assert_is_valid_email(invalid_type)

    invalid_emails = [
        "", " ", "\t", "\n",
        " alice@example.com", "alice", "@", "alice@com"
        "alice@example.com ", "alice@ex ample.com"
    ]
    for invalid_email in invalid_emails:
        with pytest.raises(InvalidStringFormatException):
            User.assert_is_valid_email(invalid_email)

    valid_emails = [
        # can contain [A-Za-z0-9+/] and up to two '=' at the end
        "alice@example.com", "a@b.c", "Alice.1337$&@Fo0.xyz",
    ]
    for valid_email in valid_emails:
        User.assert_is_valid_email(valid_email)
    

def test_is_valid_name():
    for invalid_type in [None, 0, True, ("x",), ["x"]]:
        with pytest.raises(TypeError):
            User.assert_is_valid_name(invalid_type)

    invalid_names = [
        "", " ", "\t", "\n",
        "Alice Jones", " Alice",
    ]
    for invalid_name in invalid_names:
        with pytest.raises(InvalidStringFormatException):
            User.assert_is_valid_name(invalid_name)

    valid_names = [
        "Alice", "Älice", "Böb", "A1lic3$",
        "a", "1",
    ]
    for valid_name in valid_names:
        User.assert_is_valid_name(valid_name)


def test_is_valid_picture():
    # TODO implement
    pass


def test_generate_password_hash():
    for invalid_type in [None, 0, True, ("x",), ["x"]]:
        with pytest.raises(TypeError):
            User.generate_password_hash(invalid_type)

    with pytest.raises(ValueError):
        User.generate_password_hash("")

    assert User.generate_password_hash("Password") == "5sg7KCrrLgIoRFlXIcwAu9pHyyRTfBd5+buE8EA54Wdua6hXPliNoQUlEOOqCjKp5Vh5riKwwtYhNvwKPoX4uw=="


def test_get_default_picture(app):
    with app.app_context():
        assert isinstance(User._get_default_picture(), Image.Image)
        assert isinstance(User._get_default_picture(), JpegImagePlugin.JpegImageFile)


def test_User(app):
    with app.app_context():
        with pytest.raises(ValueError):
            User(
                username="=invalid",
                password_hash=User.generate_password_hash("password"),
                email="valid@example.com",
                first_name="Test",
                last_name="User",
            )
            User(
                username="test",
                password_hash="=invalid",
                email="invalid",
                first_name="Test",
                last_name="User",
            )
            User(
                username="test",
                password_hash=User.generate_password_hash("password"),
                email="invalid",
                first_name="Test",
                last_name="User",
            )
            User(
                username="test",
                password_hash=User.generate_password_hash("password"),
                email="invalid",
                first_name="Test",
                last_name="User",
            )
            User(
                username="test",
                password_hash=User.generate_password_hash("password"),
                email="valid@example.com",
                first_name="=invalid",
                last_name="User",
            )
            User(
                username="test",
                password_hash=User.generate_password_hash("password"),
                email="valid@example.com",
                first_name="Test",
                last_name="=invalid",
            )
            User(
                username="=invalid",
                password_hash="=invalid",
                email="=invalid",
                first_name="=invalid",
                last_name="=invalid",
            )

        User(
            username="test",
            password_hash=User.generate_password_hash("password"),
            email="valid@example.com",
            first_name="Test",
            last_name="User",
        )


def test_User_get_dn(app):
    with app.app_context():
        user = User(
            username="test",
            password_hash=User.generate_password_hash("password"),
            email="valid@example.com",
            first_name="Test",
            last_name="User",
        )

        assert user.get_dn() == "uid=test," + app.config['LDAP_USERS_OU']


def test_User_eq(app):
    with app.app_context():
        user0 = User(
            username="user0",
            password_hash=User.generate_password_hash("password"),
            email="valid@example.com",
            first_name="Test",
            last_name="User",
        )
        user0_copy = User(
            username="user0",
            password_hash=User.generate_password_hash("password"),
            email="valid@example.com",
            first_name="Test",
            last_name="User",
        )
        user1 = User(
            username="user1",
            password_hash=User.generate_password_hash("password"),
            email="valid@example.com",
            first_name="Test",
            last_name="User",
        )

        assert user0 == user0_copy
        assert user0 != user1
        assert user0 < user1


def test_is_valid_groupname():
    for invalid_type in [None, 0, True, ("x",), ["x"]]:
        with pytest.raises(TypeError):
            Group.assert_is_valid_groupname(invalid_type)

    for invalid_input in [
        "", " ", "foo ", " foo",
        "1foo", "foo_", "Foo="
    ]:
        with pytest.raises(InvalidStringFormatException):
            Group.assert_is_valid_groupname(invalid_input)

    for valid_input in [
        "foo", "foo123", "F1oFoFo", "o2FoFo",
    ]:
        Group.assert_is_valid_groupname(valid_input)


def test_Group(app):
    with app.app_context():
        user0 = User(
            username="user0",
            password_hash=User.generate_password_hash("password"),
            email="valid@example.com",
            first_name="Test",
            last_name="User",
        )
        user1 = User(
            username="user1",
            password_hash=User.generate_password_hash("password"),
            email="valid@example.com",
            first_name="Test",
            last_name="User",
        )
        user2 = User(
            username="user2",
            password_hash=User.generate_password_hash("password"),
            email="valid@example.com",
            first_name="Test",
            last_name="User",
        )

        with pytest.raises(ValueError):
            Group("foo_", {user0})
            Group("foo", set())

        group0 = Group("group0", {user0})
        group0_copy = Group("group0", {user0, user1})
        group1 = Group("group1", {user1, user2})

        assert group0.get_dn() == "cn=group0," + app.config['LDAP_GROUPS_OU']
        assert group0 == group0_copy
        assert group0 != group1
        assert group0 < group1
