"""Utility functions for CLI commands."""
from functools import lru_cache
from os.path import basename, expanduser
from types import FrameType
from typing import Any, Callable, Iterator, Optional, Sequence, Union
import argparse
import functools
import itertools
import logging
import re
import sys

from loguru import logger
import click

__all__ = ('common_parser', 'complete_hosts', 'complete_ports',
           'setup_logging_stdout')


@lru_cache()
def setup_logging_stdout(name: Optional[str] = None,
                         verbose: bool = False) -> logging.Logger:
    """Basic way to set up and get a logger.

    Parameters
    ----------
    name : Optional[str]
        Name of the logger.
    verbose : bool
        If log level should be DEBUG instead of INFO.
    """
    name = name if name else basename(sys.argv[0])
    log = logging.getLogger(name)
    log.setLevel(logging.DEBUG if verbose else logging.INFO)
    channel = logging.StreamHandler(sys.stdout)
    channel.setFormatter(logging.Formatter('%(message)s'))
    channel.setLevel(logging.DEBUG if verbose else logging.INFO)
    log.addHandler(channel)
    return log


def common_options_and_arguments(
        func: Callable[..., None]) -> Callable[..., None]:
    """
    Shared options and arguments, to be used as a decorator with
    click.command().
    """
    @click.option('-u', '--username', default=None, help='Xirvik user')
    @click.option('-p', '--password', help='Xirvik password')
    @click.option('-r',
                  '--max-retries',
                  type=int,
                  default=10,
                  help='Number of retries for each request (passed to client)')
    @click.option('-d',
                  '--debug',
                  is_flag=True,
                  help='Enable debug level logging')
    @click.option(
        '--backoff-factor',
        default=5,
        type=int,
        help=('Back-off factor used when calculating time to wait to retry '
              'a failed request'))
    @click.option('--netrc',
                  default=expanduser('~/.netrc'),
                  help='netrc file path')
    @click.argument('host', shell_complete=complete_hosts)
    @functools.wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> None:
        return func(*args, **kwargs)

    return wrapper


def common_parser() -> argparse.ArgumentParser:
    """Common parser."""
    parser = argparse.ArgumentParser()
    parser.add_argument('-u', '--username', required=False, help='Xirvik user')
    parser.add_argument('-p',
                        '--password',
                        required=False,
                        help='Xirvik password')
    parser.add_argument(
        '-r',
        '--max-retries',
        type=int,
        default=10,
        help='Number of retries for each request (passed to client)')
    parser.add_argument('-v',
                        '--verbose',
                        action='store_true',
                        help='Enable verbose logging')
    parser.add_argument(
        '--backoff-factor',
        type=int,
        default=5,
        help=('Back-off factor used when calculating time to wait to retry '
              'a failed request'))
    parser.add_argument('--netrc', required=False, help='netrc file path')
    parser.add_argument('host', nargs=1, help='Host name')
    return parser


def _clean_host(s: str) -> str:
    # Attempt to not break IPv6 addresses
    if '[' not in s and (re.search(r'[0-9]+\:[0-9]+', s) or s == '::1'):
        return s
    # Remove brackets and remove port at end
    return re.sub(r'[\[\]]', '', re.sub(r'\:[0-9]+$', '', s))


def _read_ssh_known_hosts() -> Iterator[str]:
    try:
        with open(expanduser('~/.ssh/known_hosts')) as f:
            for line in f.readlines():
                host_part = line.split()[0]
                if ',' in host_part:
                    yield from (_clean_host(x) for x in host_part.split(','))
                else:
                    yield _clean_host(host_part)
    except FileNotFoundError:
        pass


def _read_netrc_hosts() -> Iterator[str]:
    try:
        with open(expanduser('~/.netrc')) as f:
            yield from (x.split()[1] for x in f.readlines())
    except FileNotFoundError:
        pass


