"""
Launches SeleniumBase Behave Commander | GUI for Behave.

Usage:
      seleniumbase behave-gui [OPTIONAL PATH or TEST FILE]
             sbase behave-gui [OPTIONAL PATH or TEST FILE]

Examples:
      sbase behave-gui
      sbase behave-gui features/
      sbase behave-gui features/calculator.feature

Output:
      Launches SeleniumBase Behave Commander | GUI for Behave.
"""
import colorama
import subprocess
import sys
import tkinter as tk
from seleniumbase.fixtures import shared_utils
from tkinter.scrolledtext import ScrolledText

if sys.version_info <= (3, 8):
    current_version = ".".join(str(ver) for ver in sys.version_info[:3])
    raise Exception(
        "\n* SBase Commander requires Python 3.8 or newer!"
        "\n** You are currently using Python %s" % current_version
    )


def set_colors(use_colors):
    c0 = ""
    c1 = ""
    c2 = ""
    c3 = ""
    c4 = ""
    c5 = ""
    c6 = ""
    cr = ""
    if use_colors:
        c0 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX
        c1 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX
        c2 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX
        c3 = colorama.Fore.BLACK + colorama.Back.LIGHTGREEN_EX
        c4 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX
        c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX
        c6 = colorama.Fore.MAGENTA + colorama.Back.LIGHTYELLOW_EX
        cr = colorama.Style.RESET_ALL
    return c0, c1, c2, c3, c4, c5, c6, cr


def send_window_to_front(root):
    root.lift()
    root.attributes("-topmost", True)
    root.after_idle(root.attributes, "-topmost", False)


def do_behave_run(
    root,
    tests,
    selected_tests,
    command_string,
    browser_string,
    rs_string,
    quiet_mode,
    demo_mode,
    mobile_mode,
    dashboard,
    headless,
    save_screenshots,
    additional_options,
):
    total_tests = len(tests)
    total_selected_tests = 0
    test_to_run = None
    for selected_test in selected_tests:
        if selected_tests[selected_test].get():
            total_selected_tests += 1

    full_run_command = '"%s" -m behave' % sys.executable
    if total_selected_tests == 0 or total_tests == total_selected_tests:
        if command_string:
            full_run_command += " "
            full_run_command += command_string
    else:
        for test_number, test in enumerate(tests):
            if selected_tests[test_number].get():
                full_run_command += " "
                test_to_run = test
                if test.startswith("(GROUP)  "):
                    test_to_run = test.split("(GROUP)  ")[1]
                    full_run_command += test_to_run.split(" => ")[0]
                else:
                    full_run_command += test.split(" => ")[0]

    if "(-D edge)" in browser_string:
        full_run_command += " -D edge"
    elif "(-D firefox)" in browser_string:
        full_run_command += " -D firefox"
    elif "(-D safari)" in browser_string:
        full_run_command += " -D safari"

    if "(-D rs)" in rs_string:
        full_run_command += " -D rs"
    elif "(-D rs -D crumbs)" in rs_string:
        full_run_command += " -D rs -D crumbs"
    elif "(-D rcs)" in rs_string:
        full_run_command += " -D rcs"
    elif "(-D rcs -D crumbs)" in rs_string:
        full_run_command += " -D rcs -D crumbs"

    if quiet_mode:
        full_run_command += " --quiet"

    if demo_mode:
        full_run_command += " -D demo"

    if mobile_mode:
        full_run_command += " -D mobile"

    if dashboard:
        full_run_command += " -D dashboard"

    if headless:
        full_run_command += " -D headless"
    elif shared_utils.is_linux():
        full_run_command += " -D gui"

    if save_screenshots:
        full_run_command += " -D screenshot"

    additional_options_list = additional_options.split(" ")
    dash_T_needed = False
    if (
        "-T" not in additional_options_list
        and "--no-timings" not in additional_options_list
        and "--show-timings" not in additional_options_list
    ):
        dash_T_needed = True
    dash_k_needed = False
    if (
        "-k" not in additional_options_list
        and "--no-skipped" not in additional_options_list
        and "--show-skipped" not in additional_options_list
    ):
        dash_k_needed = True
    additional_options = additional_options.strip()
    if additional_options:
        full_run_command += " "
        full_run_command += additional_options
    if dash_T_needed:
        full_run_command += " -T"
    if dash_k_needed:
        full_run_command += " -k"

    print(full_run_command)
    if not additional_options or " " not in additional_options:
        subprocess.Popen(full_run_command, shell=True)
    else:
        proc = subprocess.Popen(
            full_run_command, stderr=subprocess.PIPE, shell=True
        )
        (output, error) = proc.communicate()
        if error and proc.returncode == 2:
            if str(error).startswith("b'") and str(error).endswith("\\n'"):
                error = str(error)[2:-3]
            elif str(error).startswith("b'") and str(error).endswith("'"):
                error = str(error)[2:-1]
            else:
                error = str(error)
            error = error.replace("\\n", "\n")
            print(error)
    send_window_to_front(root)


