#!/usr/bin/python3
#
# Copyright (C) 2022 Red Hat, Inc.
# SPDX-License-Identifier: LGPL-2.1-or-later

from anacondalib import VirtInstallMachineCase, pixel_tests_ignore
from installer import Installer
from password import Password
from review import Review
from testlib import nondestructive, test_main  # pylint: disable=import-error
from users import CREATE_ACCOUNT_ID_PREFIX, Users, create_user


@nondestructive
class TestUsers(VirtInstallMachineCase):
    def testBasic(self):
        """
        Description:
            Test the basic functionality of the user creation screen

        Expected results:
            - A new user can be created
            - The root account can be enabled and a root password set
            - The user can skip creating a user account
            - The user can skip enabling the root account
            - The user can't skip both
        """
        b = self.browser
        i = Installer(b, self.machine)
        u = Users(b, self.machine)
        r = Review(b, self.machine)

        i.open()
        i.reach(i.steps.ACCOUNTS)

        # Check skipping root account enablement
        create_user(b, self.machine)

        # Wait for the form to be ready before taking a screenshot
        i.check_next_disabled(False)
        b.assert_pixels(
            "#app",
            "users-step-basic",
            ignore=pixel_tests_ignore,
        )
        i.reach(i.steps.REVIEW)

        users = u.dbus_get_users()
        self.assertIn('"groups" as 1 "wheel"', users)
        self.assertIn('"is-crypted" b true', users)
        self.assertIn("tester", users)
        assert u.dbus_get_root_locked()
        assert not u.dbus_get_is_root_password_set()

        r.check_account("tester")

        # Check skipping user account creation
        i.reach_on_sidebar(i.steps.ACCOUNTS)
        u.enable_user_account(False)
        u.enable_root_account(True)
        u.set_valid_root_password()
        i.next()
        r.check_account("Root account is enabled, but no user account has been configured")
        assert not u.dbus_get_root_locked()
        assert u.dbus_get_is_root_password_set()
        users = u.dbus_get_users()
        assert "tester" not in users

        # Check skipping both user and root account creation
        i.reach_on_sidebar(i.steps.ACCOUNTS)
        u.enable_user_account(False)
        u.enable_root_account(False)
        i.check_next_disabled()

    @staticmethod
    def _test_user_name(user, installer, valid, invalid):
        installer.check_next_disabled(disabled=False)

        n_valid = len(valid) - 1
        n_invalid = len(invalid) - 1

        # Set valid and invalid value in turns and check the next button
        for i in range(max(n_valid, n_invalid)):
            i_valid = min(i, n_valid)
            i_invalid = min(i, n_invalid)
            user.set_user_name(invalid[i_invalid])
            installer.check_next_disabled()
            user.set_user_name(valid[i_valid])
            installer.check_next_disabled(disabled=False)

    def testAccessAndValidation(self):
        """
        Description:
            Test the access and validation of the user creation screen

        Expected results:
            - The forward / back navigation keeps the values inserted
            - Invalid values for user name, password and full name are detected
            - The next button is disabled when the values are invalid
            - Password strength is shown
            - The username is autogenerated from the full name
        """
        b = self.browser
        i = Installer(b, self.machine)
        p = Password(b, CREATE_ACCOUNT_ID_PREFIX)
        u = Users(b, self.machine)

        i.open()

        i.reach(i.steps.ACCOUNTS)

        # Fill in valid values
        password = "password"
        full_name = "Full Tester"

        p.set_password(password)
        p.set_password_confirm(password)
        u.set_full_name(full_name)
        # Check auto-generated user name
        u.check_user_name("ftester")

        user_name = "tester"
        u.set_user_name(user_name)

        p.check_password(password)
        p.check_password_confirm(password)
        u.check_full_name(full_name)
        u.check_user_name(user_name)
        i.check_next_disabled(disabled=False)

        # Test that you can move forward and going back keeps values
        i.next()
        i.back()
        p.check_password(password)
        p.check_password_confirm(password)
        u.check_full_name(full_name)
        u.check_user_name(user_name)

        # Test that moving back and forward keeps values
        i.back()
        i.next()
        p.check_password(password)
        p.check_password_confirm(password)
        u.check_full_name(full_name)
        u.check_user_name(user_name)

        # Test full name validation
        u.set_full_name("")
        i.check_next_disabled(disabled=False)
        u.set_full_name("colon:isforbidden")
        i.check_next_disabled()
        u.set_full_name(full_name)
        i.check_next_disabled(disabled=False)

        # Test root password validation
        u.enable_root_account(True)
        i.check_next_disabled()
        u.set_valid_root_password()
        i.check_next_disabled(disabled=False)
        u.set_valid_root_password(valid=False)
        i.check_next_disabled()
        u.enable_root_account(False)
        i.check_next_disabled(disabled=False)

        # Test user name validation
        # FIXME this should be tested by unit tests?
        invalid_user_names = [
            "",
            # reserved
            "root", "system", "daemon", "home",
            "33333",
            ".",
            "..",
            "-tester",
            "123",
            "$",
            "$tester",
            "tester?",
            "longer_than_32_characteeeeeeeeeeeeeeeeeeers",
        ]
        valid_user_names = [
            "tester-",
            "...",
            "12.3",
            "tester1",
            "tester$",
            "test-er",
            "test_er",
            "test.er",
            "_tester",
            ".tester",
            "_",
        ]
        self._test_user_name(u, i, valid_user_names, invalid_user_names)
        u.set_user_name(user_name)

        # Test password validation
        # No password set - allowed in the default policy
        p.set_password("")
        p.set_password_confirm("")
        p.check_pw_rule("length", "indeterminate")
        p.check_pw_rule("match", "success")
        i.check_next_disabled(disabled=False)
        # Start which pw which is too short
        p.set_password("abcd")
        p.check_pw_strength(None)
        i.check_next_disabled()
        p.check_pw_rule("length", "error")
        p.check_pw_rule("match", "error")
        # Default non-strict policy: length is advisory; confirmation must match
        p.set_password_confirm("abcd")
        p.check_pw_rule("match", "success")
        p.check_pw_rule("length", "error")
        i.check_next_disabled(disabled=False)
        # Make the pw 6 chars long
        p.set_password("ef", append=True, value_check=False)
        i.check_next_disabled()
        p.check_password("abcdef")
        p.check_pw_rule("length", "success")
        p.check_pw_rule("match", "error")
        p.check_pw_strength("weak")
        # Set the password confirm
        p.set_password_confirm("abcde")
        p.check_pw_rule("match", "error")
        p.set_password_confirm("abcdef")
        p.check_pw_rule("match", "success")
        p.check_pw_rule("length", "success")
        p.check_pw_strength("weak")
        i.check_next_disabled(disabled=False)

        # Test password strength
        p.set_password("Rwce82ybF7dXtCzFumanchu!!!!!!!!")
        p.check_pw_strength("strong")

        # Test review values
        p.set_password(password)
        p.set_password_confirm(password)
        u.set_full_name(full_name)
        u.set_user_name(user_name)
        r = Review(b, self.machine)
        i.reach(i.steps.REVIEW)
        r.check_account(f"{full_name} ({user_name})")


if __name__ == '__main__':
    test_main()
