#!/usr/bin/python3
import argparse
import sys
import os
import subprocess
import testvm


def wait_for_selenium_running(machine, host, port=4444):
    WAIT_SELENIUM_RUNNING = """#!/bin/sh
until curl -s --connect-timeout 3 http://%s:%d >/dev/null; do
sleep 0.5;
done;
""" % (host, port)
    with testvm.Timeout(
            seconds=600,
            error_message="Timeout while waiting for selenium to start"):
        machine.execute(script=WAIT_SELENIUM_RUNNING)


# Microsoftwebdriver has to be ready before all workers get launched.
def wait_for_edge_running(machine, host, port=4444):
    WAIT_EDGE_RUNNING = """#!/bin/sh
until curl -s --connect-timeout 3 \
http://%s:%d/grid/console | grep "browserName: MicrosoftEdge" >/dev/null; do
sleep 0.5;
done;
""" % (host, port)
    with testvm.Timeout(
            seconds=600,
            error_message="Timeout while waiting for selenium to start"):
        machine.execute(script=WAIT_EDGE_RUNNING)


def run_webdriver_tests(machine, env=[]):
    """ Execute end to end test on the machine with the passed environment
        variables. For example:
        run_webdriver_tests(machine,
            ["HUB=" + selenium_addr, "BASE_URL=" + base_rul, "BROWSER=firefox"]
        )
        Return success of the tests
        (True: all passed, False: at least one failed)
    """
    return_state = True
    cmd_parts = ["cd /root/end-to-end;"] + env + ["npm run test"]

    try:
        machine.execute(" ".join(cmd_parts), timeout=3600)
    except (subprocess.CalledProcessError, RuntimeError) as e:
        sys.stderr.write("ERROR: %s\n" % e)
        return_state = False
    return return_state


def run_e2e(verbose, image, browser, cpus, memory, sit):
    # settings on composer VM
    network = testvm.VirtNetwork(0)
    composer = testvm.VirtMachine(verbose=verbose, image=image,
                                  networking=network.host(),
                                  memory_mb=memory, cpus=cpus)
    selenium = windows = None
    networking_selenium = network.host(forward={"5901": 5901, "5902": 5902})

    # settings on selenium VM
    if 'edge' in browser:
        windows = testvm.VirtMachine(image='windows-10', verbose=verbose,
                                     networking=network.host(),
                                     memory_mb=4096, cpus=4)
        # wdio.conf.js needs key word MicrosoftEdge for edge browser
        browser = 'MicrosoftEdge'
    else:
        selenium = testvm.VirtMachine(image="selenium", verbose=verbose,
                                      networking=networking_selenium)
    try:
        composer.start()
        if selenium:
            # actually wait here, because starting selenium takes a while
            selenium.pull(selenium.image_file)
            selenium.start()
            selenium.wait_boot()
            selenium.set_address("10.111.112.10/20")
            selenium.upload(["selenium_start.sh"], "/root/")

            # start selenium on the server
            selenium.execute("/root/selenium_start.sh")
        elif windows:
            selenium = windows
            selenium.pull(selenium.image_file)
            selenium.start()

        composer.wait_boot()
        composer.set_address("10.111.113.1/20")
        composer.upload(["end-to-end"], "/root/")
        composer.execute("cd /root/end-to-end && npm install", timeout=600)

        if selenium and 'MicrosoftEdge' in browser:
            composer.dhcp_server(range=['10.111.112.10', '10.111.112.10'])

        wait_for_selenium_running(composer, "10.111.112.10")
        if selenium and 'MicrosoftEdge' in browser:
            wait_for_edge_running(composer, "10.111.112.10")

        # Now actually run the tests
        selenium_address = "10.111.112.10" if selenium else ""

        # networking_selenium["forward"] is a dict like:
        # {'5901': '127.0.0.2:5903', '5902': '127.0.0.2:5904'}
        if "DEBUG_TEST" in os.environ:
            try:
                if browser == "chrome":
                    subprocess.Popen(["vncviewer",
                                     networking_selenium["forward"]["5901"]])
                if browser == "firefox":
                    subprocess.Popen(["vncviewer",
                                     networking_selenium["forward"]["5902"]])
            except OSError as e:
                if e.errno == os.errno.ENOENT:
                    print("Please install vncviewer "
                          "by 'dnf install tigervnc'")
                else:
                    print("Something else went wrong while "
                          "trying to run `vncviewer`")
                    raise

        base_url = "http://10.111.113.1:9090"
        env = [
                "HUB=" + selenium_address,
                "BASE_URL=" + base_url,
                "BROWSER=" + browser,
                "TEST_OS=" + image
              ]
        success = run_webdriver_tests(composer, env=env)

        # Download report and screenshot to environment $TEST_ATTACHMENTS
        report_dir = os.environ.get("TEST_ATTACHMENTS", os.getcwd())
        os.makedirs(report_dir, exist_ok=True)
        composer.download_dir("/root/end-to-end/wdio_report", report_dir)
        # Save cockpit and lorax-composer package version information
        cmd = 'rpm -qa name="cockpit*" "lorax*" > /tmp/composer.covered'
        composer.execute(cmd, timeout=600)
        composer.download("/tmp/composer.covered", report_dir)

        # Report code coverage result to codecov.io
        codecov_token_file = os.path.expanduser("~/.config/codecov-token")
        if os.path.isfile(codecov_token_file) and success:
            composer.download_dir("/root/end-to-end/.nyc_output/", os.getcwd())
            token = open(codecov_token_file).read().replace("\n", "")
            os.environ["CODECOV_TOKEN"] = token
            subprocess.run("curl -s https://codecov.io/bash | bash",
                           shell=True)

        if not success and sit:
            sys.stderr.write(composer.diagnose() + "\n")
            input("Press RET to continue... ")

        return success
    finally:
        composer.kill()
        if selenium:
            selenium.kill()


def main():
    parser = argparse.ArgumentParser(description='Run end to end test')
    parser.add_argument('-v', '--verbose', action='store_true',
                        help='Display verbose details')
    parser.add_argument('-M', '--memory', type=int, default=1024,
                        help='Memory (in MiB) of the target machine')
    parser.add_argument('-C', '--cpus', type=int, default=1,
                        help='Number of cpus in the target machine')
    parser.add_argument("-b", "--browser",
                        choices=['none', 'firefox', 'chrome', 'edge'],
                        default='firefox',
                        help="selenium browser choice - in case of none, \
                            selenium isn't started")
    parser.add_argument("--sit", dest='sit', action='store_true',
                        help="Sit and wait after test failure")

    parser.add_argument('image', help='Image run composer against')
    args = parser.parse_args()

    if run_e2e(args.verbose,
               args.image,
               args.browser,
               args.cpus,
               args.memory,
               args.sit):
        return 0
    else:
        return 1


if __name__ == '__main__':
    sys.exit(main())
