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

from anacondalib import INSTALLER_VM_MEMORY_MB, VirtInstallMachineCase, disk_images, pixel_tests_ignore
from installer import Installer
from language import Language
from payload_dnf import PayloadDNF
from progress import Progress
from review import Review
from storage import EFI_PARTITION_DEFAULT_SIZE_MB, Storage
from testlib import test_main, timeout  # pylint: disable=import-error
from timezone import DateAndTime
from users import Users, create_user, override_user_interface_conf_keys


class TestKickstartAutomatedPause(VirtInstallMachineCase):
    """Destructive tests for kickstart-driven automated installations."""

    provision = {
        "machine1": {
            "kickstart_file_name": "TestKickstartAutomatedPause-testBasic.ks",
            "payload_type": "dnf",
            "memory_mb": INSTALLER_VM_MEMORY_MB,
            "pause_at_summary": True,
        },
    }

    def testBasic(self):
        """
        Description:
            Boot with ``inst.ks=TestKickstartAutomatedPause-testBasic.ks``
            with inst.pauseatsummary option.

        Expected results:
            - Automated install pause on the review step with the install action enabled.
            - Users specified in kickstart are correctly picked up on the Accounts step
              and cannot be changed.
            - Root settings cannot be changed when rootpw is in kickstart.
            - Timezone and timesource lines from kickstart appear on the date & time screen;
              NTP servers, pool, and NTS options are visible in the Configure NTP servers modal.
            - Scenario is preset to 'Use configured storage' and the partitioning appears on the Review storage section.
            - Environment and groups from the ``%packages`` section in the kickstart appear on the Software selection step.
        """
        b = self.browser
        m = self.machine
        i = Installer(b, m, scenario="use-configured-storage-kickstart")
        u = Users(b, m)
        dt = DateAndTime(b, m)
        r = Review(b, m)
        s = Storage(b, m)

        b.open("/cockpit/@localhost/anaconda-webui/index.html")
        b.wait_visible("#installation-next-btn:not([aria-disabled=true])")

        i.reach_on_sidebar(i.steps.LANGUAGE)
        i.reach(i.steps.DATE_TIME)

        tz = dt.dbus_get_timezone()
        region, city = tz.split("/", 1)
        dt.check_region(region)
        dt.check_city(city)
        dt.check_auto_date_time(dt.dbus_get_ntp_enabled())
        dt.check_ntp_modal_rows([
            {"hostname": "ntp.cesnet.cz", "pool": False, "nts": False},
            {"hostname": "nts-test.strangled.net", "pool": False, "nts": True},
            {"hostname": "0.pool.ntp.org", "pool": True, "nts": False},
        ])

        i.reach(i.steps.SOFTWARE_SELECTION)
        dnf = PayloadDNF(b)
        dnf.check_selected_environment("workstation-product-environment")
        dnf.check_group_selected("domain-client")

        i.reach(i.steps.INSTALLATION_METHOD)
        s.check_scenario_selected("use-configured-storage-kickstart")

        i.reach(i.steps.ACCOUNTS)

        # Verify kickstart users (read-only) and locked root
        u.check_readonly_users_section_visible()
        u.check_readonly_user_displayed("alice", full_name="Alice Admin")
        u.check_readonly_user_displayed("bob", full_name="Bob User")
        u.check_readonly_user_displayed("carol", full_name="Carol Dev")
        b.wait_visible("#anaconda-screen-accounts-root-account-enable-root-account:disabled")
        b.assert_pixels(".anaconda-wizard-step-anaconda-screen-accounts", "users-kickstarted", ignore=pixel_tests_ignore)

        i.reach(i.steps.REVIEW)

        r.check_storage_config("Use configured storage")
        dev1 = "vda"
        r.check_disk(dev1, "16.1 GB vda (Virtio Block Device)")
        r.check_disk_row(dev1, "/boot/efi", "vda1", EFI_PARTITION_DEFAULT_SIZE_MB, True, "efi")
        r.check_disk_row(dev1, "/", "vda5", "11.2 GB", True, "xfs")
        r.check_disk_row(dev1, "/boot", "vda2", "1.15 GB", True, "xfs")
        r.check_disk_row(dev1, "/home", "vda3", "2.15 GB", True, "xfs")
        r.check_disk_row(dev1, "swap", "vda4", "1.07 GB", True, "swap")

        i.check_next_disabled(disabled=False)
        b.assert_pixels(".anaconda-wizard-step-anaconda-screen-review", "review-kickstarted", ignore=pixel_tests_ignore)

    def testUserInterfaceCanChangeAccountsConf(self):
        """
        Description:
            Non-default ``can_change_root`` and ``can_change_users`` in ``anaconda.conf``
            (both ``True``): root must stay interactive and the user section must show the
            editable create-account UI instead of the kickstart read-only summary.

        Expected results:
            - Read-only kickstart user summary is not shown; create-account controls are present
            - Root enable checkbox is not disabled
        """
        b = self.browser
        m = self.machine
        i = Installer(b, m, scenario="use-configured-storage-kickstart")

        override_user_interface_conf_keys(self, can_change_root=True, can_change_users=True)
        b.open("/cockpit/@localhost/anaconda-webui/index.html")
        b.wait_visible("#installation-next-btn:not([aria-disabled=true])")
        i.reach_on_sidebar(i.steps.ACCOUNTS)

        b.wait_visible("#anaconda-screen-accounts-root-account-enable-root-account:not(:disabled)")
        b.wait_visible("#anaconda-screen-accounts-create-account-set-user-account:not(:disabled)")


