from uaclient import (
    apt_news,
    config,
    entitlements,
    event_logger,
    exceptions,
    http,
    messages,
)
from uaclient.api.u.pro.status.is_attached.v1 import _is_attached
from uaclient.apt import AptProxyScope
from uaclient.cli import cli_util
from uaclient.cli.commands import ProArgument, ProArgumentGroup, ProCommand
from uaclient.cli.parser import HelpCategory
from uaclient.entitlements.entitlement_status import ApplicationStatus
from uaclient.files import state_files
from uaclient.files.user_config_file import LXDGuestAttachEnum
from uaclient.livepatch import (
    configure_livepatch_proxy,
    unconfigure_livepatch_proxy,
)
from uaclient.snap import configure_snap_proxy, unconfigure_snap_proxy

event = event_logger.get_event_logger()


def action_config(args, *, cfg, **kwargs):
    # Avoiding a circular import
    from uaclient.cli import get_parser

    get_parser().print_help_for_command("config")
    return 0


def action_config_show(args, *, cfg, **kwargs):
    """Perform the 'config show' action optionally limit output to a single key

    :return: 0 on success
    :raise UbuntuProError: on invalid keys
    """
    if args.key:  # limit reporting config to a single config key
        if args.key not in config.UA_CONFIGURABLE_KEYS:
            raise exceptions.InvalidArgChoice(
                arg="'{}'".format(args.key),
                choices=", ".join(config.UA_CONFIGURABLE_KEYS),
            )
        print(
            "{key} {value}".format(
                key=args.key, value=getattr(cfg, args.key, None)
            )
        )
        return 0

    col_width = str(max([len(x) for x in config.UA_CONFIGURABLE_KEYS]) + 1)
    row_tmpl = "{key: <" + col_width + "} {value}"

    for key in config.UA_CONFIGURABLE_KEYS:
        print(row_tmpl.format(key=key, value=getattr(cfg, key, None)))

    if (cfg.global_apt_http_proxy or cfg.global_apt_https_proxy) and (
        cfg.ua_apt_http_proxy or cfg.ua_apt_https_proxy
    ):
        print(messages.CLI_CONFIG_GLOBAL_XOR_UA_PROXY)


@cli_util.assert_root
def action_config_set(args, *, cfg, **kwargs):
    """Perform the 'config set' action.

    @return: 0 on success, 1 otherwise
    """
    from uaclient.cli import get_parser

    parser = get_parser()
    try:
        set_key, set_value = args.key_value_pair.split("=")
    except ValueError:
        parser.print_help_for_command("config set")
        raise exceptions.GenericInvalidFormat(
            expected="<key>=<value>", actual=args.key_value_pair
        )
    if set_key not in config.UA_CONFIGURABLE_KEYS:
        parser.print_help_for_command("config set")
        raise exceptions.InvalidArgChoice(
            arg="<key>", choices=", ".join(config.UA_CONFIGURABLE_KEYS)
        )
    if not set_value.strip():
        parser.print_help_for_command("config set")
        raise exceptions.EmptyConfigValue(arg=set_key)

    if type(getattr(cfg, set_key, None)) == bool:
        if set_value.lower() not in ("true", "false"):
            raise exceptions.InvalidArgChoice(
                arg="<value>", choices="true, false"
            )
        set_value = set_value.lower() == "true"

    if set_key in ("http_proxy", "https_proxy"):
        protocol_type = set_key.split("_")[0]
        if protocol_type == "http":
            validate_url = http.PROXY_VALIDATION_SNAP_HTTP_URL
        else:
            validate_url = http.PROXY_VALIDATION_SNAP_HTTPS_URL
        http.validate_proxy(protocol_type, set_value, validate_url)

        kwargs = {set_key: set_value}
        configure_snap_proxy(**kwargs)
        # Only set livepatch proxy if livepatch is enabled
        entitlement = entitlements.livepatch.LivepatchEntitlement(cfg)
        livepatch_status, _ = entitlement.application_status()
        if livepatch_status == ApplicationStatus.ENABLED:
            configure_livepatch_proxy(**kwargs)
    elif set_key in cfg.ua_scoped_proxy_options:
        protocol_type = set_key.split("_")[2]
        if protocol_type == "http":
            validate_url = http.PROXY_VALIDATION_APT_HTTP_URL
        else:
            validate_url = http.PROXY_VALIDATION_APT_HTTPS_URL
        http.validate_proxy(protocol_type, set_value, validate_url)
        unset_current = bool(
            cfg.global_apt_http_proxy or cfg.global_apt_https_proxy
        )
        if unset_current:
            print(
                messages.WARNING_APT_PROXY_OVERWRITE.format(
                    current_proxy="pro scoped apt", previous_proxy="global apt"
                )
            )
        cli_util.configure_apt_proxy(
            cfg, AptProxyScope.UACLIENT, set_key, set_value
        )
        cfg.global_apt_http_proxy = None
        cfg.global_apt_https_proxy = None

    elif set_key in (
        cfg.deprecated_global_scoped_proxy_options
        + cfg.global_scoped_proxy_options
    ):
        # setup_apt_proxy is destructive for unprovided values. Source complete
        # current config values from uaclient.conf before applying set_value.

        protocol_type = "https" if "https" in set_key else "http"
        if protocol_type == "http":
            validate_url = http.PROXY_VALIDATION_APT_HTTP_URL
        else:
            validate_url = http.PROXY_VALIDATION_APT_HTTPS_URL

        if set_key in cfg.deprecated_global_scoped_proxy_options:
            print(
                messages.WARNING_CONFIG_FIELD_RENAME.format(
                    old="apt_{}_proxy".format(protocol_type),
                    new="global_apt_{}_proxy".format(protocol_type),
                )
            )
            set_key = "global_" + set_key

        http.validate_proxy(protocol_type, set_value, validate_url)

        unset_current = bool(cfg.ua_apt_http_proxy or cfg.ua_apt_https_proxy)

        if unset_current:
            print(
                messages.WARNING_APT_PROXY_OVERWRITE.format(
                    current_proxy="global apt", previous_proxy="pro scoped apt"
                )
            )
        cli_util.configure_apt_proxy(
            cfg, AptProxyScope.GLOBAL, set_key, set_value
        )
        cfg.ua_apt_http_proxy = None
        cfg.ua_apt_https_proxy = None

    elif set_key in (
        "update_messaging_timer",
        "metering_timer",
    ):
        try:
            set_value = int(set_value)
            if set_value < 0:
                raise ValueError("Invalid interval for {}".format(set_key))
        except ValueError:
            parser.print_help_for_command("config set")
            # More readable in the CLI, without breaking the line in the logs
            print("")
            raise exceptions.InvalidPosIntConfigValue(
                key=set_key, value=set_value
            )
    elif set_key == "apt_news":
        if set_value:
            apt_news.update_apt_news(cfg)
        else:
            state_files.apt_news_contents_file.delete()
    elif set_key == "lxd_guest_attach":
        if not _is_attached(cfg).is_attached:
            raise exceptions.UnattachedError()
        state_files.lxd_pro_config_file.write(
            state_files.LXDProConfig(
                guest_attach=LXDGuestAttachEnum.from_value(set_value.lower())
            )
        )

    setattr(cfg, set_key, set_value)


