from functools import update_wrapper
import logging
from pathlib import Path
import typing as t

import click
from oslo_config import cfg

import aprsd
from aprsd import conf  # noqa: F401
from aprsd.log import log
from aprsd.utils import trace


CONF = cfg.CONF
home = str(Path.home())
DEFAULT_CONFIG_DIR = f"{home}/.config/aprsd/"
DEFAULT_SAVE_FILE = f"{home}/.config/aprsd/aprsd.p"
DEFAULT_CONFIG_FILE = f"{home}/.config/aprsd/aprsd.conf"


F = t.TypeVar("F", bound=t.Callable[..., t.Any])

common_options = [
    click.option(
        "--loglevel",
        default="INFO",
        show_default=True,
        type=click.Choice(
            ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
            case_sensitive=False,
        ),
        show_choices=True,
        help="The log level to use for aprsd.log",
    ),
    click.option(
        "-c",
        "--config",
        "config_file",
        show_default=True,
        default=DEFAULT_CONFIG_FILE,
        help="The aprsd config file to use for options.",
    ),
    click.option(
        "--quiet",
        is_flag=True,
        default=False,
        help="Don't log to stdout",
    ),
]


class AliasedGroup(click.Group):
    def command(self, *args, **kwargs):
        """A shortcut decorator for declaring and attaching a command to
        the group.  This takes the same arguments as :func:`command` but
        immediately registers the created command with this instance by
        calling into :meth:`add_command`.
        Copied from `click` and extended for `aliases`.
        """
        def decorator(f):
            aliases = kwargs.pop("aliases", [])
            cmd = click.decorators.command(*args, **kwargs)(f)
            self.add_command(cmd)
            for alias in aliases:
                self.add_command(cmd, name=alias)
            return cmd
        return decorator

    def group(self, *args, **kwargs):
        """A shortcut decorator for declaring and attaching a group to
        the group.  This takes the same arguments as :func:`group` but
        immediately registers the created command with this instance by
        calling into :meth:`add_command`.
        Copied from `click` and extended for `aliases`.
        """
        def decorator(f):
            aliases = kwargs.pop("aliases", [])
            cmd = click.decorators.group(*args, **kwargs)(f)
            self.add_command(cmd)
            for alias in aliases:
                self.add_command(cmd, name=alias)
            return cmd
        return decorator


def add_options(options):
    def _add_options(func):
        for option in reversed(options):
            func = option(func)
        return func
    return _add_options


def process_standard_options(f: F) -> F:
    def new_func(*args, **kwargs):
        ctx = args[0]
        ctx.ensure_object(dict)
        config_file_found = True
        if kwargs["config_file"]:
            default_config_files = [kwargs["config_file"]]
        else:
            default_config_files = None
        try:
            CONF(
                [], project="aprsd", version=aprsd.__version__,
                default_config_files=default_config_files,
            )
        except cfg.ConfigFilesNotFoundError:
            config_file_found = False
        ctx.obj["loglevel"] = kwargs["loglevel"]
        # ctx.obj["config_file"] = kwargs["config_file"]
        ctx.obj["quiet"] = kwargs["quiet"]
        log.setup_logging(
            ctx.obj["loglevel"],
            ctx.obj["quiet"],
        )
        if CONF.trace_enabled:
            trace.setup_tracing(["method", "api"])

        if not config_file_found:
            LOG = logging.getLogger("APRSD")   # noqa: N806
            LOG.error("No config file found!! run 'aprsd sample-config'")

        del kwargs["loglevel"]
        del kwargs["config_file"]
        del kwargs["quiet"]
        return f(*args, **kwargs)

    return update_wrapper(t.cast(F, new_func), f)


def process_standard_options_no_config(f: F) -> F:
    """Use this as a decorator when config isn't needed."""
    def new_func(*args, **kwargs):
        ctx = args[0]
        ctx.ensure_object(dict)
        ctx.obj["loglevel"] = kwargs["loglevel"]
        ctx.obj["config_file"] = kwargs["config_file"]
        ctx.obj["quiet"] = kwargs["quiet"]
        log.setup_logging(
            ctx.obj["loglevel"],
            ctx.obj["quiet"],
        )

        del kwargs["loglevel"]
        del kwargs["config_file"]
        del kwargs["quiet"]
        return f(*args, **kwargs)

    return update_wrapper(t.cast(F, new_func), f)