class TestKickstartAutomatedWarning(VirtInstallMachineCase):
    """Destructive tests for kickstart-driven automated installations."""

    provision = {
        "machine1": {
            "kickstart_file_name": "TestKickstartAutomatedWarning-testBasic.ks",
            "payload_type": "dnf",
            "memory_mb": INSTALLER_VM_MEMORY_MB,
        },
    }

    def testBasic(self):
        """
        Description:
            Boot with ``inst.ks=TestKickstartAutomatedWarning-testBasic.ks``

        Expected results:
            - Automated installation starts despite storage warnings.
            - Storage warnings do not block auto-proceed (matching GTK/TUI behavior).
        """
        b = self.browser

        b.open("/cockpit/@localhost/anaconda-webui/index.html")
        b.wait_in_text("h2", "Installing")


class TestKickstartAutomated(VirtInstallMachineCase):
    """Destructive tests for kickstart-driven automated installations."""

    provision = {
        "machine1": {
            "kickstart_file_name": "TestKickstartAutomated-testBasic.ks",
            "payload_type": "dnf",
            "memory_mb": INSTALLER_VM_MEMORY_MB,
        },
    }

    @timeout(1200)
    def testBasic(self):
        """
        Description:
            Boot with ``inst.ks=TestKickstartAutomated-testBasic.ks``

        Expected results:
            - Automated installation starts.
        """
        b = self.browser
        p = Progress(b)

        b.open("/cockpit/@localhost/anaconda-webui/index.html")
        p.wait_done()

        self.handleReboot()


class TestKickstartAutomatedNonCriticalError(VirtInstallMachineCase):
    """Kickstart install with a missing package triggers a non-critical error dialog."""

    provision = {
        "machine1": {
            "kickstart_file_name": "TestKickstartAutomated-testNonCriticalError.ks",
            "payload_type": "dnf",
            "memory_mb": INSTALLER_VM_MEMORY_MB,
        },
    }

    def testMissingPackageAbort(self):
        """
        Description:
            Boot with kickstart that requests a non-existent package during
            ``%packages``.

        Expected results:
            - During installation the UI must show a continue/abort dialog.
            - The dialog mentions the missing package.
            - Choosing abort ends installation with a failure state.
        """
        b = self.browser
        p = Progress(b)

        b.open("/cockpit/@localhost/anaconda-webui/index.html")
        p.wait_installation_error_dialog("domain-client-nonexisting")
        b.wait_visible("#anaconda-screen-progress-installation-error-continue-btn")
        # Simulate remote reconnection
        b.reload()
        b.wait_visible(".pf-v6-c-page")
        p.wait_installation_error_dialog("domain-client-nonexisting")
        b.wait_visible("#anaconda-screen-progress-installation-error-continue-btn")
        p.respond_installation_error_abort()
        p.wait_installation_failed()


class TestKickstartAutomatedLangRuntime(VirtInstallMachineCase):
    """Kickstart install with language set is applied during runtime."""

    provision = {
        "machine1": {
            "kickstart_file_name": "TestKickstartAutomated-testLang.ks",
            "payload_type": "dnf",
            "memory_mb": INSTALLER_VM_MEMORY_MB,
            "pause_at_summary": True,
        },
    }

    def testRuntimeLangSet(self):
        """
        Description:
            Boot with kickstart setting language with lang command.

        Expected results:
            - The UI is translated into language set in kickstart.
        """
        b = self.browser
        r = Review(b, self.machine)

        b.open("/cockpit/@localhost/anaconda-webui/index.html")
        r.check_language("Italiano (Italia)")


