#!/usr/bin/python3
import argparse
import sys
import syslog
import lib.common_vars
import lib.log
import lib.file
import copy

# Lazy imports:
#import json
#import lib.socket
#import lib.params

DAEMON_NAME = lib.common_vars.DAEMON_NAME


class ArgumentParser(argparse.ArgumentParser):
    """
    Change argparse class to handle errors.

    Before change, "error" function exited executable program
    """

    def error(self, message):
        raise AttributeError(message)


def convert_links_stats_to_humanfriendly_sting(links, link_group=None):
    """
    Convert dictionary with links parameters to string.
    """
    links_params_to_print = ""

    add_space = ''
    if len(links) != 1 or "default" not in links:
        add_space = ' ' * 4
    for group in links:
        if link_group:
            if link_group != group:
                continue

        if add_space:
            links_params_to_print += f'Group: {group}\n'
        for index, link in enumerate(links[group]):
            link_params = {"Name": link['name'], "Interface": link['iface_name'],
                           "Priority": link['prio'], "State": 'unknown',
                           "Active": link['status']['active'],
                           "Hosts reachability": link['check']['hosts_reachability'],
                           "IPv4 hosts": 'None',
                           "IPv4 nexthop": 'None',
                           "IPv6 hosts": 'None',
                           "IPv6 nexthop": 'None'}

            if link['status']['reachable'] is True:
                link_params['State'] = 'reachable'
            elif link['status']['reachable'] is False:
                link_params['State'] = 'unreachable'

            if 'ipv4' in link['check']:
                link_params['IPv4 hosts'] = '"' + '", "'.join(link['check']['ipv4']['hosts']) + '"'

            if 'ipv6' in link['check']:
                link_params['IPv6 hosts'] = '"' + '", "'.join(link['check']['ipv6']['hosts']) + '"'

            if 'ipv4' in link['routes'] and link['routes']['ipv4']['nexthop'] is not None:
                if link['routes']['ipv4']['nexthop'] == "DHCP":
                    link_params['IPv4 nexthop'] = 'Not received (DHCP)'
                else:
                    link_params['IPv4 nexthop'] = '"' + link['routes']['ipv4']['nexthop'] + '"'
                    if link['ipv4_dhcp']['enable']:
                        link_params['IPv4 nexthop'] += ' (DHCP)'

            if 'ipv6' in link['routes'] and link['routes']['ipv6']['nexthop'] is not None:
                link_params['IPv6 nexthop'] = '"' + link['routes']['ipv6']['nexthop'] + '"'

            links_params_to_print += f'{add_space}Link: {index + 1}\n'
            for key, value in link_params.items():
                links_params_to_print += f'{add_space}    {key}: {value}\n'
    if link_group and links_params_to_print == "":
        return f'Group {link_group} not used in {lib.common_vars.DAEMON_NAME}'
    return links_params_to_print


def show_links_status(json_format, group=None):
    import json

    data_info = copy.deepcopy(lib.common_vars.PACKET_REQUEST_TYPES[1])
    data_info['request_data'] = data_info['request_data'][0]
    ret, reply = lib.socket.send_data_to_daemon_server_socket_and_read_reply(data_info)
    if ret != 0:
        return ret

    try:
        links = json.loads(reply)
    except json.decoder.JSONDecodeError:
        lib.log.send_err_msg('Unexpected message. Data is not json format.', msg_lvl_output=5)
        return 1

    if links == {}:
        lib.log.send_warning_msg(f'{lib.common_vars.DAEMON_NAME} is busy. Please try again later.',
                                 msg_lvl_output=5)
        return 2
    if json_format:
        print(reply)
    else:
        str_to_out = convert_links_stats_to_humanfriendly_sting(links, group)
        print(str_to_out)

    return 0


def set_log_lvl(log_lvl):
    data_info = copy.deepcopy(lib.common_vars.PACKET_REQUEST_TYPES[3])
    data_info['request_data'] = log_lvl
    ret, reply = lib.socket.send_data_to_daemon_server_socket_and_read_reply(data_info)
    if ret == 0:
        if reply != data_info['reply_data']:
            return 1
    return ret


def show_log_lvl():
    data_info = copy.deepcopy(lib.common_vars.PACKET_REQUEST_TYPES[5])
    data_info['request_data'] = data_info['request_data'][0]
    ret, reply = lib.socket.send_data_to_daemon_server_socket_and_read_reply(data_info)
    if ret != 0:
        return ret
    if ret == 0:
        if reply not in data_info['reply_data']:
            return 1

    print(reply)
    return 0


def arg_parse():
    parser = ArgumentParser()
    subparser = parser.add_subparsers(dest='MODE', required=True, metavar='MODE',
                                      title='required argument',
                                      help='mode of mwan_mgr. Supported mode: show, set,'
                                           ' check-config')
    show_mode = subparser.add_parser('show')
    show_mode.add_argument('--json',
                           action='store_true',
                           help='show information in json format',
                           dest='json_format')

    show_mode_group = show_mode.add_mutually_exclusive_group(required=True)
    show_mode_group.add_argument('--links_status',
                                 help='show links status',
                                 nargs='?',
                                 default=False,
                                 metavar='group')
    show_mode_group.add_argument('--log_level',
                                 action='store_true',
                                 help='show log level',
                                 dest='show_log_level')

    set_mode = subparser.add_parser('set')
    show_mode_group = set_mode.add_mutually_exclusive_group(required=True)
    show_mode_group.add_argument('--log_level',
                                 choices=['error', 'warning', 'info', 'debug'],
                                 help='Set log level',
                                 dest='log_lvl')

    check_mode = subparser.add_parser('check-config')
    check_mode.add_argument("-f", "--file", dest="config_file",
                            help="path to config file", required=False, type=str)
    check_mode.add_argument("--result", dest='result', action='store_true',
                            help="show the resulting configuration "
                                 "as it will be used in the daemon")

    try:
        arguments = parser.parse_args()
    except AttributeError as exception_info:
        lib.log.send_err_msg(exception_info, msg_lvl_output=5)
        return None

    return arguments


if __name__ == '__main__':
    syslog_tag = lib.common_vars.SYSLOG_TAG
    syslog.openlog(facility=syslog.LOG_LOCAL7, ident=syslog_tag, logoption=syslog.LOG_NDELAY)

    args = arg_parse()

    if not args:
        sys.exit(1)

    connect_to_daemon_modes = ['show', 'set']
    action_mode = ['check-config']

    exit_code = 0
    if args.MODE in connect_to_daemon_modes:
        check_error_msg = lib.file.check_file_for_reading(lib.common_vars.MULTIWAN_FILE_PID)
        if check_error_msg:
            lib.log.send_warning_msg(f'PID file for {DAEMON_NAME} service is not defined.',
                                     msg_lvl_output=5)
            sys.exit(2)

        import lib.socket

        if args.MODE == 'show':
            exit_code = 255
            if args.links_status or args.links_status is None:
                exit_code = show_links_status(args.json_format, args.links_status)
            elif args.show_log_level:
                exit_code = show_log_lvl()
        elif args.MODE == 'set':
            if args.log_lvl:
                exit_code = set_log_lvl(args.log_lvl)
    elif args.MODE in action_mode:
        if args.MODE == 'check-config':
            import lib.params

            file_path = lib.params.get_abs_path_to_config(args.config_file)
            err_msg = lib.file.check_file_for_reading(file_path)
            if err_msg:
                lib.log.send_err_msg(err_msg, msg_lvl_output=5)
                sys.exit(1)

            exit_code = lib.params.check_config(file_path, args.result)
    sys.exit(exit_code)
