import xml.etree.ElementTree as ET
from os import environ
from pathlib import Path

import requests
from marionette_driver import expected
from marionette_driver.by import By
from marionette_driver.wait import Wait
from marionette_harness import MarionetteTestCase


class TestApplyUpdate(MarionetteTestCase):
    def setUp(self):
        MarionetteTestCase.setUp(self)
        self.about_fx_url = "chrome://browser/content/aboutDialog.xhtml"

    def test_update_is_applied(self):
        self.marionette.set_pref("app.update.disabledForTesting", False)
        self.marionette.set_pref("app.update.log", True)
        self.marionette.set_pref("remote.log.level", "Trace")
        self.marionette.set_pref("remote.system-access-check.enabled", False)
        self.marionette.navigate(self.about_fx_url)

        self.marionette.set_context(self.marionette.CONTEXT_CHROME)
        update_url = self.marionette.execute_async_script(
            """
            (async function() {
                const checker = Cc["@mozilla.org/updates/update-checker;1"].getService(Ci.nsIUpdateChecker);
                let url = await checker.wrappedJSObject.getUpdateURL(checker.BACKGROUND_CHECK);
                return url;
            })().then(arguments[0]);
            """
        )

        response = requests.get(f"{update_url}?force=1")
        response.raise_for_status()

        # Get the target version
        root = ET.fromstring(response.text)
        target_ver = root[0].get("appVersion")

        if environ.get("UPLOAD_DIR"):
            version_info_log = Path(
                environ.get("UPLOAD_DIR"), environ.get("VERSION_LOG_FILENAME")
            )
            if version_info_log.is_file():
                with version_info_log.open("a") as fh:
                    fh.write(f"Target version: {target_ver}\n")

        self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
        initial_ver = self.marionette.find_element(By.ID, "version").text

        # Try runs build unsigned updates, releases can't update on unsigned MARs
        # ...so we're just going to check that balrog gives a reasonably-named file that exists
        if environ.get("BALROG_STAGING"):
            print("staging")
            patch_url = root[0][0].get("URL")
            assert (
                f"{target_ver}" in patch_url
            ), f"{target_ver} not in patch url: {patch_url}"
            patch_response = requests.get(patch_url)
            patch_response.raise_for_status()
            return True

        Wait(self.marionette, timeout=10).until(
            expected.element_displayed(By.ID, "downloadAndInstallButton")
        )
        self.marionette.find_element(By.ID, "downloadAndInstallButton").click()

        # Long timeouts are a known issue - Bug 2000040
        Wait(self.marionette, timeout=240).until(
            expected.element_displayed(By.ID, "updateButton")
        )

        self.marionette.restart(
            callback=lambda: self.marionette.find_element(By.ID, "updateButton").click()
        )

        self.marionette.set_pref("app.update.disabledForTesting", False)
        self.marionette.set_pref("app.update.log", True)
        self.marionette.set_pref("remote.log.level", "Trace")
        self.marionette.set_pref("remote.system-access-check.enabled", False)
        self.marionette.navigate(self.about_fx_url)

        Wait(self.marionette, timeout=240).until(
            expected.element_displayed(By.ID, "noUpdatesFound")
        )

        # Mini smoke test
        try:
            print(f"Updated from {initial_ver} to {target_ver}")
        except UnicodeEncodeError:
            print(f"Updated to {target_ver}")
        version_text = self.marionette.find_element(By.ID, "version").text
        assert target_ver in version_text
        assert len(self.marionette.window_handles) == 1
        self.marionette.open("tab")
        Wait(self.marionette, timeout=20).until(
            lambda _: len(self.marionette.window_handles) == 2
        )
        self.marionette.close()
        Wait(self.marionette, timeout=20).until(
            lambda _: len(self.marionette.window_handles) == 1
        )

    def tearDown(self):
        MarionetteTestCase.tearDown(self)
