#!/usr/bin/python3

# checkpoint:
# 1. osbuild-composer.socket can be started and enabled
# 2. no blueprint by default and can add and delete blueprint
import string
import random

import composerlib
import testlib


@testlib.nondestructive
class TestService(composerlib.ComposerCase):

    def setUp(self):
        # remove all blueprints
        # stop and disable osbuild-composer.socket service
        super().setUp()
        m = self.machine

        m.execute("""
            for bp in $(composer-cli blueprints list); do
                composer-cli blueprints delete $bp
            done
            systemctl disable --now osbuild-composer.socket osbuild-composer.service osbuild-local-worker.socket
        """)

    def testBasic(self):
        b = self.browser

        self.login_and_go("/composer", superuser=True)
        b.wait_visible("#main")

        # start and enable osbuild-composer from UI
        b.click("button:contains('Start')")
        # work around .pf-c-empty-state element disappear in one second
        b.wait_in_text("#main", "No blueprints")
        b.wait_text(".pf-c-empty-state__content .pf-c-title", "No blueprints")

        # create blueprint with random name and description
        name_template = string.ascii_letters + string.digits + "-" + "_"
        bp_name = "".join(random.sample(name_template, 20))
        desc_template = string.ascii_letters + string.digits + " "
        bp_desc = "".join(random.sample(desc_template, 40))
        b.click(".pf-c-empty-state button:contains('Create blueprint')")
        b.wait_visible("div[role=dialog] #cmpsr-modal-crt-blueprint")
        # blueprint name
        b.set_input_text("#textInput-modal-markup", bp_name)
        # blueprint description
        b.set_input_text("#textInput2-modal-markup", bp_desc)
        # create
        b.click("#create-blueprint-modal-create-button")
        b.wait_not_present("div[role=dialog] #cmpsr-modal-crt-blueprint")

        # wait for all available compontents visible
        with b.wait_timeout(300):
            b.wait_visible("ul[data-list=inputs]")
        # search for openssh-server pacakge
        b.set_input_text("#cmpsr-blueprint-input-filter", "openssh-server", blur=False)
        b.key_press("\r")
        with b.wait_timeout(120):
            b.wait_text("#openssh-server-input", "openssh-server")
        # add it to blueprint
        b.click("li[data-input=openssh-server] .fa-plus")
        # wait for package added
        b.wait_visible("#blueprint-tabs")
        b.wait_visible("li[data-component=openssh-server]")
        # click commit button
        b.click(".cmpsr-header__actions .btn-primary")
        # click commit button
        b.click("#cmpsr-modal-pending-changes .modal-footer .btn-primary")
        b.wait_not_present("#cmpsr-modal-pending-changes")

        # wait for notification
        b.wait_visible("#alertBlueprintCommitSucceeded")
        # click close button
        b.click("#alertBlueprintCommitSucceeded .pf-c-alert__action button")
        b.wait_not_present("#alertBlueprintCommitSucceeded")

        # click back to blueprint link
        b.click("a:contains('Back to blueprints')")

        # new added blueprint should be there with correct name and description
        b.wait_visible("li[data-blueprint={}]".format(bp_name))
        b.wait_text("#{}-name".format(bp_name), bp_name)
        b.wait_text("div[data-description]", bp_desc)

        # delete blueprint, cancel first
        b.click("#{}-kebab".format(bp_name))
        b.wait_attr("#{}-kebab".format(bp_name), "aria-expanded", "true")
        b.click("ul[aria-labelledby={}-kebab] a:contains('Delete')".format(bp_name))
        b.wait_attr("#{}-kebab".format(bp_name), "aria-expanded", "false")
        b.click("#cmpsr-modal-delete button:contains('Cancel')")
        b.wait_not_present("#cmpsr-modal-delete")
        # delete here
        b.click("#{}-kebab".format(bp_name))
        b.wait_attr("#{}-kebab".format(bp_name), "aria-expanded", "true")
        b.click("ul[aria-labelledby={}-kebab] a:contains('Delete')".format(bp_name))
        b.wait_attr("#{}-kebab".format(bp_name), "aria-expanded", "false")
        b.click("#cmpsr-modal-delete button:contains('Delete')")
        b.wait_not_present("#cmpsr-modal-delete")
        b.wait_not_present("li[data-blueprint={}]".format(bp_name))
        # no blueprint here
        b.wait_in_text("#main", "No blueprints")

        # collect code coverage result
        self.check_coverage()

    def testNoEnable(self):
        b = self.browser
        m = self.machine

        self.login_and_go("/composer", superuser=True)
        b.wait_visible("#main")

        # do not enable osbuild-composer
        b.click(".checkbox input")
        # start and enable osbuild-composer from UI
        b.click("button:contains('Start')")
        # work around .pf-c-empty-state element disappear in one second
        b.wait_in_text("#main", "No blueprints")
        b.wait_text(".pf-c-empty-state__content .pf-c-title", "No blueprints")
        # osbuild-composer should be disabled
        is_enabled = m.execute("systemctl is-enabled osbuild-composer.socket || true").rstrip()
        self.assertEqual(is_enabled, "disabled")
        # osbuild-composer should be started
        is_active = m.execute("systemctl is-active osbuild-composer.socket").rstrip()
        self.assertEqual(is_active, "active")

        # collect code coverage result
        self.check_coverage()

    def testNonAdminUser(self):
        b = self.browser

        self.login_and_go("/composer", superuser=False)
        b.wait_visible("#main")

        # Start button can be disabled button or clickable button
        b.wait_visible("button:contains('Start')")

        # disable this test due to https://github.com/osbuild/cockpit-composer/issues/941
        # if "disabled" not in b.attr("button:contains('Start')", "class"):
        #     b.click("button:contains('Start')")
        #     # error message
        #     b.wait_in_text(".cmpsr-alert-blank-slate", "Not permitted to perform this action.")
        #     # Start button still there
        #     b.wait_visible("button:contains('Start')")
        # We expect multiple permission denied journal messages from the api calls
        self.allow_journal_messages(".*: couldn't connect: Could not connect: Permission denied")
        # collect code coverage result
        self.check_coverage()


if __name__ == '__main__':
    testlib.test_main()
