"""Netmiko Cisco WLC support."""
import time
import re
import socket

from netmiko.ssh_exception import NetmikoAuthenticationException
from netmiko.base_connection import BaseConnection


class CiscoWlcSSH(BaseConnection):
    """Netmiko Cisco WLC support."""

    def __init__(self, *args, **kwargs):
        # WLC/AireOS has an issue where you can get "No Existing Session" with
        # the default conn_timeout (so increase conn_timeout to 10-seconds).
        kwargs.setdefault("conn_timeout", 10)
        return super().__init__(*args, **kwargs)

    def special_login_handler(self, delay_factor=1):
        """WLC presents with the following on login (in certain OS versions)

        login as: user

        (Cisco Controller)

        User: user

        Password:****
        """
        delay_factor = self.select_delay_factor(delay_factor)
        i = 0
        time.sleep(delay_factor * 0.5)
        output = ""
        while i <= 12:
            output = self.read_channel()
            if output:
                if "login as" in output or "User:" in output:
                    self.write_channel(self.username + self.RETURN)
                elif "Password" in output:
                    self.write_channel(self.password + self.RETURN)
                    break
                time.sleep(delay_factor * 1)
            else:
                self.write_channel(self.RETURN)
                time.sleep(delay_factor * 1.5)
            i += 1

    def send_command_w_enter(self, *args, **kwargs):
        """
        For 'show run-config' Cisco WLC adds a 'Press Enter to continue...' message
        Even though pagination is disabled
        show run-config also has excessive delays in the output which requires special
        handling.
        Arguments are the same as send_command_timing() method
        """
        if len(args) > 1:
            raise ValueError("Must pass in delay_factor as keyword argument")

        # If no delay_factor use 1 for default value
        delay_factor = kwargs.get("delay_factor", 1)
        kwargs["delay_factor"] = self.select_delay_factor(delay_factor)
        output = self.send_command_timing(*args, **kwargs)

        if "Press any key" in output or "Press Enter to" in output:
            new_args = list(args)
            if len(args) == 1:
                new_args[0] = self.RETURN
            else:
                kwargs["command_string"] = self.RETURN
            if not kwargs.get("max_loops"):
                kwargs["max_loops"] = 150

            # Send an 'enter'
            output = self.send_command_timing(*new_args, **kwargs)

            # WLC has excessive delay after this appears on screen
            if "802.11b Advanced Configuration" in output:

                # Defaults to 30 seconds
                time.sleep(kwargs["delay_factor"] * 30)
                not_done = True
                i = 1
                while not_done and i <= 150:
                    time.sleep(kwargs["delay_factor"] * 3)
                    i += 1
                    new_data = ""
                    new_data = self.read_channel()
                    if new_data:
                        output += new_data
                    else:
                        not_done = False

        strip_prompt = kwargs.get("strip_prompt", True)
        if strip_prompt:
            # Had to strip trailing prompt twice.
            output = self.strip_prompt(output)
            output = self.strip_prompt(output)
        return output

    def session_preparation(self):
        """
        Prepare the session after the connection has been established

        Cisco WLC uses "config paging disable" to disable paging
        """
        self._test_channel_read()

        try:
            self.set_base_prompt()
        except ValueError:
            msg = f"Authentication failed: {self.host}"
            raise NetmikoAuthenticationException(msg)

        self.disable_paging(command="config paging disable")
        # Clear the read buffer
        time.sleep(0.3 * self.global_delay_factor)
        self.clear_buffer()

    def cleanup(self, command="logout"):
        """Reset WLC back to normal paging and gracefully close session."""
        self.send_command_timing("config paging enable")

        # Exit configuration mode
        try:
            # The pattern="" forces use of send_command_timing
            if self.check_config_mode(pattern=""):
                self.exit_config_mode()
        except Exception:
            pass

        # End SSH/telnet session
        self.write_channel(command + self.RETURN)
        count = 0
        output = ""
        while count <= 5:
            time.sleep(0.5)

            # The connection might be dead at this point.
            try:
                output += self.read_channel()
            except socket.error:
                break

            # Don't automatically save the config (user's responsibility)
            if "Would you like to save them now" in output:
                self._session_log_fin = True
                self.write_channel("n" + self.RETURN)

            try:
                self.write_channel(self.RETURN)
            except socket.error:
                break
            count += 1

    def check_config_mode(self, check_string="config", pattern=""):
        """Checks if the device is in configuration mode or not."""
        if not pattern:
            pattern = re.escape(self.base_prompt)
        return super().check_config_mode(check_string, pattern)

    def config_mode(self, config_command="config", pattern=""):
        """Enter into config_mode."""
        if not pattern:
            pattern = re.escape(self.base_prompt)
        return super().config_mode(config_command, pattern)

    def exit_config_mode(self, exit_config="exit", pattern=""):
        """Exit config_mode."""
        if not pattern:
            pattern = re.escape(self.base_prompt)
        return super().exit_config_mode(exit_config, pattern)

    def send_config_set(
        self,
        config_commands=None,
        exit_config_mode=False,
        enter_config_mode=False,
        **kwargs,
    ):
        return super().send_config_set(
            config_commands=config_commands,
            exit_config_mode=exit_config_mode,
            enter_config_mode=enter_config_mode,
            **kwargs,
        )

    def save_config(self, cmd="save config", confirm=True, confirm_response="y"):
        """Saves Config."""
        self.enable()
        if confirm:
            output = self.send_command_timing(command_string=cmd)
            if confirm_response:
                output += self.send_command_timing(confirm_response)
            else:
                # Send enter by default
                output += self.send_command_timing(self.RETURN)
        else:
            # Some devices are slow so match on trailing-prompt if you can
            output = self.send_command(command_string=cmd)
        return output