@cli_util.assert_root
def action_config_unset(args, *, cfg, **kwargs):
    """Perform the 'config unset' action.

    @return: 0 on success, 1 otherwise
    """
    from uaclient.cli import get_parser

    if args.key not in config.UA_CONFIGURABLE_KEYS:
        parser = get_parser()
        parser.print_help_for_command("config unset")
        raise exceptions.InvalidArgChoice(
            arg="<key>", choices=", ".join(config.UA_CONFIGURABLE_KEYS)
        )
    if args.key in ("http_proxy", "https_proxy"):
        protocol_type = args.key.split("_")[0]
        unconfigure_snap_proxy(protocol_type=protocol_type)
        # Only unset livepatch proxy if livepatch is enabled
        entitlement = entitlements.livepatch.LivepatchEntitlement(cfg)
        livepatch_status, _ = entitlement.application_status()
        if livepatch_status == ApplicationStatus.ENABLED:
            unconfigure_livepatch_proxy(protocol_type=protocol_type)
    elif args.key in cfg.ua_scoped_proxy_options:
        cli_util.configure_apt_proxy(
            cfg, AptProxyScope.UACLIENT, args.key, None
        )
    elif args.key in (
        cfg.deprecated_global_scoped_proxy_options
        + cfg.global_scoped_proxy_options
    ):
        if args.key in cfg.deprecated_global_scoped_proxy_options:
            protocol_type = "https" if "https" in args.key else "http"
            event.info(
                messages.WARNING_CONFIG_FIELD_RENAME.format(
                    old="apt_{}_proxy".format(protocol_type),
                    new="global_apt_{}_proxy".format(protocol_type),
                )
            )
            args.key = "global_" + args.key
        cli_util.configure_apt_proxy(cfg, AptProxyScope.GLOBAL, args.key, None)
    elif args.key == "lxd_guest_attach":
        state_files.lxd_pro_config_file.write(
            state_files.LXDProConfig(guest_attach=LXDGuestAttachEnum.OFF)
        )

    setattr(cfg, args.key, None)
    return 0


show_subcommand = ProCommand(
    "show",
    help=messages.CLI_CONFIG_SHOW_DESC,
    description=messages.CLI_CONFIG_SHOW_DESC,
    action=action_config_show,
    argument_groups=[
        ProArgumentGroup(
            arguments=[
                ProArgument(
                    "key", help=messages.CLI_CONFIG_SHOW_KEY, nargs="?"
                )
            ]
        )
    ],
)

set_subcommand = ProCommand(
    "set",
    help=messages.CLI_CONFIG_SET_DESC,
    description=messages.CLI_CONFIG_SET_DESC,
    action=action_config_set,
    argument_groups=[
        ProArgumentGroup(
            arguments=[
                ProArgument(
                    "key_value_pair",
                    help=(
                        messages.CLI_CONFIG_SET_KEY_VALUE.format(
                            options=", ".join(config.UA_CONFIGURABLE_KEYS)
                        )
                    ),
                )
            ]
        )
    ],
)

unset_subcommand = ProCommand(
    "unset",
    help=messages.CLI_CONFIG_UNSET_DESC,
    description=messages.CLI_CONFIG_UNSET_DESC,
    action=action_config_unset,
    argument_groups=[
        ProArgumentGroup(
            arguments=[
                ProArgument(
                    "key",
                    help=(
                        messages.CLI_CONFIG_UNSET_KEY.format(
                            options=", ".join(config.UA_CONFIGURABLE_KEYS)
                        )
                    ),
                    metavar="key",
                )
            ]
        )
    ],
)

config_command = ProCommand(
    "config",
    help=messages.CLI_ROOT_CONFIG,
    description=messages.CLI_CONFIG_DESC,
    action=action_config,
    help_category=HelpCategory.OTHER,
    subcommands=[show_subcommand, set_subcommand, unset_subcommand],
)
