# -*- coding:utf-8 -*-
"""
Function: set_loginrule.py moudle. This moudle mainly involves the
Setting Login Rule Information function.
Copyright Information: xFusion Digital Technologies Co.,
 Ltd. All Rights Reserved © 2017-2020
"""
from re import match
from sys import stdout
import sys
from scripts.common_function import REDFISH_STATUS_CODE_200
from scripts.common_function import REDFISH_STATUS_CODE_400
from scripts.common_function import REDFISH_STATUS_CODE_404
from scripts.common_function import UREST_STATUS_CODE_144
from scripts import common_function

FAILURE_MESS = 'Failure: some of the settings failed.\
 possible causes include the following: '
START_TIME_HELP = 'start time, the time formats YYYY-MM-DD HH:MM, \
YYYY-MM-DD, and HH:MM are supported. however, the start time \
and end time must be in the same format'
END_TIME_HELP = 'end time, the time formats YYYY-MM-DD HH:MM, \
YYYY-MM-DD, and HH:MM are supported. however, the end time \
and start time must be in the same format'
IP_HELP = 'IP address can be in IPv4 or IPv4/subnet mask format, \
and the subnet mask range is 1 to 32'
MAC_HELP = 'MAC address can be the first three parts (xx:xx:xx) or \
the complete MAC address (xx:xx:xx:xx:xx:xx)'


def setloginrule_init(parser, parser_dict):
    """
    Function Description:initializing the Command for
    Setting Login Rule Information
    Parameter:parser object: action object
    parser_dict dict: dictionary format of subcommand and ArgumentParser
    """
    sub_parser = parser.add_parser('setloginrule', help='''set login rule''')
    sub_parser.add_argument('-T', dest='RuleType',
                            type=str, required=True,
                            choices=['Rule1', 'Rule2', 'Rule3'],
                            help='''login rule''')
    sub_parser.add_argument('-S', dest='RuleEnabled',
                            type=str, required=False,
                            choices=['False', 'True'],
                            help='''login rule enable status''')
    sub_parser.add_argument('-ST', dest='StartTime', nargs='*',
                            type=str, required=False, help=START_TIME_HELP)
    sub_parser.add_argument('-ET', dest='EndTime', nargs='*',
                            type=str, required=False, help=END_TIME_HELP)
    sub_parser.add_argument('-IP', dest='IP',
                            type=str, required=False, help=IP_HELP)
    sub_parser.add_argument('-MAC', dest='Mac',
                            type=str, required=False, help=MAC_HELP)

    parser_dict['setloginrule'] = sub_parser

    return 'setloginrule'


def check_parameter(parser, args):
    """
    Function Description:check CLI command
    Parameter:args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    if args.RuleEnabled is None and args.StartTime is None \
            and args.IP is None and args.Mac is None and args.EndTime is None:
        parser.error('at least another one parameter ' +
                     'must be specified besides -T')
    # Check the time.
    check_time(parser, args)
    check_ip(parser, args)
    check_mac(parser, args)


def check_mac(parser, args):
    """
    Function Description:Check the mac
    Parameter:args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    mac_type1 = '^[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}' + \
                ':[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}$'
    mac_type2 = '^[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}$'

    if args.Mac is not None:
        if match(mac_type1, args.Mac) is None \
                and match(mac_type2, args.Mac) is None:
            parser.error("argument -MAC: %s" % MAC_HELP)


def check_ip(parser, args):
    """
    Function Description:Check IP addresses.
    Parameter:args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    error_message = 'argument -IP: IP address can be in IPv4' \
                    ' or IPv4/subnet mask format,' \
                    ' and the subnet mask range is 1 to 32'
    ip_type = r'^(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))$'
    if args.IP is not None:
        ip_info = args.IP.split('/')
        if len(ip_info) == 2:
            if int(ip_info[1]) < 1 or int(ip_info[1]) > 32:
                parser.error(error_message)
            elif match(ip_type, ip_info[0]) is None:
                parser.error(error_message)
        # The format is matched.
        elif match(ip_type, args.IP) is None:
            parser.error(error_message)


def check_time(parser, args):
    """
    Function Description:Check time.
    Parameter:args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    # The start time and end time must be set at the same time.
    if args.StartTime is not None and args.EndTime is None:
        parser.error('the following argument are required: -ET')
    elif args.StartTime is None and args.EndTime is not None:
        parser.error('the following argument are required: -ST')
    elif args.StartTime is not None and args.EndTime is not None:
        args.StartTime = " ".join(args.StartTime)
        args.EndTime = " ".join(args.EndTime)
        # Check the time format.
        check_start_time(args, parser)
        check_end_time(args, parser)
        compare_start_end_time(args, parser)
        check_start_end_year(args, parser)


def check_start_end_year(args, parser):
    """
    Function Description:Check The year cannot be later than 2050.
    Parameter:args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    if args.StartTime[2] != ":":
        if args.StartTime[0:4] > "2050":
            parser.error("argument -ST: the start and end " +
                         "years cannot be later than 2050")
        if args.EndTime[0:4] > "2050":
            parser.error("argument -ET: the start and end " +
                         "years cannot be later than 2050")


def compare_start_end_time(args, parser):
    """
    Function Description:The start time and end time must be in the same format.
    The start time cannot be later than the end time.
    Parameter:args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    if len(args.StartTime) != len(args.EndTime):
        parser.error("argument -ST: the start time and end " +
                     "time must be in the same format")
    if args.StartTime > args.EndTime:
        parser.error("argument -ST: the start time can " +
                     "not be greater than the end time")