def complete_hosts(_: Any, __: Any, incomplete: str) -> Sequence[str]:
    """
    Returns a list of hosts from SSH known_hosts and ~/.netrc for completion.
    """
    return [
        k
        for k in itertools.chain(_read_ssh_known_hosts(), _read_netrc_hosts())
        if k.startswith(incomplete)
    ]


def complete_ports(_: Any, __: Any, incomplete: str) -> Sequence[str]:
    """Returns common ports for completion."""
    return [k for k in ('80', '443', '8080') if k.startswith(incomplete)]


class InterceptHandler(logging.Handler):  # pragma: no cover
    """Intercept handler taken from Loguru's documentation."""
    def emit(self, record: logging.LogRecord) -> None:
        level: Union[str, int]
        # Get corresponding Loguru level if it exists
        try:
            level = logger.level(record.levelname).name
        except ValueError:
            level = record.levelno
        # Find caller from where originated the logged message
        frame: Optional[FrameType]
        frame = logging.currentframe()
        depth = 2
        while frame and frame.f_code.co_filename == logging.__file__:
            frame = frame.f_back
            depth += 1
        logger.opt(depth=depth, exception=record.exc_info).log(
            level, record.getMessage())


def setup_log_intercept_handler() -> None:  # pragma: no cover
    """Sets up Loguru to intercept records from the logging module."""
    logging.basicConfig(handlers=[InterceptHandler()], level=0)

@@ -39,40 +39,8 @@
 %22%22%22%0A
-from functools import lru_cache%0A
 from
@@ -58,18 +58,8 @@
 port
- basename,
  exp
@@ -168,24 +168,8 @@
 ion%0A
-import argparse%0A
 impo
@@ -226,19 +226,8 @@
 t re
-%0Aimport sys
 %0A%0Afr
@@ -283,22 +283,37 @@
 'common_
-parser
+options_and_arguments
 ', 'comp
@@ -368,727 +368,28 @@
 _log
-ging_stdout')%0A%0A%0A@lru_cache()%0Adef setup_logging_stdout(name: Optional%5Bstr%5D = None,%0A                         verbose: bool = False) -%3E logging.Logger:%0A    %22%22%22Basic way to set up and get a logger.%0A%0A    Parameters%0A    ----------%0A    name : Optional%5Bstr%5D%0A        Name of the logger.%0A    verbose : bool%0A        If log level should be DEBUG instead of INFO.%0A    %22%22%22%0A    name = name if name else basename(sys.argv%5B0%5D)%0A    log = logging.getLogger(name)%0A    log.setLevel(logging.DEBUG if verbose else logging.INFO)%0A    channel = logging.StreamHandler(sys.stdout)%0A    channel.setFormatter(logging.Formatter('%25(message)s'))%0A    channel.setLevel(logging.DEBUG if verbose else logging.INFO)%0A    log.addHandler(channel)%0A    return log
+_intercept_handler')
 %0A%0A%0Ad
@@ -1544,16 +1544,16 @@
 wargs)%0A%0A
+
     retu
@@ -1569,1051 +1569,8 @@
 r%0A%0A%0A
-def common_parser() -%3E argparse.ArgumentParser:%0A    %22%22%22Common parser.%22%22%22%0A    parser = argparse.ArgumentParser()%0A    parser.add_argument('-u', '--username', required=False, help='Xirvik user')%0A    parser.add_argument('-p',%0A                        '--password',%0A                        required=False,%0A                        help='Xirvik password')%0A    parser.add_argument(%0A        '-r',%0A        '--max-retries',%0A        type=int,%0A        default=10,%0A        help='Number of retries for each request (passed to client)')%0A    parser.add_argument('-v',%0A                        '--verbose',%0A                        action='store_true',%0A                        help='Enable verbose logging')%0A    parser.add_argument(%0A        '--backoff-factor',%0A        type=int,%0A        default=5,%0A        help=('Back-off factor used when calculating time to wait to retry '%0A              'a failed request'))%0A    parser.add_argument('--netrc', required=False, help='netrc file path')%0A    parser.add_argument('host', nargs=1, help='Host name')%0A    return parser%0A%0A%0A
 def 

remove old functions