def create_tkinter_gui(tests, command_string, t_count, f_count, s_tests):
    root = tk.Tk()
    root.title("SeleniumBase Behave Commander | GUI for Behave")
    if shared_utils.is_windows():
        root.minsize(820, 640)
    else:
        root.minsize(820, 656)
    tk.Label(root, text="").pack()

    options_list = [
        "Use Chrome Browser  (Default)",
        "Use Edge Browser  (-D edge)",
        "Use Firefox Browser  (-D firefox)",
    ]
    if shared_utils.is_mac():
        options_list.append("Use Safari Browser  (-D safari)")
    brx = tk.StringVar(root)
    brx.set(options_list[0])
    question_menu = tk.OptionMenu(root, brx, *options_list)
    question_menu.pack()

    options_list = [
        "New Session Per Test  (Default)",
        "Reuse Session for ALL the tests  (-D rs)",
        "Reuse Session and clear cookies  (-D rs -D crumbs)",
        "Reuse Session in the SAME class/feature  (-D rcs)",
        "Reuse Session in class and clear cookies  (-D rcs -D crumbs)",
    ]
    rsx = tk.StringVar(root)
    rsx.set(options_list[0])
    question_menu = tk.OptionMenu(root, rsx, *options_list)
    question_menu.pack()

    qmx = tk.IntVar()
    chk = tk.Checkbutton(
        root, text="Quiet Mode  (--quiet)", variable=qmx, pady=0
    )
    chk.pack()

    dmx = tk.IntVar()
    chk = tk.Checkbutton(
        root, text="Demo Mode  (-D demo)", variable=dmx, pady=0
    )
    chk.pack()

    mmx = tk.IntVar()
    chk = tk.Checkbutton(
        root, text="Mobile Mode  (-D mobile)", variable=mmx, pady=0
    )
    chk.pack()

    dbx = tk.IntVar()
    chk = tk.Checkbutton(
        root, text="Dashboard  (-D dashboard)", variable=dbx, pady=0
    )
    chk.pack()
    chk.select()

    hbx = tk.IntVar()
    chk = tk.Checkbutton(
        root, text="Headless Browser  (-D headless)", variable=hbx, pady=0
    )
    chk.pack()

    ssx = tk.IntVar()
    chk = tk.Checkbutton(
        root, text="Save Screenshots  (-D screenshot)", variable=ssx, pady=0
    )
    chk.pack()

    tk.Label(root, text="").pack()
    plural = "s"
    if f_count == 1:
        plural = ""
    run_display = (
        "Select from %s rows (%s feature%s with %s scenarios):  "
        "(All tests will run if none are selected)"
        % (len(tests), f_count, plural, t_count)
    )
    if t_count == 1:
        run_display = "Only ONE TEST was found and will be run:"
        tests = s_tests
    tk.Label(root, text=run_display, bg="yellow", fg="magenta").pack()
    text_area = ScrolledText(
        root, width=100, height=12, wrap="word", state=tk.DISABLED
    )
    text_area.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
    count = 0
    ara = {}
    for row in tests:
        row += " " * 200
        ara[count] = tk.IntVar()
        cb = None
        if shared_utils.is_windows():
            cb = tk.Checkbutton(
                text_area,
                text=(row),
                bg="white",
                fg="black",
                anchor="w",
                pady=0,
                borderwidth=1,
                highlightthickness=1,
                variable=ara[count],
            )
        else:
            cb = tk.Checkbutton(
                text_area,
                text=(row),
                bg="white",
                fg="black",
                anchor="w",
                pady=0,
                variable=ara[count],
            )
        text_area.window_create("end", window=cb)
        text_area.insert("end", "\n")
        count += 1

    tk.Label(root, text="").pack()
    additional_options = ""
    aopts = tk.StringVar(value=additional_options)
    tk.Label(
        root,
        text='Additional "behave" Options:  (Eg. "-D incognito --junit")',
        bg="yellow", fg="blue",
    ).pack()
    entry = tk.Entry(root, textvariable=aopts)
    entry.pack()
    entry.focus()
    entry.bind(
        "<Return>",
        (
            lambda _: do_behave_run(
                root,
                tests,
                ara,
                command_string,
                brx.get(),
                rsx.get(),
                qmx.get(),
                dmx.get(),
                mmx.get(),
                dbx.get(),
                hbx.get(),
                ssx.get(),
                aopts.get(),
            )
        ),
    )
    tk.Button(
        root,
        text="Run Selected Tests",
        fg="green",
        command=lambda: do_behave_run(
            root,
            tests,
            ara,
            command_string,
            brx.get(),
            rsx.get(),
            qmx.get(),
            dmx.get(),
            mmx.get(),
            dbx.get(),
            hbx.get(),
            ssx.get(),
            aopts.get(),
        ),
    ).pack()
    tk.Label(root, text="\n").pack()

    # Bring form window to front
    send_window_to_front(root)
    # Use decoy to set correct focus on main window
    decoy = tk.Tk()
    decoy.geometry("1x1")
    decoy.iconify()
    decoy.update()
    decoy.deiconify()
    decoy.destroy()
    # Start tkinter
    root.mainloop()