def check_end_time(args, parser):
    """
    Function Description:check end time is format 'YYYY-MM-DD'.
    Parameter:args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    end_ret = match_time(args.EndTime)
    if not end_ret:
        parser.error("argument -ET: the time formats YYYY-MM-DD HH:MM, " +
                     "YYYY-MM-DD, and HH:MM are supported")


def check_start_time(args, parser):
    """
    Function Description:check start time is format 'YYYY-MM-DD'.
    Parameter:args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    start_ret = match_time(args.StartTime)
    if not start_ret:
        parser.error("argument -ST: the time formats YYYY-MM-DD HH:MM, " +
                     "YYYY-MM-DD, and HH:MM are supported")


def match_time(rule_time):
    """
    Function Description:Time format check
    Parameter:rule_time str:end time
    """
    time_type1 = r"^((?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-" + \
                 "(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|" + \
                 "(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|" + \
                 "[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29))" + \
                 r"\s([0-1]\d|2[0-3])\:[0-5]\d$"
    time_type2 = r"^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-" + \
                 "(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|" + \
                 "(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|" + \
                 "[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$"
    time_type3 = r"^([0-1]\d|2[0-3])\:[0-5]\d$"
    if match(time_type1, rule_time) is not None:
        return True
    if match(time_type2, rule_time) is not None:
        return True
    if match(time_type3, rule_time) is not None:
        return True
    return False


def setloginrule(client, args):
    """
    Function Description:Setting Login Rule Information
    Parameter:client refishClient:class object
    args object:CLI command
    """
    slotid = client.get_slotid()
    if slotid is None:
        return None
    url = "/redfish/v1/Managers/%s" % slotid
    resp_ru = client.get_resource(url)
    if resp_ru is None:
        return None
    if resp_ru['status_code'] == REDFISH_STATUS_CODE_200:
        # Set attributes.
        resp_ru = set_rulinfo(url, client, args)
    if resp_ru['status_code'] == REDFISH_STATUS_CODE_404:
        print('Failure: resource was not found')
    return resp_ru


def set_rulinfo(url, client, args):
    """
    Function Description:Set resource information.
    Parameter:url str:url
    client refishClient:class object
    args object:CLI command
    """
    # Encapsulate the request body.
    ret = package_request(args)
    if ret is None:
        return None
    # Set resource attributes.
    resp_ru = client.set_resource(url, ret)
    if resp_ru is None:
        return None
    if resp_ru['status_code'] == REDFISH_STATUS_CODE_200:
        flag = check_err_info(resp_ru['resource'], resp_ru['status_code'])
        if not flag:
            sys.exit(UREST_STATUS_CODE_144)
    if resp_ru['status_code'] == REDFISH_STATUS_CODE_400:
        check_err_info(resp_ru['message']['error'], resp_ru['status_code'])
    return resp_ru


def check_err_info(resp_ru, code_rule):
    """
    Function Description:Determine whether all attributes are set successfully.
    Parameter:resp_ru dict:redfish value
    code_rule int:redfish code
    """
    flag = False
    rule_msge = resp_ru.get("@Message.ExtendedInfo", "")
    # Determine whether the configuration is successful.
    if rule_msge == "":
        print('Success: successfully completed request')
        flag = True
    else:
        # Determine whether a permission problem occurs.
        #  The entire resource has only one type of permission.
        mess_id = rule_msge[0].get("MessageId", "")
        if mess_id in ["iBMC.1.0.PropertyModificationNeedPrivilege",
                       "Base.1.0.InsufficientPrivilege"]:
            print('Failure: you do not have the required permissions to '
                  'perform this operation')
        else:
            # Display messages.
            resq_mess(rule_msge, code_rule)
    return flag


def resq_mess(rule_msge, code_rule):
    """
    Function Description:Respond to messages.
    Parameter:rule_msge dict:redfish value
    code_rule int:redfish code
    """
    if code_rule == REDFISH_STATUS_CODE_200:
        print(FAILURE_MESS)
    else:
        stdout.write('Failure: ')
    idx = 0
    while idx < len(rule_msge):
        # Display messages.
        message = rule_msge[idx].get('Message', "")
        message = "%s%s" % (message[0].lower(), message[1:len(message) - 1])
        message = message.replace("Oem/%s/" % common_function.COMMON_KEY, "")

        if code_rule == REDFISH_STATUS_CODE_400 and idx == 0:
            print('%s' % message)
        else:
            print('         %s' % message)
        idx += 1


def package_request(args):
    """
    Function Description:Encapsulate the request body.
    Parameter:args object:CLI command
    """
    payload = {}
    payload_inner_dic = {}
    oem = {}
    if args.RuleType == "Rule1":
        ret = package_rule(args, 0)
    elif args.RuleType == "Rule2":
        ret = package_rule(args, 1)
    else:
        ret = package_rule(args, 2)
    payload_inner_dic["LoginRule"] = ret
    oem[common_function.COMMON_KEY] = payload_inner_dic
    payload["Oem"] = oem
    return payload


def package_rule(args, idx):
    """
    Function Description:Encapsulate rule information.
    Parameter:args object:CLI command
    idx int:id
    """
    rule = [{}, {}, {}]
    rule_message = {}
    if args.RuleEnabled is not None:
        rule_message['RuleEnabled'] = args.RuleEnabled == str(True)
    if args.StartTime is not None:
        rule_message["StartTime"] = args.StartTime
    if args.EndTime is not None:
        rule_message["EndTime"] = args.EndTime
    if args.IP is not None:
        rule_message["IP"] = args.IP
    if args.Mac is not None:
        rule_message["Mac"] = args.Mac
    rule[idx] = rule_message
    return rule
