from . import utils as setting_utils
from ....exceptions import HTTPConnectionError
from ....foundation.cli.ui.setting.OptionsHandler import OptionsHandler
from ....manager.Kathara import Kathara
from ....setting.Setting import Setting, DEFAULTS, AVAILABLE_DEBUG_LEVELS
from ....trdparty.consolemenu import *
from ....trdparty.consolemenu.items import *
from ....trdparty.consolemenu.validators.regex import RegexValidator
from ....utils import exec_by_platform
from ....validator.ImageValidator import ImageValidator
from ....validator.TerminalValidator import TerminalValidator
from ....webhooks.DockerHubApi import DockerHubApi

SHELLS_HINT = ["/bin/bash", "/bin/sh", "/bin/ash", "/bin/ksh", "/bin/zsh", "/bin/fish", "/bin/csh", "/bin/tcsh"]
TERMINALS_OSX = ["Terminal", "iTerm"]


class CommonOptionsHandler(OptionsHandler):
    def add_items(self, current_menu: ConsoleMenu, menu_formatter: MenuFormatBuilder) -> None:
        # Manager Submenu
        managers = Kathara.get_available_managers_name()
        manager_type = Setting.get_instance().manager_type

        choose_manager_string = "Choose default manager"
        manager_menu = SelectionMenu(
            strings=[],
            title=choose_manager_string,
            subtitle=lambda: "Current: %s" % managers[manager_type],
            prologue_text="""Manager is the Engine used to run Kathara labs.
            
                          Default is `%s`.""" % managers[DEFAULTS['manager_type']],
            formatter=menu_formatter
        )

        for name, formatted_name in managers.items():
            manager_menu.append_item(
                FunctionItem(
                    text=formatted_name,
                    function=self.update_manager_value,
                    args=[current_menu, name],
                    should_exit=True
                )
            )

        manager_item = SubmenuItem(choose_manager_string, manager_menu, current_menu)

        # Image Selection Submenu
        image_string = "Choose default image"
        select_image_menu = SelectionMenu(
            strings=[],
            title=image_string,
            subtitle=setting_utils.current_string("image"),
            prologue_text="""Default Docker image when you start a network scenario or a single Kathara device.
                          
                          Default is `%s`.""" % DEFAULTS['image'],
            formatter=menu_formatter
        )

        try:
            for image_name in DockerHubApi.get_tagged_images():
                select_image_menu.append_item(
                    FunctionItem(
                        text=image_name,
                        function=setting_utils.update_setting_value,
                        args=['image', image_name],
                        should_exit=True
                    )
                )
        except HTTPConnectionError:
            pass

        select_image_menu.append_item(
            FunctionItem(
                text="Choose another image",
                function=setting_utils.update_value,
                args=['image',
                      ImageValidator(),
                      'Write the name of a Docker image available on Docker Hub:',
                      None
                      ],
                should_exit=True
            )
        )
        submenu_item = SubmenuItem(image_string, select_image_menu, current_menu)

        # Open Terminals Option
        open_terminals_string = "Automatically open terminals on startup"
        open_terminals_menu = SelectionMenu(
            strings=[],
            title=open_terminals_string,
            subtitle=setting_utils.current_bool("open_terminals"),
            prologue_text="""Determines if the device terminal should be opened when starting it.
            
                          Default is %s."""
                          % setting_utils.format_bool(DEFAULTS['open_terminals']),
            formatter=menu_formatter
        )

        open_terminals_menu.append_item(
            FunctionItem(
                text="Yes",
                function=setting_utils.update_setting_value,
                args=['open_terminals', True],
                should_exit=True
            )
        )
        open_terminals_menu.append_item(
            FunctionItem(
                text="No",
                function=setting_utils.update_setting_value,
                args=['open_terminals', False],
                should_exit=True
            )
        )

        open_terminals_item = SubmenuItem(open_terminals_string, open_terminals_menu, current_menu)

        # Machine Shell Submenu
        machine_shell_string = "Choose device shell to be used"
        machine_shell_menu = SelectionMenu(
            strings=[],
            title=machine_shell_string,
            subtitle=setting_utils.current_string("device_shell"),
            formatter=menu_formatter,
            prologue_text="""The shell to use inside the device.
            
                          **The application must be correctly installed in the Docker image used for the device!**
                          
                          Default is `%s`, but it depends on the used Docker image.""" % DEFAULTS['device_shell']
        )

        for shell in SHELLS_HINT:
            machine_shell_menu.append_item(
                FunctionItem(
                    text=shell,
                    function=setting_utils.update_setting_value,
                    args=["device_shell", shell],
                    should_exit=True
                )
            )
        machine_shell_menu.append_item(
            FunctionItem(
                text="Choose another shell",
                function=setting_utils.update_value,
                args=['device_shell',
                      RegexValidator(r"^(\w|/)+$"),
                      'Write the name of a shell:',
                      'Shell name is not valid!'
                      ],
                should_exit=True
            )
        )

        machine_shell_item = SubmenuItem(machine_shell_string, machine_shell_menu, current_menu)

        # Terminal Emulator Submenu
        # Linux Version
        def terminal_emulator_menu_linux():
            terminal_string = "Choose terminal emulator to be used"
            terminal_menu = SelectionMenu(
                strings=[],
                title=terminal_string,
                subtitle=setting_utils.current_string("terminal"),
                formatter=menu_formatter,
                prologue_text="""Terminal emulator application to be used for device terminals.
                 
                              **The application must be correctly installed in the host system!**
                              
                              Default is `%s`.""" % DEFAULTS['terminal']
            )

            terminal_menu.append_item(
                FunctionItem(
                    text="/usr/bin/xterm",
                    function=setting_utils.update_setting_value,
                    args=["terminal", "/usr/bin/xterm"],
                    should_exit=True
                )
            )
            terminal_menu.append_item(
                FunctionItem(
                    text="/usr/bin/gnome-terminal",
                    function=setting_utils.update_setting_value,
                    args=["terminal", "/usr/bin/gnome-terminal"],
                    should_exit=True
                )
            )
            terminal_menu.append_item(
                FunctionItem(
                    text="TMUX",
                    function=setting_utils.update_setting_value,
                    args=["terminal", "TMUX"],
                    should_exit=True
                )
            )
            terminal_menu.append_item(
                FunctionItem(
                    text="Choose another terminal emulator",
                    function=setting_utils.update_value,
                    args=['terminal',
                          TerminalValidator(),
                          'Write the path of a terminal emulator:',
                          'Terminal emulator is not valid! Install it before using it.'
                          ],
                    should_exit=True
                )
            )

            return SubmenuItem(terminal_string, terminal_menu, current_menu)

        # macOS Version
        def terminal_emulator_menu_osx():
            terminal_string = "Choose terminal emulator to be used"
            terminal_menu = SelectionMenu(
                strings=[],
                title=terminal_string,
                subtitle=setting_utils.current_string("terminal"),
                formatter=menu_formatter,
                prologue_text="""Terminal emulator application to be used for device terminals.
                 
                              **The application must be correctly installed in the host system!**
                              
                              Default is `Terminal`."""
            )

            for terminal in TERMINALS_OSX:
                terminal_menu.append_item(
                    FunctionItem(
                        text=terminal,
                        function=setting_utils.update_setting_value,
                        args=["terminal", terminal],
                        should_exit=True
                    )
                )
            terminal_menu.append_item(
                FunctionItem(
                    text="TMUX",
                    function=setting_utils.update_setting_value,
                    args=["terminal", "TMUX"],
                    should_exit=True
                )
            )

            return SubmenuItem(terminal_string, terminal_menu, current_menu)

        terminal_item = exec_by_platform(terminal_emulator_menu_linux, lambda: None, terminal_emulator_menu_osx)

        # Prefixes Submenu
        prefixes_string = "Choose Kathara prefixes"
        prefixes_menu = SelectionMenu(
            strings=[],
            title=prefixes_string,
            formatter=menu_formatter,
            prologue_text="""Prefixes assigned to the network and device names when deployed.
            
                          Defaults are `%s` and `%s`.""" % (DEFAULTS['net_prefix'], DEFAULTS['device_prefix'])
        )

        net_prefix_item = FunctionItem(
            text=setting_utils.current_string("net_prefix", text="Insert Kathara networks prefix"),
            function=setting_utils.update_value,
            args=['net_prefix',
                  RegexValidator(r"^[a-z]+_?[a-z_]+$"),
                  'Write a Kathara networks prefix:',
                  'Network Prefix must only contain lowercase letters and underscore.'
                  ],
            should_exit=True
        )
        prefixes_menu.append_item(net_prefix_item)

        machine_prefix_item = FunctionItem(
            text=setting_utils.current_string("device_prefix", text="Insert Kathara devices prefix"),
            function=setting_utils.update_value,
            args=['device_prefix',
                  RegexValidator(r"^[a-z]+_?[a-z_]+$"),
                  'Write a Kathara devices prefix:',
                  'Device Prefix must only contain lowercase letters and underscore.'
                  ],
            should_exit=True
        )
        prefixes_menu.append_item(machine_prefix_item)

        prefixes_item = SubmenuItem(prefixes_string, prefixes_menu, current_menu)

        # Debug Level Submenu
        debug_level_string = "Choose logging level to be used"
        debug_level_menu = SelectionMenu(
            strings=[],
            title=debug_level_string,
            subtitle=setting_utils.current_string("debug_level"),
            formatter=menu_formatter,
            prologue_text="""Logging level of Kathara messages.
                            
                          Default is `%s`.""" % DEFAULTS['debug_level']
        )

        for debug_level in AVAILABLE_DEBUG_LEVELS:
            debug_level_menu.append_item(
                FunctionItem(
                    text=debug_level,
                    function=setting_utils.update_setting_value,
                    args=["debug_level", debug_level],
                    should_exit=True
                )
            )

        debug_level_item = SubmenuItem(debug_level_string, debug_level_menu, current_menu)

        # Print Startup Logs Option
        print_startup_log_string = "Print Startup Logs on device startup"
        print_startup_log_menu = SelectionMenu(
            strings=[],
            title=open_terminals_string,
            subtitle=setting_utils.current_bool("print_startup_log"),
            formatter=menu_formatter,
            prologue_text="""When opening a device terminal, print its startup log.
            
                          Default is %s.""" % setting_utils.format_bool(DEFAULTS['print_startup_log'])
        )

        print_startup_log_menu.append_item(
            FunctionItem(
                text="Yes",
                function=setting_utils.update_setting_value,
                args=['print_startup_log', True],
                should_exit=True
            )
        )
        print_startup_log_menu.append_item(
            FunctionItem(
                text="No",
                function=setting_utils.update_setting_value,
                args=['print_startup_log', False],
                should_exit=True
            )
        )

        print_startup_log_item = SubmenuItem(print_startup_log_string, print_startup_log_menu, current_menu)

        # Enable IPv6 Option
        enable_ipv6_string = "Enable IPv6"
        enable_ipv6_menu = SelectionMenu(
            strings=[],
            title=enable_ipv6_string,
            subtitle=setting_utils.current_bool("enable_ipv6"),
            formatter=menu_formatter,
            prologue_text="""This option enables IPv6 inside the devices.
            
                          Default is %s.""" % setting_utils.format_bool(DEFAULTS['enable_ipv6'])
        )

        enable_ipv6_menu.append_item(
            FunctionItem(
                text="Yes",
                function=setting_utils.update_setting_value,
                args=['enable_ipv6', True],
                should_exit=True
            )
        )
        enable_ipv6_menu.append_item(
            FunctionItem(
                text="No",
                function=setting_utils.update_setting_value,
                args=['enable_ipv6', False],
                should_exit=True
            )
        )

        enable_ipv6_item = SubmenuItem(enable_ipv6_string, enable_ipv6_menu, current_menu)

        # Volume Mount Policy Option
        volume_mount_policy_string = "Device Volume Mount Policy"
        volume_mount_policy_menu = SelectionMenu(
            strings=[],
            title=volume_mount_policy_string,
            subtitle=setting_utils.current_string("volume_mount_policy"),
            prologue_text="""Choose the policy to apply when a device is trying to mount a volume from the host.             

                                  \tDefault is %s.""" % DEFAULTS['volume_mount_policy'],
            formatter=menu_formatter
        )

        volume_mount_policy_menu.append_item(
            FunctionItem(
                text="Always",
                function=setting_utils.update_setting_value,
                args=["volume_mount_policy", "Always"],
                should_exit=True
            )
        )
        volume_mount_policy_menu.append_item(
            FunctionItem(
                text="Prompt",
                function=setting_utils.update_setting_value,
                args=["volume_mount_policy", "Prompt"],
                should_exit=True
            )
        )
        volume_mount_policy_menu.append_item(
            FunctionItem(
                text="Never",
                function=setting_utils.update_setting_value,
                args=["volume_mount_policy", "Never"],
                should_exit=True
            )
        )

        volume_mount_policy_item = SubmenuItem(volume_mount_policy_string, volume_mount_policy_menu, current_menu)

        current_menu.append_item(manager_item)
        current_menu.append_item(submenu_item)
        current_menu.append_item(open_terminals_item)
        current_menu.append_item(machine_shell_item)
        if terminal_item:
            current_menu.append_item(terminal_item)
        current_menu.append_item(prefixes_item)
        current_menu.append_item(debug_level_item)
        current_menu.append_item(print_startup_log_item)
        current_menu.append_item(enable_ipv6_item)
        current_menu.append_item(volume_mount_policy_item)

    def update_manager_value(self, current_menu: ConsoleMenu, value: str) -> None:
        setting_utils.update_setting_value("manager_type", value)

        # exit() does not work. So we force the menu to exit as the user selected the Exit option.
        # This is always the last item of the current menu.
        exit_option = len(current_menu.items) - 1
        current_menu.go_to(exit_option)

        clear_terminal()

        # Create a new menu (Manager could be changed so specific options need to be changed)
        new_menu = self.menu_factory.create_menu()
        new_menu.draw()
        new_menu.show()