class TestKickstartAutomatedFatalError(VirtInstallMachineCase):
    """Kickstart install with a fatal error shows the critical report dialog."""

    provision = {
        "machine1": {
            "kickstart_file_name": "TestKickstartAutomated-testFatalError.ks",
            "payload_type": "dnf",
            "memory_mb": INSTALLER_VM_MEMORY_MB,
        },
    }

    def testPreInstallScriptFailure(self):
        """
        Description:
            Boot with kickstart whose ``%pre-install --erroronfail`` script fails.

        Expected results:
            - Installation does not show the non-critical continue/abort dialog.
            - The critical error report dialog appears with script failure details.
        """
        b = self.browser
        p = Progress(b)

        b.open("/cockpit/@localhost/anaconda-webui/index.html")
        p.wait_installation_failed("This is a fatal error", timeout=1200)
        b.wait_not_present("#anaconda-screen-progress-installation-error-continue-btn")

        # Simulate remote reconnection — error should persist
        b.reload()
        b.wait_visible(".pf-v6-c-page")
        p.wait_installation_failed("This is a fatal error", timeout=10)


class TestKickstartAutomatedIncomplete(VirtInstallMachineCase):
    """Incomplete kickstart (missing storage, accounts, timezone, and lang) blocks install until filled in."""

    provision = {
        "machine1": {
            "kickstart_file_name": "TestKickstartAutomatedIncomplete-testBasic.ks",
            "payload_type": "dnf",
            "memory_mb": INSTALLER_VM_MEMORY_MB,
        },
    }

    def testBasic(self):
        """
        Description:
            Boot with ``inst.ks=TestKickstartAutomatedIncomplete-testBasic.ks``
            which has no partitioning, ``timezone``, ``lang``, or account commands.

        Expected results:
            - The review screen shows an incomplete-configuration warning,
              with storage, language, timezone, and account rows marked incomplete,
              and the install button is disabled.
            - After setting language, timezone, and accounts (navigating forward
              through the storage screen configures storage), all steps are complete.
        """
        b = self.browser
        m = self.machine
        i = Installer(b, m)
        r = Review(b, m)
        l = Language(b, m)
        dt = DateAndTime(b, m)
        u = Users(b, m)

        b.open("/cockpit/@localhost/anaconda-webui/index.html")

        b.wait_in_text(f"#{i.steps.REVIEW}-incomplete-configuration",
                       "Some installation steps still need to be completed before you can continue.")
        r.check_storage_incomplete()
        r.check_language_incomplete()
        r.check_timezone_incomplete()
        r.check_account_incomplete()

        i.check_next_disabled()

        # Navigate back to language page via sidebar and set a locale.
        i.reach_on_sidebar(i.steps.LANGUAGE)
        l.select_locale("en_US")

        # Navigate forward step by step via Next
        i.reach(i.steps.DATE_TIME)
        dt.set_auto_timezone(False)
        dt.select_region("Europe")
        dt.select_city("Prague")

        i.reach(i.steps.ACCOUNTS)
        create_user(b, m)
        # Workaround to stabilize the button state before calling the reach
        # to prevent race condition on state flipping
        u.enable_user_account(False)
        u.enable_user_account(True)

        i.reach(i.steps.REVIEW)

        b.wait_in_text(f"#{i.steps.REVIEW}-target-system-account",
                       "The following user will be created")
        r.check_timezone("Europe/Prague")


class TestKickstartAutomatedRAID(VirtInstallMachineCase):
    """Kickstart automated install with RAID layout from kickstart-tests raid-1.ks."""

    provision = {
        "machine1": {
            "kickstart_file_name": "TestKickstartAutomatedRAID-testRAID1.ks",
            "payload_type": "dnf",
            "memory_mb": INSTALLER_VM_MEMORY_MB,
            "pause_at_summary": True,
        },
    }

    def setUp(self):
        super().setUp()
        s = Storage(self.browser, self.machine)
        s.dbus_reset_selected_disks()
        s.dbus_scan_devices()

    @disk_images([("", 15), ("", 15)])
    def testRAID1(self):
        """
        Description:
            Boot with ``inst.ks=TestKickstartAutomatedRAID-testRAID1.ks`` on two disks

        Expected results:
            - Automated install pauses on review without bootloader warnings;
              /boot on vda, RAID1 swap, and RAID0 /.
        """
        b = self.browser
        r = Review(b, self.machine)

        b.open("/cockpit/@localhost/anaconda-webui/index.html")
        r.check_disk_row("vda", "/boot", "vda1", "1.15 GB", True, "xfs")
        r.check_disk_row("vda", "/", "vda3, vdb3, RAID", "27.7 GB", True, "xfs")


if __name__ == "__main__":
    test_main()