def main():
    use_colors = True
    if shared_utils.is_linux():
        use_colors = False
    c0, c1, c2, c3, c4, c5, c6, cr = set_colors(use_colors)
    command_args = sys.argv[2:]
    command_string = " ".join(command_args)
    message = ""
    message += c2
    message += "*"
    message += c4
    message += " Starting the "
    message += c0
    message += "Selenium"
    message += c1
    message += "Base"
    message += c2
    message += " "
    message += c6
    message += "Behave"
    message += c4
    message += " "
    message += c3
    message += "Commander"
    message += c4
    message += " GUI App"
    message += c2
    message += "..."
    message += cr
    print(message)
    command_string = command_string.replace("--quiet", "")
    command_string = command_string.replace("-q", "")
    proc = subprocess.Popen(
        '"%s" -m behave -d %s --show-source'
        % (sys.executable, command_string),
        stdout=subprocess.PIPE,
        shell=True,
    )
    (output, error) = proc.communicate()
    if error:
        error_msg = "Error collecting tests: %s" % str(error)
        error_msg = c5 + error_msg + cr
        print(error_msg)
        return
    filename = None
    feature_name = None
    scenario_name = None
    f_tests = []  # Features
    s_tests = []  # Scenarios
    tests = []  # All tests
    file_scenario_count = {}
    f_count = 0
    s_count = 0
    t_count = 0
    if shared_utils.is_windows():
        output = output.decode("latin1")
    else:
        output = output.decode("utf-8")
    for row in output.replace("\r", "").split("\n"):
        if row.startswith("Feature: "):
            if f_count > 0:
                file_scenario_count[str(f_count)] = s_count
            f_count += 1
            s_count = 0
        elif row.startswith("  Scenario: "):
            s_count += 1
            file_scenario_count[str(f_count)] = s_count
        elif row.startswith("  Scenario Outline: "):
            s_count += 1
            file_scenario_count[str(f_count)] = s_count
    file_scenario_count[str(f_count)] = s_count
    f_count = 0
    s_count = 0
    for row in output.replace("\r", "").split("\n"):
        if row.startswith("Feature: "):
            f_count += 1
            feature_name = row.split("Feature: ")[1]
            if " # features/" in feature_name:
                filename = feature_name.split(" # features/")[-1]
                filename = "features/" + filename.split(":")[0]
                feature_name = feature_name.split(" # features/")[0]
            elif " # features\\" in feature_name:
                filename = feature_name.split(" # features\\")[-1]
                filename = "features\\" + filename.split(":")[0]
                feature_name = feature_name.split(" # features\\")[0]
            else:
                filename = feature_name.split(" # ")[-1]
                filename = filename.split(":")[0]
                feature_name = feature_name.split(" # ")[-1]
            s_count = file_scenario_count[str(f_count)]
            filename = filename.strip()
            t_name = "(GROUP)  %s => %s" % (filename, feature_name)
            t_name += "  <>  (%s Total)" % s_count
            f_tests.append(t_name)
        elif (
            row.startswith("  Scenario: ")
            or row.startswith("  Scenario Outline: ")
        ):
            t_count += 1
            line_num = row.split(":")[-1]
            scenario_name = None
            if row.startswith("  Scenario: "):
                scenario_name = row.split("  Scenario: ")[-1]
            else:
                scenario_name = row.split("  Scenario Outline: ")[-1]
            if " -- @" in scenario_name:
                scenario_name = scenario_name.split(" # ")[0].rstrip()
            elif " # features/" in scenario_name:
                scenario_name = scenario_name.split(" # features/")[0]
            else:
                scenario_name = scenario_name.split(" # ")[0]
            scenario_name = scenario_name.strip()
            s_tests.append("%s:%s => %s" % (filename, line_num, scenario_name))
    tests = f_tests + s_tests
    if not tests:
        err_msg_0 = c5 + "ERROR:" + cr + "\n"
        err_msg_1 = '  No "behave" tests found! Expecting "*.feature" files!'
        err_msg_1 = c6 + err_msg_1 + cr + "\n"
        err_msg_2 = '  "*.feature" files would live in a "features/" folder.'
        err_msg_2 = c6 + err_msg_2 + cr + "\n"
        err_msg_3 = "Exiting SBase Behave Commander..."
        err_msg_3 = c5 + err_msg_3 + cr
        error_msg = err_msg_0 + err_msg_1 + err_msg_2 + err_msg_3
        print(error_msg)
        return

    create_tkinter_gui(tests, command_string, t_count, f_count, s_tests)


if __name__ == "__main__":
    print('To open SBase Behave Commander, type "sbase behave-gui"')
