from requests import Response
import demistomock as demisto
from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
from CommonServerUserPython import *  # noqa

import urllib3
from typing import Dict, Any
import base64
import re

# Disable insecure warnings
urllib3.disable_warnings()

''' CLIENT CLASS '''


class Client(BaseClient):

    def __init__(self, url: str, auth: tuple, headers: Dict, proxy: bool = False, verify: bool = False):
        self.url = url
        self.headers = headers
        super().__init__(base_url=url, verify=verify, proxy=proxy, auth=auth, headers=headers)

    def get_session_request(self, encoded_str: str) -> Dict:
        """ Gets a session from the API.
            Args:
                encoded_str: str - The string that contains username:password in base64.
            Returns:
                A dictionary with the session details.
        """
        url_suffix = '/session'
        self.headers['NSM-SDK-API'] = encoded_str
        return self._http_request(method='GET', url_suffix=url_suffix)

    def list_domain_firewall_policy_request(self, domain_id: int) -> Dict:
        """ Gets the list of Firewall Policies defined in a particular domain.
            Args:
                domain_id: int - The id of the domain.
            Returns:
                A dictionary with the firewall policy list.
        """
        url_suffix = f'/domain/{domain_id}/firewallpolicy'
        return self._http_request(method='GET', url_suffix=url_suffix)

    def get_firewall_policy_request(self, policy_id: int) -> Dict:
        """ Gets the Firewall Policy details.
            Args:
                policy_id: int - The id of the policy.
            Returns:
                A dictionary with the policy details.
        """
        url_suffix = f'/firewallpolicy/{policy_id}'
        return self._http_request(method='GET', url_suffix=url_suffix)

    def create_firewall_policy_request(self, body: Dict) -> Dict:
        """ Adds a new Firewall Policy and Access Rules.
            Args:
                body: Dict - The params to the API call.
            Returns:
                A dictionary with the id of the newly created policy.
        """
        url_suffix = '/firewallpolicy'
        return self._http_request(method='POST', url_suffix=url_suffix, json_data=body)

    def update_firewall_policy_request(self, body: Dict, policy_id: int) -> Dict:
        """ Updates an existing Firewall Policy and Access Rules.
            Args:
                body: Dict - The params to the API call.
                policy_id: int - The id of the updated policy.
            Returns:
                A dictionary with the request status, if it succeeded or not.
        """
        url_suffix = f'/firewallpolicy/{policy_id}'
        return self._http_request(method='PUT', url_suffix=url_suffix, json_data=body)

    def delete_firewall_policy_request(self, policy_id: int) -> Dict:
        """ Updates an existing Firewall Policy and Access Rules.
            Args:
                policy_id: int - The id of the updated policy.
            Returns:
                A dictionary with the request status, if it succeeded or not.
        """
        url_suffix = f'/firewallpolicy/{policy_id}'
        return self._http_request(method='DELETE', url_suffix=url_suffix)

    def list_domain_rule_objects_request(self, domain_id: int, rule_type: str) -> Dict:
        """ Gets the list of rule objects defined in a particular domain.
            Args:
                domain_id: int - The id of the domain.
                rule_type: str - The type of the rules to be returned.
            Returns:
                A dictionary with the rule objects list.
        """
        url_suffix = f'/domain/{domain_id}/ruleobject?type={rule_type}'
        return self._http_request(method='GET', url_suffix=url_suffix)

    def get_rule_object_request(self, rule_id: int) -> Dict:
        """ Gets the list of rule objects defined in a particular domain.
            Args:
                rule_id: int - The id of the rule.
            Returns:
                A dictionary with the rule object information.
        """
        url_suffix = f'/ruleobject/{rule_id}'
        return self._http_request(method='GET', url_suffix=url_suffix)

    def create_rule_object_request(self, body: Dict) -> Dict:
        """ Gets the list of rule objects defined in a particular domain.
            Args:
                body: Dict - The params to the API call.
            Returns:
                A dictionary with the id of the new rule object.
        """
        url_suffix = '/ruleobject'
        return self._http_request(method='POST', url_suffix=url_suffix, json_data=body)

    def update_rule_object_request(self, body: Dict, rule_id: int) -> Dict:
        """ Updates a Rule Object.
            Args:
                body: Dict - The params to the API call.
                rule_id: int - The rule id.
            Returns:
                A dictionary with the status of the request.
        """
        url_suffix = f'/ruleobject/{rule_id}'
        return self._http_request(method='PUT', url_suffix=url_suffix, json_data=body, resp_type='response')

    def delete_rule_object_request(self, rule_id: int) -> Dict:
        """ Updates a Rule Object.
            Args:
                rule_id: int - The rule id.
            Returns:
                A dictionary with the status of the request.
        """
        url_suffix = f'/ruleobject/{rule_id}'
        return self._http_request(method='DELETE', url_suffix=url_suffix)

    def get_alerts_request(self, time_period: str, start_time: str, end_time: str, state: str,
                           search: str, filter_arg: str, domain_id: int, page: str = None) -> Dict:
        """ Retrieves All Alerts.
            Args:
                time_period: str - The time period of the alert.
                start_time: str - The start time of the alert.
                end_time: str - The end time of the alert.
                state: str - The state of the alert.
                search: str - Search string in alert details.
                filter_arg: str - Filter alert by fields.
                page: str - Next/Previous page.
                domain_id: int - The id of the domain
            Returns:
                A dictionary with the list of alerts and info about the list.
        """
        params = {}
        if time_period:
            params['timeperiod'] = time_period
            if time_period == 'CUSTOM':
                params['starttime'] = start_time
                params['endtime'] = end_time
        if state:
            params['alertstate'] = state
        if search:
            params['search'] = search
        if filter_arg:
            params['filter'] = filter_arg
        if page:
            params['page'] = page
        if domain_id:
            params['domainId'] = f'{domain_id}'
        url_suffix = '/alerts'
        return self._http_request(method='GET', url_suffix=url_suffix, params=params)

    def get_alert_details_request(self, alert_id: int, sensor_id: int) -> Dict:
        """ Retrieves the alert details.
            Args:
                alert_id: int - The id of the relevant alert.
                sensor_id: int - The id of the relevant sensor.
            Returns:
                A dictionary with the alert details.
        """
        url_suffix = f'/alerts/{alert_id}'
        params = {
            'sensorId': sensor_id
        }
        return self._http_request(method='GET', url_suffix=url_suffix, params=params)

    def get_attacks_request(self, attack_id: str) -> Dict:
        """ If an attack id is given The command returns the details of the specific attack. Else, gets all available
        attack definitions in the Manager UI.
            Args:
                attack_id: str - The id of the relevant attack.
            Returns:
                A dictionary with the attack list of the specific attack details.
        """
        if attack_id:
            url_suffix = f'/attack/{attack_id}'
        else:
            url_suffix = '/attacks'
        response = self._http_request(method='GET', timeout=5000, url_suffix=url_suffix)
        return response

    def get_domains_request(self, domain_id: Optional[int]) -> Dict:
        """ If a domain id is given The command returns the details of the specific domain.
            Else, gets all available domains.
            Args:
                domain_id: Optional[int] - The id of the relevant attack.
            Returns:
                A dictionary with the attack list of the specific attack details.
        """
        url_suffix = '/domain'
        if domain_id:
            url_suffix = f'{url_suffix}/{domain_id}'
        return self._http_request(method='GET', url_suffix=url_suffix)

    def get_sensors_request(self, domain_id: Optional[int]) -> Dict:
        """ If a domain id is given The command returns the details of the sensors in the specific domain.
            Else, gets all available sensors.
            Args:
                domain_id: Optional[int] - The id of the relevant domain.
            Returns:
                A dictionary with the domains list of the specific domain details.
        """
        url_suffix = '/sensors'
        params = {}
        if domain_id:
            params['domain'] = domain_id
        return self._http_request(method='GET', url_suffix=url_suffix, params=params)

    def get_ips_policies_request(self, domain_id: int) -> Dict:
        """ Gets all the IPS Policies defined in the specific domain.
            Args:
                domain_id: int - The id of the relevant domain.
            Returns:
                A dictionary with ips policies list of the specific domain details.
        """
        url_suffix = f'/domain/{domain_id}/ipspolicies'
        return self._http_request(method='GET', url_suffix=url_suffix)

    def get_ips_policy_details_request(self, policy_id: int) -> Dict:
        """ Gets the policy details for the specific IPS policy.
            Args:
                policy_id: int - The id of the relevant ips policy.
            Returns:
                A dictionary with the ips policy details.
        """
        url_suffix = f'/ipspolicy/{policy_id}'
        return self._http_request(method='GET', url_suffix=url_suffix)

    def update_alerts_request(self, time_period: str, start_time: str, end_time: str, state: str,
                              search: str, filter_arg: str, body: Dict) -> Dict:
        """ Updates all relevant alerts.
            Args:
                time_period: str - The time period of the alert.
                start_time: str - The start time of the alert.
                end_time: str - The end time of the alert.
                state: str - The state of the alert.
                search: str - Search string in alert details.
                filter_arg: str - Filter alert by fields.
                body: Dict - The body of the request.
            Returns:
                A dictionary with the request status.
        """
        params = {}
        if time_period:
            params['timeperiod'] = time_period
            if time_period == 'CUSTOM':
                params['starttime'] = start_time
                params['endtime'] = end_time
        if state:
            params['alertstate'] = state
        if search:
            params['search'] = search
        if filter_arg:
            params['filter'] = filter_arg
        url_suffix = '/alerts'
        return self._http_request(method='PUT', url_suffix=url_suffix, params=params, json_data=body)

    def list_pcap_file_request(self, sensor_id: int) -> Dict:
        """ Retrieves the list of captured PCAP files.
            Args:
                sensor_id: int - the relevant sensor id.
            Returns:
                A dictionary with a list of PCAP file names.
        """
        url_suffix = f'/sensor/{sensor_id}/packetcapturepcapfiles'
        return self._http_request(method='GET', url_suffix=url_suffix)

    def export_pcap_file_request(self, sensor_id: int, body: Dict) -> Response:
        """ Retrieves the list of captured PCAP files.
            Args:
                sensor_id: int - The relevant sensor id.
                body: Dict - The parameter for the http request (file name).
            Returns:
                A dictionary with a list of PCAP file names.
        """
        url_suffix = f'/sensor/{sensor_id}/packetcapturepcapfile/export'
        self.headers['Accept'] = 'application/octet-stream'
        return self._http_request(method='PUT', url_suffix=url_suffix, json_data=body, resp_type='response')


''' HELPER FUNCTIONS '''


def encode_to_base64(str_to_convert: str) -> str:
    """ Converts a string to base64 string.
    Args:
        str_to_convert: str - The string that needs to be converted to base64.
    Returns:
        The converted string.
    """
    b = base64.b64encode(bytes(str_to_convert, 'utf-8'))  # bytes
    base64_str = b.decode('utf-8')  # convert bytes to string
    return base64_str


def get_session(client: Client, user_name_n_password: str) -> str:
    """ Gets the session string.
    Args:
        client: Client - A McAfeeNSM client.
        user_name_n_password: str - The username and password that needs to be encoded to base64
            in order to get the session information.
    Returns:
        The converted string.
    """
    user_name_n_password_encoded = encode_to_base64(user_name_n_password)
    session = client.get_session_request(user_name_n_password_encoded)
    return encode_to_base64(f'{session.get("session")}:{session.get("userId")}')


def pagination(records_list: List, limit: int, page: Optional[int], page_size: Optional[int]) -> List[Dict]:
    """ Returns the wanted records.
    Args:
        records_list: List - The original list of objects.
        limit: str - The amount of records to be returned
        page: Optional[int] - The page of the results (The results in page 1, 2 ...)
        page_size: Optional[int] - the number of records that will be in the page.
    Returns:
        The wanted records.
    """
    if page and page_size:
        num_rec_2_remove = (page_size * (page - 1))
        results_list = records_list[num_rec_2_remove:]
        return results_list[:page_size]
    else:
        return records_list[:limit]


def alerts_list_pagination(records_list: List, limit: int, page: Optional[int], page_size: Optional[int],
                           time_period: str, start_time: str, end_time: str, state: str, search: str, filter_arg: str,
                           client: Client, domain_id: int) -> List:
    """ Returns the wanted records.
    Args:
        records_list: List - The original list of objects.
        limit: int - The amount of records to be returned
        page: Optional[int] - The page of the results (The results in page 1, 2 ...)
        page_size: Optional[int] - The number of records in a page.
        time_period: str - The time period of the alert.
        start_time: str - The start time of the alert.
        end_time: str - The end time of the alert.
        state: str - The state of the alert.
        search: str - Search string in alert details.
        filter_arg: str - Filter alert by fields.
        client: Client - McAfeeNSMv2 client
        domain_id: str - The id of the domain.
    Returns:
        The wanted records.
    """
    if page and page_size:
        offset = (page_size * (page - 1))
        results_list = []
        while (offset + page_size > 1000) and (len(records_list) == 1000):
            records_list = records_list[offset:]
            results_list.extend(records_list)
            page_size = page_size - len(records_list)
            offset = 0 if offset <= 1000 else offset - 1000
            response = client.get_alerts_request(time_period, start_time, end_time, state, search, filter_arg,
                                                 domain_id, 'next')
            records_list = response.get('alertsList', [])

        records_list = records_list[offset:]
        results_list.extend(records_list[:page_size])
        return results_list
    else:
        results_list = []
        while limit > 1000 and (len(records_list) == 1000):
            results_list.extend(records_list)
            limit = limit - len(records_list)
            response = client.get_alerts_request(time_period, start_time, end_time, state, search, filter_arg,
                                                 domain_id, 'next')
            records_list = response.get('alertsList', [])
        results_list.extend(records_list[:limit])
        return results_list


def response_cases(response_str: str) -> str:
    """ Checks the response param and returns the correct response string.
    Args:
        response_str: str - The response string.
    Returns:
        The correct response string.
    Example:
        Scan -> SCAN
        Drop -> DROP
        Deny -> DENY
        Ignore -> IGNORE
        Stateless Ignore -> STATELESS_IGNORE
        Stateless Drop -> STATELESS_DROP
        Require Authentication-> REQUIRE_AUTHENTICATION
    """
    split_str = response_str.upper().split()
    if len(split_str) == 1:
        return split_str[0]
    else:
        return '_'.join(split_str)


def rule_object_type_cases(str_type: str, case: str) -> str:
    """ Checks the rule_object_type params and returns the correct format of them.
    Args:
        str_type: str - The type string.
        case: str - In what case should the letters be, upper or lower case.
    Returns:
        The correct rule_object_type string.
    Examples:
        - In list_domain_rule_objects_command the arguments should be in lower case and in this form:
            Endpoint IP V.4 -> hostipv4
            Range IP V.4 -> ipv4addressrange
            Network IP V.4 -> networkipv4
            Endpoint IP V.6 -> hostipv6
            Range IP V.6 -> ipv6addressrange
            Network IP V.6 -> networkipv6
        - In all the other commands that use this function, the arguments should be in upper case and in this form:
            Endpoint IP V.4 -> HOST_IPV_4
            Range IP V.4 -> IPV_4_ADDRESS_RANGE
            Network IP V.4 -> NETWORK_IPV_4
            Endpoint IP V.6 -> HOST_IPV_6
            Range IP V.6 -> IPV_6_ADDRESS_RANGE
            Network IP V.6 -> NETWORK_IPV_6
    """
    type_split = str_type.upper().replace('.', ' ').split()
    if 'ENDPOINT' in type_split[0]:
        r_type = f'HOST_IPV_{type_split[-1]}'
    elif 'RANGE' in type_split[0]:
        r_type = f'IPV_{type_split[-1]}_ADDRESS_RANGE'
    else:
        r_type = f'NETWORK_IPV_{type_split[-1]}'
    if case == 'low':
        return r_type.lower().replace('_', '')
    return r_type


def reverse_rule_object_type_cases(rule_type: str) -> str:
    """ Checks the rule_object_type params that return from the API call and returns the matching string in the UI.
    Args:
        rule_type: str - The type string.
    Returns:
        The matching string.
    Example:
        HOST_IPV_4 -> Endpoint IP V.4
        IPV_4_ADDRESS_RANGE -> Range IP V.4
        NETWORK_IPV_4 -> Network IP V.4
        HOST_IPV_6 -> Endpoint IP V.6
        IPV_6_ADDRESS_RANGE -> Range IP V.6
        NETWORK_IPV_6 -> Network IP V.6
    """
    number = '4' if ('4' in rule_type) else '6'
    if 'HOST' in rule_type:
        return f'Endpoint IP V.{number}'
    elif 'ADDRESS_RANGE' in rule_type:
        return f'Range IP V.{number}'
    else:
        return f'Network IP V.{number}'


def check_source_and_destination(source_rule_object_id: Optional[int], source_rule_object_type: Optional[str],
                                 destination_rule_object_id: Optional[int], destination_rule_object_type: Optional[str],
                                 create_or_update: str):
    """ Checks the source and destination objects.
    Args:
        source_rule_object_id: Optional[int] - Unique Rule Object ID.
        source_rule_object_type: Optional[str] - Source / Destination Mode.
        destination_rule_object_id: Optional[int] - Unique Rule Object ID.
        destination_rule_object_type: Optional[str] - Source / Destination Mode.
        create_or_update: str - From what function it was called.
    Returns:
        Throws exception .
    """
    if (source_rule_object_id and not source_rule_object_type and source_rule_object_id != -1) or (
            not source_rule_object_id and source_rule_object_type):
        # If the user provides source_rule_object_id he must provide source_rule_object_type and vice versa
        raise Exception('Please provide both source_rule_object_id and source_rule_object_type.')
    if (destination_rule_object_id and not destination_rule_object_type and destination_rule_object_id != -1) or \
            (not destination_rule_object_id and destination_rule_object_type):
        # If the user provides destination_rule_object_id he must provide destination_rule_object_type and vice versa
        raise Exception('Please provide both destination_rule_object_id and destination_rule_object_type.')
    if create_or_update == 'create':
        # if the user wants to create a new firewall policy, he must provide a source rule or destination rule or both.
        if source_rule_object_id == -1 and destination_rule_object_id == -1:
            raise Exception('You must provide the source fields or destination fields or both.')


def create_body_firewall_policy(domain: int, name: str, visible_to_child: bool, description: str, is_editable: bool,
                                policy_type: str, rule_description: str, response_param: str, rule_enabled: bool,
                                direction: str, source_object: List, destination_object: List) -> Dict:
    """
    Args:
        domain: int - The id of the domain.
        name: str - The name of the policy.
        visible_to_child: bool - Will the policy be visible to the child domain.
        description: str - the policy description.
        is_editable: bool - Is the policy editable.
        policy_type: str - The type of the policy.
        rule_description: str - The description of the rule.
        response_param: str - Action to be performed if the traffic matches this rule.
        rule_enabled: bool - Is Rule Enabled or not.
        direction: str - The rule direction.
        source_object: List - Information about the source addresses.
        destination_object: List - Information about the destination addresses.
    Returns:
        Returns the body for the request.
    """
    return {
        'Name': name,
        'DomainId': domain,
        'VisibleToChild': visible_to_child,
        'Description': description,
        'IsEditable': is_editable,
        'PolicyType': policy_type,
        'MemberDetails': {
            'MemberRuleList': [
                {
                    'Description': rule_description,
                    'Enabled': rule_enabled,
                    'Response': response_param,
                    'Direction': direction,
                    'SourceAddressObjectList': source_object,
                    'DestinationAddressObjectList': destination_object,
                    "SourceUserObjectList": [
                        {
                            "RuleObjectId": "-1",
                            "Name": "Any",
                            "RuleObjectType": "USER"
                        }
                    ],
                    "ServiceObjectList": [
                        {
                            "RuleObjectId": "-1",
                            "Name": "Any",
                            "RuleObjectType": None,
                            "ApplicationType": None
                        }
                    ],
                    "ApplicationObjectList": [],
                    "TimeObjectList": [
                        {
                            "RuleObjectId": "-1",
                            "Name": "Always",
                            "RuleObjectType": None
                        }
                    ]
                }
            ]
        }
    }


def create_body_create_rule(rule_type: str, address: List, number: int,
                            from_to_list: list[dict[str, Any | None]]) -> tuple:
    """ create part of the body for the command create_rule_object
        Args:
            rule_type: str - The type of the rule.
            address: List - A list of addresses, if relevant.
            number: int - The number of the IPV.
            from_to_list: List - A list that contains dictionaries with from and do addresses.
        Returns:
            Returns the body for the request.
        """
    if 'HOST' in rule_type:
        return f'HostIPv{number}', {
            f'hostIPv{number}AddressList': address
        }
    elif 'ADDRESS_RANGE' in rule_type:
        return f'IPv{number}AddressRange', {
            f'IPV{number}RangeList': from_to_list
        }
    else:
        return f'Network_IPV_{number}', {
            f'networkIPV{number}List': address
        }


def check_args_create_rule(rule_type: str, address: List, from_address: str, to_address: str, number: int):
    """ Validate the arguments of the function
        Args:
            rule_type: str - The type of the rule.
            address: List - A list of addresses, if relevant.
            from_address: str - The from address, if relevant.
            to_address: str - The to address, if relevant.
            number: int - The number of the addresses IP V.
    """
    if not address and not from_address and not to_address:
        raise Exception('Please enter a matching address.')
    if ('4' in rule_type and number == 6) or ('6' in rule_type and number == 4):
        raise Exception('The version of the IP in "rule_object_type" should match the addresses version.')
    if ('HOST' in rule_type or 'NETWORK' in rule_type) and (not address or from_address or to_address):
        raise Exception(f'If the "rule_object_type" is “Endpoint IP V.{number}” or “Network IP V.{number}” than only'
                        f' the argument “address_ip_v.{number}” must contain a value. The other address arguments '
                        f'should be empty.')
    if 'ADDRESS_RANGE' in rule_type and (not to_address or not from_address or address):
        raise Exception(f'If the "rule_object_type" is “Range IP V.{number}” than only the arguments '
                        f'“from_address_ip_v.{number}” and “to_address_ip_v.{number}” must contain a value, the other'
                        f' address arguments should be empty.')


def h_r_get_domains(children: List[Dict], contents: List):
    """ Creates the human readable for the command get_domains.
        Args:
            children: List[Dict] - A list of the children.
            contents: List - The human readable object.
        Returns:
            The human readable contains the relevant values.
    """
    for child in children:
        child['ID'] = child.get('id')
        del child['id']
        child['Name'] = child.get('name')
        del child['name']
        d = {
            'ID': child.get('ID'),
            'Name': child.get('Name')
        }
        contents.append(d)
        if child.get('childdomains', []):
            h_r_get_domains(child.get('childdomains', []), contents)


def update_source_destination_object(obj: List[Dict], rule_object_id: Optional[int], rule_object_type: Optional[str]) -> \
        List[Dict]:
    """ Updates the source and destination objects in the command update_firewall_policy.
        Args:
            obj: List[Dict] - The relevant object.
            rule_object_id: Optional[int] - The id of the rule.
            rule_object_type: Optional[str] - The type of the rule
        Returns:
            The updated object.
    """
    if rule_object_id:
        if rule_object_id == -1:
            raise Exception('If you want to delete an address please provide is_overwrite=true and the relevant '
                            'rule_object_id=-1.')
        new_object = {
            'RuleObjectId': rule_object_id,
            'RuleObjectType': rule_object_type
        }
        old_id = obj[0].get('RuleObjectId')
        # if the old id is -1, it means that there wasn't any specific rule before, and we need to overwrite the
        # "placeholder" rule.
        if old_id == '-1':
            obj = [new_object]
        else:
            obj.append(new_object)
    return obj


def overwrite_source_destination_object(rule_object_id: int | None, rule_object_type: Optional[str], dest_or_src: str,
                                        member_rule_list: Dict) -> List:
    """ overwrite the source and destination objects in the command update_firewall_policy.
        Args:
            rule_object_id: int | None - The id of the rule.
            rule_object_type: Optional[str] - The type of the rule.
            dest_or_src: str - Overwrite the destination or source object.
            member_rule_list: Dict - The first object in MemberRuleList in the API response.
        Returns:
            The overwrite object.
    """
    if rule_object_id:
        if rule_object_id == -1:
            return [{
                'RuleObjectId': -1,
                'RuleObjectType': None
            }]
        else:
            return [{
                'RuleObjectId': rule_object_id,
                'RuleObjectType': rule_object_type
            }]
    else:
        return member_rule_list.get(f'{dest_or_src}AddressObjectList', [Dict])


def update_filter(filter_arg: str) -> str:
    """ Removes the special characters from the name argument in filter, because tha api do not work with special
        characters.
        Args:
            filter_arg: str - The original filter
        Returns:
            The updated filter, without special chars.
        Example:
            - name:HTTP: IIS 6.0 (CVE-0000-0000) -> name:HTTP  IIS 6 0  CVE 0000 0000
    """
    split_filter = filter_arg.split(';')
    for index, s in enumerate(split_filter):
        if 'name' in s:
            s = s.replace('name:', '')
            s = re.sub('[^a-zA-Z0-9]', ' ', s)
            s = f'name:{s}'
            split_filter[index] = s
            break
    return ';'.join(split_filter)


def get_addresses_from_response(response: Dict) -> List:
    """ Returns the addresses from the response, for the human-readable in the command get_rule_object.
        Args:
            response: Dict - The response from the API.
        Returns:
            The list of addresses.
    """
    rule_type = response.get('ruleobjType', '')
    number = 4 if '4' in rule_type else 6
    if 'HOST' in rule_type:
        return response.get(f'HostIPv{number}', {}).get(f'hostIPv{number}AddressList', [])
    elif 'ADDRESS_RANGE' in rule_type:
        return response.get(f'IPv{number}AddressRange', {}).get(f'IPV{number}RangeList', [Dict])
    else:  # 'NETWORK'
        return response.get(f'Network_IPV_{number}', {}).get(f'networkIPV{number}List', [])


''' COMMAND FUNCTIONS '''


def test_module(client: Client, username_n_password: str) -> str:
    """ Test the connection to McAfee NSM.
    Args:
        client: Client - A McAfeeNSM client.
        username_n_password: str - The string that contains username:password to be encoded.
    Returns:
        'ok' if the connection was successful, else throws exception.
    """
    try:
        get_session(client, username_n_password)
        return 'ok'
    except DemistoException as e:
        raise Exception(e.message)


def list_domain_firewall_policy_command(client: Client, args: Dict) -> CommandResults:
    """ Gets the list of Firewall Policies defined in a particular domain.
    Args:
        client: client - A McAfeeNSM client.
        args: Dict - The function arguments.
    Returns:
        A CommandResult object with the list of Firewall Policies defined in a particular domain.
    """
    domain_id = int(args.get('domain_id', 0))
    limit = arg_to_number(args.get('limit', 50)) or 50
    page = arg_to_number(args.get('page'))
    page_size = arg_to_number(args.get('page_size'))
    if (page and not page_size) or (not page and page_size):
        raise Exception('Please provide both page and page_size arguments.')

    response = client.list_domain_firewall_policy_request(domain_id)
    result = sorted(response.get('FirewallPoliciesForDomainResponseList', []), key=lambda k: k['policyId'],
                    reverse=True)
    result = pagination(result, limit, page, page_size)
    contents = []
    for value in result:
        d = {'policyId': value.get('policyId'),
             'policyName': value.get('policyName'),
             'domainId': value.get('domainId'),
             'visibleToChild': value.get('visibleToChild'),
             'description': value.get('description'),
             'isEditable': value.get('isEditable'),
             'policyType': value.get('policyType'),
             'policyVersion': value.get('policyVersion'),
             'lastModUser': value.get('lastModUser')}
        contents.append(d)

    headers = ['policyId', 'policyName', 'domainId', 'visibleToChild', 'description', 'isEditable', 'policyType',
               'policyVersion', 'lastModUser']
    readable_output = tableToMarkdown(
        name='Firewall Policies List',
        t=contents,
        removeNull=True,
        headers=headers
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='NSM.Policy',
        outputs_key_field='policyId',
        outputs=result,
        raw_response=result
    )


def get_firewall_policy_command(client: Client, args: Dict) -> CommandResults:
    """ Gets the Firewall Policy details.
    Args:
        client: client - A McAfeeNSM client.
        args: Dict - The function arguments.
    Returns:
        A CommandResult object with the Firewall Policy details.
    """
    policy_id = int(args.get('policy_id', ''))
    include_rule_objects = argToBoolean(args.get('include_rule_objects', False))
    response = client.get_firewall_policy_request(policy_id)
    if not include_rule_objects:
        member_rule_list = response.get('MemberDetails', {}).get('MemberRuleList', [Dict])
        updated_member_rule_list = []
        for member in member_rule_list:
            d = {
                'Description': member.get('Description'),
                'Direction': member.get('Direction'),
                'Enabled': member.get('Enabled'),
                'Response': member.get('Response'),
                'IsLogging': member.get('IsLogging')
            }
            updated_member_rule_list.append(d)
        response.get('MemberDetails', {})['MemberRuleList'] = updated_member_rule_list
    contents = {'FirewallPolicyId': response.get('FirewallPolicyId'),
                'Name': response.get('Name'),
                'Description': response.get('Description'),
                'VisibleToChild': response.get('VisibleToChild'),
                'IsEditable': response.get('IsEditable'),
                'PolicyType': response.get('PolicyType'),
                'PolicyVersion': response.get('PolicyVersion'),
                'LastModifiedUser': response.get('LastModifiedUser'),
                'LastModifiedTime': response.get('LastModifiedTime')}
    headers = ['PolicyId', 'Name', 'Description', 'VisibleToChild', 'IsEditable', 'PolicyType', 'PolicyVersion',
               'LastModifiedUser', 'LastModifiedTime']
    readable_output = tableToMarkdown(
        name=f'Firewall Policy {policy_id}',
        t=contents,
        removeNull=True,
        headers=headers
    )
    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='NSM.Policy',
        outputs=response,
        raw_response=response,
        outputs_key_field='FirewallPolicyId'
    )


def create_firewall_policy_command(client: Client, args: Dict) -> CommandResults:
    """ Adds a new Firewall Policy and Access Rules.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a success message.
    """
    domain = arg_to_number(args.get('domain', '0'), required=True) or 0
    name = args.get('name', '')
    visible_to_child = argToBoolean(args.get('visible_to_child', True))
    description = args.get('description', '')
    is_editable = argToBoolean(args.get('is_editable', True)) or True
    policy_type = args.get('policy_type', '').upper()
    rule_description = args.get('rule_description', '')
    rule_enabled = argToBoolean(args.get('rule_enabled', True))
    response_param = response_cases(args.get('response', ''))
    direction = args.get('direction', '').upper()
    source_rule_object_id = arg_to_number(args.get('source_rule_object_id', -1))
    source_rule_object_type = args.get('source_rule_object_type', None)
    destination_rule_object_id = arg_to_number(args.get('destination_rule_object_id', -1))
    destination_rule_object_type = args.get('destination_rule_object_type', None)

    check_source_and_destination(source_rule_object_id, source_rule_object_type, destination_rule_object_id,
                                 destination_rule_object_type, 'create')

    source_rule_object_type = rule_object_type_cases(source_rule_object_type, 'up') if source_rule_object_type else None
    destination_rule_object_type = rule_object_type_cases(destination_rule_object_type, 'up') if \
        destination_rule_object_type else None
    source_object = overwrite_source_destination_object(source_rule_object_id, source_rule_object_type, '', {})
    destination_object = overwrite_source_destination_object(destination_rule_object_id, destination_rule_object_type,
                                                             '', {})

    body = create_body_firewall_policy(domain, name, visible_to_child, description, is_editable, policy_type,
                                       rule_description, response_param, rule_enabled, direction, source_object,
                                       destination_object)

    response = client.create_firewall_policy_request(body)
    response = {
        'FirewallPolicyId': response.get('createdResourceId')
    }
    new_firewall_policy_id = response.get('FirewallPolicyId')
    return CommandResults(readable_output=f'The firewall policy no.{new_firewall_policy_id} was created successfully',
                          outputs_prefix='NSM.Policy',
                          outputs=response,
                          raw_response=response,
                          outputs_key_field='FirewallPolicyId'
                          )


def update_firewall_policy_command(client: Client, args: Dict) -> CommandResults:
    """ Updates the Firewall Policy details.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a success message.
    """
    policy_id = int(args.get('policy_id', ''))
    domain = args.get('domain')
    name = args.get('name')
    visible_to_child = args.get('visible_to_child')
    description = args.get('description')
    is_editable = args.get('is_editable')
    policy_type = args.get('policy_type')
    rule_description = args.get('rule_description')
    response_param = args.get('response')
    rule_enabled = args.get('rule_enabled')
    direction = args.get('direction')
    source_rule_object_id = arg_to_number(args.get('source_rule_object_id', None))
    source_rule_object_type = args.get('source_rule_object_type', None)
    source_rule_object_type = rule_object_type_cases(source_rule_object_type, 'up') if source_rule_object_type else None
    destination_rule_object_id = arg_to_number(args.get('destination_rule_object_id', None))
    destination_rule_object_type = args.get('destination_rule_object_type')
    destination_rule_object_type = rule_object_type_cases(destination_rule_object_type, 'up') \
        if destination_rule_object_type else None
    is_overwrite = argToBoolean(args.get('is_overwrite', False))

    if is_overwrite and (not source_rule_object_id and not destination_rule_object_id):
        raise Exception('If is_overwrite=true than at least one of the rules (source or destination) must be provided.')

    check_source_and_destination(source_rule_object_id, source_rule_object_type, destination_rule_object_id,
                                 destination_rule_object_type, 'update')

    policy_get_details = client.get_firewall_policy_request(policy_id)

    if not policy_get_details.get('IsEditable'):
        raise Exception(f"The policy no.{policy_id} can't be edited")

    member_rule_list = policy_get_details.get('MemberDetails', {}).get('MemberRuleList', [Dict])[0]
    domain = policy_get_details.get('DomainId') if not domain else domain
    name = policy_get_details.get('Name') if not name else name
    visible_to_child = policy_get_details.get('VisibleToChild') if not visible_to_child \
        else argToBoolean(visible_to_child)
    description = policy_get_details.get('Description') if not description else description
    is_editable = policy_get_details.get('IsEditable') if not is_editable else argToBoolean(is_editable)
    policy_type = policy_get_details.get('PolicyType') if not policy_type else policy_type.upper()
    rule_description = member_rule_list.get('Description') if not rule_description else argToBoolean(rule_description)
    response_param = member_rule_list.get('Response') if not response_param else response_cases(response_param)
    rule_enabled = member_rule_list.get('Enabled') if not rule_enabled else argToBoolean(rule_enabled)
    direction = member_rule_list.get('Direction') if not direction else direction.upper()

    if is_overwrite:
        source_object = overwrite_source_destination_object(source_rule_object_id, source_rule_object_type, 'Source',
                                                            member_rule_list)
        destination_object = overwrite_source_destination_object(destination_rule_object_id,
                                                                 destination_rule_object_type, 'Destination',
                                                                 member_rule_list)
    else:
        source_object = member_rule_list.get('SourceAddressObjectList', [Dict])
        source_object = update_source_destination_object(source_object, source_rule_object_id, source_rule_object_type)

        destination_object = member_rule_list.get('DestinationAddressObjectList', [])
        destination_object = update_source_destination_object(destination_object, destination_rule_object_id,
                                                              destination_rule_object_type)

    body = create_body_firewall_policy(domain, name, visible_to_child, description, is_editable, policy_type,
                                       rule_description, response_param, rule_enabled, direction, source_object,
                                       destination_object)

    client.update_firewall_policy_request(body, policy_id)
    return CommandResults(readable_output=f'The firewall policy no.{policy_id} was updated successfully')


def delete_firewall_policy_command(client: Client, args: Dict) -> CommandResults:
    """ Deletes the specified Firewall Policy.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a success message.
    """
    policy_id = int(args.get('policy_id', ''))
    client.delete_firewall_policy_request(policy_id)
    return CommandResults(readable_output=f'The firewall policy no.{policy_id} was deleted successfully')


def list_domain_rule_objects_command(client: Client, args: Dict) -> CommandResults:
    """ Gets the list of rule objects defined in a particular domain.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with the list.
    """
    domain_id = arg_to_number(args.get('domain_id'), required=True) or 0
    rule_type = args.get('type', 'All')
    limit = arg_to_number(args.get('limit', 50)) or 50
    page = arg_to_number(args.get('page'))
    page_size = arg_to_number(args.get('page_size'))

    if (page and not page_size) or (not page and page_size):
        raise Exception('Please provide both page and page_size arguments.')

    if rule_type == 'All':
        rule_type = 'hostipv4,hostipv6,ipv4addressrange,ipv6addressrange,networkipv4,networkipv6'
    else:
        rule_type = rule_object_type_cases(rule_type, 'low')
    response = client.list_domain_rule_objects_request(domain_id, rule_type)
    results = pagination(response.get('RuleObjDef', []), limit, page, page_size)

    contents = []
    for record in results:
        record['ruleobjType'] = reverse_rule_object_type_cases(record.get('ruleobjType', None))
        d = {
            'RuleId': record.get('ruleobjId'),
            'Name': record.get('name'),
            'Description': record.get('description'),
            'VisibleToChild': record.get('visibleToChild'),
            'RuleType': record.get('ruleobjType')
        }
        contents.append(d)
    headers = ['RuleId', 'Name', 'Description', 'VisibleToChild', 'RuleType']
    readable_output = tableToMarkdown(name='List of Rule Objects',
                                      t=contents,
                                      removeNull=True,
                                      headers=headers)

    return CommandResults(readable_output=readable_output,
                          outputs_prefix='NSM.Rule',
                          outputs=results,
                          raw_response=results,
                          outputs_key_field='ruleobjId')


def get_rule_object_command(client: Client, args: Dict) -> CommandResults:
    """ Gets the details of a Rule Object.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with information about the rule object.
    """
    rule_id = int(args.get('rule_id', ''))
    response = client.get_rule_object_request(rule_id)
    response = response.get('RuleObjDef', {})
    addresses = get_addresses_from_response(response)
    response['ruleobjType'] = reverse_rule_object_type_cases(response.get('ruleobjType'))
    contents = {
        'RuleId': response.get('ruleobjId'),
        'Name': response.get('name'),
        'Description': response.get('description'),
        'VisibleToChild': response.get('visibleToChild'),
        'RuleType': response.get('ruleobjType'),
        'Addresses': addresses
    }
    headers = ['RuleId', 'Name', 'Description', 'VisibleToChild', 'RuleType', 'Addresses']
    readable_output = tableToMarkdown(name=f'Rule Objects {rule_id}',
                                      t=contents,
                                      removeNull=True,
                                      headers=headers)
    return CommandResults(readable_output=readable_output,
                          outputs_prefix='NSM.Rule',
                          outputs=response,
                          raw_response=response,
                          outputs_key_field='ruleobjId')


def create_rule_object_command(client: Client, args: Dict) -> CommandResults:
    """ Adds a new Rule Object.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a success message.
    """
    domain = arg_to_number(args.get('domain', 0))
    rule_type = rule_object_type_cases(args.get('rule_object_type', ''), 'up')
    name = args.get('name')
    visible_to_child = argToBoolean(args.get('visible_to_child', True))
    description = args.get('description')
    address_ip_v_4 = argToList(args.get('address_ip_v.4', None))
    from_address_ip_v_4 = args.get('from_address_ip_v.4')
    to_address_ip_v_4 = args.get('to_address_ip_v.4')
    address_ip_v_6 = argToList(args.get('address_ip_v.6'))
    from_address_ip_v_6 = args.get('from_address_ip_v.6')
    to_address_ip_v_6 = args.get('to_address_ip_v.6')

    if (address_ip_v_4 and address_ip_v_6) or (from_address_ip_v_4 and from_address_ip_v_6) or \
            (to_address_ip_v_4 and to_address_ip_v_6):
        raise Exception('Those pairs of arguments (address_ip_v_4 and address_ip_v_6) or '
                        '(from_address_ip_v_4 and from_address_ip_v_6) or (to_address_ip_v_4 and to_address_ip_v_6)'
                        'should not have values in parallel, only one at a time.')
    address = address_ip_v_4 if address_ip_v_4 else address_ip_v_6
    number = 4 if (address_ip_v_4 or from_address_ip_v_4) else 6
    from_address = from_address_ip_v_4 if from_address_ip_v_4 else from_address_ip_v_6
    to_address = to_address_ip_v_4 if to_address_ip_v_4 else to_address_ip_v_6

    check_args_create_rule(rule_type, address, from_address, to_address, number)

    body = {
        'RuleObjDef': {
            "domain": domain,
            "ruleobjType": rule_type,
            "visibleToChild": visible_to_child,
            "description": description,
            "name": name
        }
    }

    from_to_list = [{
        'FromAddress': from_address,
        'ToAddress': to_address
    }]

    d_name, extra_body = create_body_create_rule(rule_type, address, number, from_to_list)
    rule_obj_def = body.get('RuleObjDef', {})
    rule_obj_def[d_name] = extra_body
    response = client.create_rule_object_request(body)
    response = {
        'ruleobjId': response.get('createdResourceId')
    }

    return CommandResults(readable_output=f'The rule object no.{response.get("ruleobjId")} '
                                          f'was created successfully',
                          outputs_prefix='NSM.Rule',
                          outputs=response,
                          raw_response=response,
                          outputs_key_field='ruleobjId'
                          )


def update_rule_object_command(client: Client, args: Dict) -> CommandResults:
    """ Updates a Rule Object.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a success message.
    """
    domain = arg_to_number(args.get('domain', 0)) or 0
    rule_id = int(args.get('rule_id', ''))
    name = args.get('name')
    visible_to_child = args.get('visible_to_child')
    description = args.get('description')
    address_ip_v_4 = argToList(args.get('address_ip_v.4', None))
    from_address_ip_v_4 = args.get('from_address_ip_v.4')
    to_address_ip_v_4 = args.get('to_address_ip_v.4')
    address_ip_v_6 = argToList(args.get('address_ip_v.6'))
    from_address_ip_v_6 = args.get('from_address_ip_v.6')
    to_address_ip_v_6 = args.get('to_address_ip_v.6')
    is_overwrite = argToBoolean(args.get('is_overwrite', False))

    response_get = client.get_rule_object_request(rule_id)
    response_get = response_get.get('RuleObjDef', {})

    rule_type = response_get.get('ruleobjType')
    if (rule_type == 'HOST_IPV_4' or rule_type == 'NETWORK_IPV_4') and \
            (from_address_ip_v_4 or to_address_ip_v_4 or address_ip_v_6 or from_address_ip_v_6 or to_address_ip_v_6):
        raise Exception('If the rule object type is Endpoint IP V.4 or Network IP V.4 than only the argument '
                        '"address_ip_v_4" should contain a value')
    elif (rule_type == 'IPV_4_ADDRESS_RANGE') and \
            ((from_address_ip_v_4 and not to_address_ip_v_4) or (not from_address_ip_v_4 and to_address_ip_v_4)):
        raise Exception('If the rule object type is Range IP V.4 than both "from_address_ip_v_4" and '
                        '"to_address_ip_v_4" must contain a value or be empty.')
    elif (rule_type == 'IPV_4_ADDRESS_RANGE') and \
            (address_ip_v_4 or address_ip_v_6 or from_address_ip_v_6 or to_address_ip_v_6):
        raise Exception('If the rule object type is Range IP V.4 than only the arguments "from_address_ip_v_4" and '
                        '"to_address_ip_v_4" should contain a value')
    elif (rule_type == 'HOST_IPV_6' or rule_type == 'NETWORK_IPV_6') and \
            (address_ip_v_4 or from_address_ip_v_4 or to_address_ip_v_4 or from_address_ip_v_6 or to_address_ip_v_6):
        raise Exception('If the rule object type is Endpoint IP V.6 or Network IP V.6 than only the argument '
                        '"address_ip_v_6" should contain a value')
    elif (rule_type == 'IPV_6_ADDRESS_RANGE') and \
            ((from_address_ip_v_6 and not to_address_ip_v_6) or (not from_address_ip_v_6 and to_address_ip_v_6)):
        raise Exception('If the rule object type is Range IP V.6 than both "from_address_ip_v_6" and '
                        '"to_address_ip_v_6" must contain a value or be empty.')
    elif (rule_type == 'IPV_6_ADDRESS_RANGE') and \
            (address_ip_v_4 or address_ip_v_6 or from_address_ip_v_4 or to_address_ip_v_4):
        raise Exception('If the rule object type is Range IP V.6 than only the arguments "from_address_ip_v_6" and '
                        '"to_address_ip_v_6" should contain a value')

    name = name if name else response_get.get('name')
    visible_to_child = argToBoolean(visible_to_child) if visible_to_child else response_get.get('visibleToChild')
    description = description if description else response_get.get('description')
    from_to_address_ip_v_6 = []
    from_to_address_ip_v_4 = []
    if is_overwrite:
        if rule_type == 'HOST_IPV_4':
            address_ip_v_4 = address_ip_v_4 if address_ip_v_4 else response_get.get('HostIPv4', {}) \
                .get('hostIPv4AddressList')
        if rule_type == 'NETWORK_IPV_4':
            address_ip_v_4 = address_ip_v_4 if address_ip_v_4 else response_get.get('Network_IPV_4', {}) \
                .get('networkIPV4List')
        if from_address_ip_v_4:
            from_to_address_ip_v_4 = [{
                'FromAddress': from_address_ip_v_4,
                'ToAddress': to_address_ip_v_4
            }]
        elif not from_address_ip_v_4 and rule_type == 'IPV_4_ADDRESS_RANGE':
            from_to_address_ip_v_4 = response_get.get('IPv4AddressRange', {}).get('IPV4RangeList')
        if rule_type == 'HOST_IPV_6':
            address_ip_v_6 = address_ip_v_6 if address_ip_v_6 else response_get.get('HostIPv6', {}) \
                .get('hostIPv6AddressList')
        if rule_type == 'NETWORK_IPV_6':
            address_ip_v_6 = address_ip_v_6 if address_ip_v_6 else response_get.get('Network_IPV_6', {}) \
                .get('networkIPV6List')
        if from_address_ip_v_6:
            from_to_address_ip_v_6 = [{
                'FromAddress': from_address_ip_v_6,
                'ToAddress': to_address_ip_v_6
            }]
        elif not from_address_ip_v_6 and rule_type == 'IPV_6_ADDRESS_RANGE':
            from_to_address_ip_v_6 = response_get.get('IPv6AddressRange', {}).get('IPV6RangeList')
    else:
        if rule_type == 'HOST_IPV_4':
            old_address_ip_v_4 = response_get.get('HostIPv4', {}).get('hostIPv4AddressList', [])
            if address_ip_v_4:
                old_address_ip_v_4.extend(address_ip_v_4)
            address_ip_v_4 = old_address_ip_v_4
        elif rule_type == 'NETWORK_IPV_4':
            old_address_ip_v_4 = response_get.get('Network_IPV_4', {}).get('networkIPV4List', [])
            if address_ip_v_4:
                old_address_ip_v_4.extend(address_ip_v_4)
            address_ip_v_4 = old_address_ip_v_4
        elif rule_type == 'IPV_4_ADDRESS_RANGE':
            from_to_address_ip_v_4 = response_get.get('IPv4AddressRange', {}).get('IPV4RangeList', [])
            if from_address_ip_v_4 and to_address_ip_v_4:
                from_to_address_ip_v_4.append({
                    'FromAddress': from_address_ip_v_4,
                    'ToAddress': to_address_ip_v_4
                })
        elif rule_type == 'HOST_IPV_6':
            old_address_ip_v_6 = response_get.get('HostIPv6', {}).get('hostIPv6AddressList', [])
            if address_ip_v_6:
                old_address_ip_v_6.extend(address_ip_v_6)
            address_ip_v_6 = old_address_ip_v_6
        elif rule_type == 'NETWORK_IPV_6':
            old_address_ip_v_6 = response_get.get('Network_IPV_6', {}).get('hostIPv6AddressList', [])
            if address_ip_v_6:
                old_address_ip_v_6.extend(address_ip_v_6)
            address_ip_v_6 = old_address_ip_v_6
        elif rule_type == 'IPV_6_ADDRESS_RANGE':
            from_to_address_ip_v_6 = response_get.get('IPv6AddressRange', {}).get('networkIPV6List', [])
            if from_address_ip_v_6 and to_address_ip_v_6:
                from_to_address_ip_v_6.append({
                    'FromAddress': from_address_ip_v_6,
                    'ToAddress': to_address_ip_v_6
                })

    body = {
        'RuleObjDef': {
            "domain": domain,
            "ruleobjType": rule_type,
            "visibleToChild": visible_to_child,
            "description": description,
            "name": name
        }
    }
    address = address_ip_v_4 if address_ip_v_4 else address_ip_v_6
    number = 4 if (address_ip_v_4 or from_to_address_ip_v_4) else 6
    from_to_list = from_to_address_ip_v_4 if from_to_address_ip_v_4 else from_to_address_ip_v_6
    d_name, extra_body = create_body_create_rule(rule_type, address, number, from_to_list)
    rule_obj_def = body.get('RuleObjDef', {})
    rule_obj_def[d_name] = extra_body
    client.update_rule_object_request(body, rule_id)
    return CommandResults(readable_output=f'The rule object no.{rule_id} was updated successfully.')


def delete_rule_object_command(client: Client, args: Dict) -> CommandResults:
    """ Deletes a Rule Object.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a success message.
    """
    rule_id = int(args.get('rule_id', ''))
    client.delete_rule_object_request(rule_id)
    return CommandResults(readable_output=f'The rule object no.{rule_id} was deleted successfully')


def get_alerts_command(client: Client, args: Dict) -> CommandResults:
    """ Retrieves All Alerts.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a list of alerts.
    """
    limit = arg_to_number(args.get('limit', 50)) or 50
    page = arg_to_number(args.get('page'))
    page_size = arg_to_number(args.get('page_size'))
    time_period = args.get('time_period', 'LAST_7_DAYS')
    start_time = args.get('start_time', None)
    end_time = args.get('end_time', None)
    state = args.get('state', None)
    search = args.get('search', None)
    filter_arg = args.get('filter', None)
    domain_id = arg_to_number(args.get('domain_id')) or 0
    if args.get('new_state'):
        state = args.get('new_state')

    if (page and not page_size) or (not page and page_size):
        raise Exception('Please provide both page and page_size arguments.')
    if (start_time and not end_time) or (not start_time and end_time):
        raise Exception('If you provide one of the time parameters, you must provide the other as well.')
    if start_time and time_period != 'CUSTOM':
        time_period = 'CUSTOM'
    if time_period == 'CUSTOM' and not start_time:
        raise Exception('If you enter "time_period=CUSTOM" please enter start_time and end_time as well.')

    if filter_arg and 'name' in filter_arg:
        filter_arg = update_filter(filter_arg)

    response = client.get_alerts_request(time_period, start_time, end_time, state, search, filter_arg, domain_id)
    total_alerts_count = response.get('totalAlertsCount', 0)
    alerts_list = alerts_list_pagination(response.get('alertsList', []), limit, page, page_size, time_period,
                                         start_time, end_time, state, search, filter_arg, client, domain_id)
    result_list = []
    for alert in alerts_list:
        record = {
            'ID': alert.get('event', {}).get('alertId'),
            'Name': alert.get('name'),
            'State': alert.get('alertState'),
            'CreatedTime': alert.get('event', {}).get('time'),
            'Assignee': alert.get('assignTo'),
            'AttackSeverity': alert.get('attackSeverity'),
            'Application': alert.get('application'),
            'EventResult': alert.get('event', {}).get('result'),
            'SensorID': alert.get('detection', {}).get('deviceId'),
            'uniqueAlertId': alert.get('uniqueAlertId'),
            'Event': alert.get('event'),
            'Attack': alert.get('attack'),
            'Attacker': alert.get('attacker'),
            'Target': alert.get('target'),
            'MalwareFile': alert.get('malwareFile'),
            'endpointExcutable': alert.get('endpointExcutable'),
            'detection': alert.get('detection'),
            'layer7Data': alert.get('layer7Data')
        }
        event_obj = record.get('Event', {})
        event_obj['domain'] = alert.get('detection', {}).get('domain')
        event_obj['interface'] = alert.get('detection', {}).get('interface')
        event_obj['device'] = alert.get('detection', {}).get('device')
        result_list.append(record)
    alerts_list = result_list
    contents = []
    for alert_info in alerts_list:
        d = {'ID': alert_info.get('ID'),
             'Name': alert_info.get('Name'),
             'Event Time': alert_info.get('CreatedTime'),
             'Severity': alert_info.get('AttackSeverity'),
             'State': alert_info.get('State'),
             'Direction': alert_info.get('Event', {}).get('direction'),
             'Result': alert_info.get('Event.result'),
             'Attack Count': alert_info.get('EventResult'),
             'Attacker IP': alert_info.get('Attacker', {}).get('ipAddrs'),
             'Target IP': alert_info.get('Target', {}).get('ipAddrs')}
        contents.append(d)

    headers = ['ID', 'Name', 'Event Time', 'Severity', 'State', 'Direction', 'Result', 'Attack Count', 'Attacker IP',
               'Target IP']
    if args.get('new_state'):
        title = f'Updated Alerts list. Showing {len(alerts_list)} of {total_alerts_count}'
    else:
        title = f'Alerts list. Showing {len(alerts_list)} of {total_alerts_count}'
    readable_output = tableToMarkdown(
        name=title,
        t=contents,
        removeNull=True,
        headers=headers
    )
    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='NSM.Alerts',
        outputs=alerts_list,
        raw_response=response,
        outputs_key_field='ID'
    )


def get_alert_details_command(client: Client, args: Dict) -> CommandResults:
    """ Retrieves the relevant alert.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with the alert details.
    """
    alert_id = int(args.get('alert_id', ''))
    sensor_id = int(args.get('sensor_id', ''))
    response = client.get_alert_details_request(alert_id, sensor_id)
    response = {
        'ID': response.get('summary', {}).get('event', {}).get('alertId'),
        'Name': response.get('name'),
        'State': response.get('alertState'),
        'CreatedTime': response.get('summary', {}).get('event', {}).get('time'),
        'Assignee': response.get('assignTo'),
        'Description': response.get('description', {}).get('definition'),
        'EventResult': response.get('summary', {}).get('event', {}).get('result'),
        'Attack': {
            'attackCategory': response.get('description', {}).get('attackCategory'),
            'attackSubCategory': response.get('description', {}).get('attackSubCategory'),
            'nspId': response.get('description', {}).get('reference', {}).get('nspId')
        },
        'Protocols': response.get('description', {}).get('protocols'),
        'SensorID': response.get('summary', {}).get('event', {}).get('deviceId'),
        'Event': response.get('summary', {}).get('event'),
        'Attacker': response.get('summary', {}).get('attacker'),
        'Target': response.get('summary', {}).get('target'),
        'MalwareFile': response.get('details', {}).get('malwareFile'),
        'Details': response.get('details'),
        'uniqueAlertId': response.get('uniqueAlertId'),
        'summary': response.get('summary'),
        'description': response.get('description')
    }
    summary_obj = response.get('summary', {})
    del summary_obj['event']
    del summary_obj['attacker']
    del summary_obj['target']
    contents = {
        'ID': response.get('ID'),
        'Name': response.get('Name'),
        'Event Time': response.get('CreatedTime'),
        'State': response.get('State'),
        'Direction': response.get('Event', {}).get('direction'),
        'Result': response.get('EventResult'),
        'Attack Count': response.get('Event', {}).get('attackCount'),
        'Attacker IP': response.get('Attacker', {}).get('ipAddrs'),
        'Target IP': response.get('Target', {}).get('ipAddrs')
    }
    headers = ['ID', 'Name', 'Event Time', 'State', 'Direction', 'Result', 'Attack Count', 'Attacker IP', 'Target IP']
    readable_output = tableToMarkdown(
        name=f'Alert no.{alert_id}',
        t=contents,
        removeNull=True,
        headers=headers
    )
    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='NSM.Alerts',
        outputs=response,
        raw_response=response,
        outputs_key_field='uniqueAlertId'
    )


def get_attacks_command(client: Client, args: Dict) -> List:
    """ If an attack id is given The command returns the details for the specific attack.
        Else, gets all available attack definitions in the Manager UI.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with The attack details or attacks list.
    """
    attack_id = args.get('attack_id')
    if attack_id:
        if not re.match('^0x[0-9A-Fa-f]{8}$', attack_id):
            raise Exception('Error! Attack ID must be formatted as 32-bit hexadecimal number. for example: 0x1234BEEF')

    response = client.get_attacks_request(attack_id)

    if not attack_id:
        title = 'Attacks List'
        attacks_list = response.get('AttackDescriptorDetailsList', [])
    else:
        title = f'Attack no.{attack_id}'
        attacks_list = [response.get('AttackDescriptor', {})]
    result_list = []
    for attack in attacks_list:
        record = {
            'ID': attack.get('attackId'),
            'Name': attack.get('name'),
            'Direction': attack.get('DosDirection'),
            'Category': attack.get('description', {}).get('attackCategory'),
            'Severity': attack.get('Severity'),
            'description': attack.get('description')
        }
        result_list.append(record)
    attacks_list = result_list
    contents = []
    for attack in attacks_list:
        d = {
            'ID': attack.get('ID'),
            'Name': attack.get('Name'),
            'Direction': attack.get('Direction'),
            'Severity': attack.get('Severity'),
            'Category': attack.get('Category')
        }
        contents.append(d)
    headers = ['ID', 'Name', 'Direction', 'Severity', 'Category']
    readable_outputs = tableToMarkdown(
        name=title,
        t=contents,
        removeNull=True,
        headers=headers
    )
    if not attack_id:
        file_ = fileResult(filename='get-attacks-file-result', data=readable_outputs,
                           file_type=entryTypes['entryInfoFile'])
        return [file_]
    else:
        return [CommandResults(
            readable_output=readable_outputs,
            outputs_prefix='NSM.Attacks',
            outputs=attacks_list,
            raw_response=attacks_list,
            outputs_key_field='ID'
        )]


def get_domains_command(client: Client, args: Dict) -> CommandResults:
    """ If a domain id is given The command returns the details for the specific domain.
        Else, gets all available domains.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with The domain details or domains list.
    """
    domain_id = arg_to_number(args.get('domain_id', None))
    limit = arg_to_number(args.get('limit', 50)) or 50
    page = arg_to_number(args.get('page'))
    page_size = arg_to_number(args.get('page_size'))
    if (page and not page_size) or (not page and page_size):
        raise Exception('Please provide both page and page_size arguments.')

    response = client.get_domains_request(domain_id)
    results = response.get('DomainDescriptor', {})
    contents = []
    if domain_id is not None:
        title = f'Domain no.{domain_id}'
        results = {
            'ID': results.get('id'),
            'Name': results.get('name'),
            'childdomains': results.get('childdomains')
        }
        contents = [{
            'ID': results.get('ID'),
            'Name': results.get('Name')
        }]
    else:
        title = 'List of Domains'
        children = [results]
        h_r_get_domains(children, contents)
    contents = pagination(contents, limit, page, page_size)
    readable_outputs = tableToMarkdown(
        name=title,
        t=contents,
        removeNull=True
    )
    return CommandResults(
        readable_output=readable_outputs,
        outputs_prefix='NSM.Domains',
        outputs=results,
        raw_response=results,
        outputs_key_field='ID'
    )


def get_sensors_command(client: Client, args: Dict) -> CommandResults:
    """ Gets the list of sensors available in the specified domain. If the domain is not specified, details of all
        the sensors in all domains will be provided.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with The relevant sensors.
    """
    domain_id = arg_to_number(args.get('domain_id'))
    limit = arg_to_number(args.get('limit', 50)) or 50
    page = arg_to_number(args.get('page'))
    page_size = arg_to_number(args.get('page_size'))
    if (page and not page_size) or (not page and page_size):
        raise Exception('Please provide both page and page_size arguments.')

    response = client.get_sensors_request(domain_id)
    sensors_list = pagination(response.get('SensorDescriptor', [Dict]), limit, page, page_size)
    result_list = []
    for sensor in sensors_list:
        record = {
            'ID': sensor.get('sensorId'),
            'Name': sensor.get('name'),
            'Description': sensor.get('Description'),
            'DomainID': sensor.get('DomainID'),
            'IPSPolicyID': sensor.get('IPSPolicyID'),
            'IP Address': sensor.get('sensorIPAddress'),
            'model': sensor.get('model'),
            'isFailOver': sensor.get('isFailOver'),
            'isNTBA': sensor.get('isNTBA'),
            'isLoadBalancer': sensor.get('isLoadBalancer'),
            'SerialNumber': sensor.get('SerialNumber'),
            'SigsetVersion': sensor.get('SigsetVersion'),
            'DATVersion': sensor.get('DATVersion'),
            'SoftwareVersion': sensor.get('SoftwareVersion'),
            'LastSignatureUpdateTs': sensor.get('LastSignatureUpdateTs'),
            'ReconPolicyID': sensor.get('ReconPolicyID'),
            'LastModTs': sensor.get('LastModTs'),
            'nsmVersion': sensor.get('nsmVersion'),
            'MemberSensors': sensor.get('MemberSensors')
        }
        result_list.append(record)
    sensors_list = result_list
    contents = []
    for sensor in sensors_list:
        d = {
            'ID': sensor.get('ID'),
            'Name': sensor.get('Name'),
            'Description': sensor.get('Description'),
            'DomainID': sensor.get('DomainID'),
            'IPSPolicyID': sensor.get('IPSPolicyID'),
            'IP Address': sensor.get('IP Address')
        }
        contents.append(d)
    headers = ['ID', 'Name', 'Description', 'DomainID', 'IPSPolicyID', 'IP Address']
    if domain_id:
        title = f'The Sensors of Domain no.{domain_id}'
    else:
        title = 'Sensors List'
    readable_output = tableToMarkdown(
        name=title,
        t=contents,
        removeNull=True,
        headers=headers
    )
    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='NSM.Sensors',
        outputs=sensors_list,
        raw_response=sensors_list,
        outputs_key_field='ID'
    )


def get_ips_policies_command(client: Client, args: Dict) -> CommandResults:
    """ Gets all the IPS Policies defined in the specific domain.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with The relevant ips policies.
    """
    domain_id = arg_to_number(args.get('domain_id')) or 0
    limit = arg_to_number(args.get('limit', 50)) or 50
    page = arg_to_number(args.get('page'))
    page_size = arg_to_number(args.get('page_size'))
    if (page and not page_size) or (not page and page_size):
        raise Exception('Please provide both page and page_size arguments.')

    response = client.get_ips_policies_request(domain_id)
    policies_list = pagination(response.get('PolicyDescriptorDetailsList', [Dict]), limit, page, page_size)
    result_list = []
    for policy in policies_list:
        record = {
            'ID': policy.get('policyId'),
            'Name': policy.get('name'),
            'DomainID': policy.get('DomainId'),
            'VisibleToChildren': policy.get('VisibleToChild'),
            'IsEditable': policy.get('IsEditable'),
        }
        result_list.append(record)
    policies_list = result_list
    contents = []
    for policy in policies_list:
        d = {
            'ID': policy.get('ID'),
            'Name': policy.get('Name'),
            'DomainID': policy.get('DomainID'),
            'IsEditable': policy.get('IsEditable'),
            'VisibleToChildren': policy.get('VisibleToChildren')
        }
        contents.append(d)
    headers = ['ID', 'Name', 'DomainID', 'IsEditable', 'VisibleToChildren']
    readable_output = tableToMarkdown(
        name=f'IPS Policies List of Domain no.{domain_id}',
        t=contents,
        removeNull=True,
        headers=headers
    )
    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='NSM.IPSPolicies',
        outputs=policies_list,
        raw_response=policies_list,
        outputs_key_field='ID'
    )


def get_ips_policy_details_command(client: Client, args: Dict) -> CommandResults:
    """ gets the policy details for the specific IPS policy.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with The relevant ips policy details.
    """
    policy_id = int(args.get('policy_id', ''))
    response = client.get_ips_policy_details_request(policy_id)
    policy_details = response.get('PolicyDescriptor', {})
    policy_details = {
        'ID': policy_id,
        'Name': policy_details.get('PolicyName'),
        'CreatedTime': policy_details.get('Timestamp'),
        'VisibleToChildren': policy_details.get('IsVisibleToChildren'),
        'Version': policy_details.get('VersionNum'),
        'ExploitAttacks': policy_details.get('AttackCategory', {}).get('ExpolitAttackList'),
        'Description': policy_details.get('Description'),
        'InboundRuleSet': policy_details.get('InboundRuleSet'),
        'OutboundRuleSet': policy_details.get('OutboundRuleSet'),
        'OutboundAttackCategory': policy_details.get('OutboundAttackCategory'),
        'DosPolicy': policy_details.get('DosPolicy'),
        'DosResponseSensitivityLevel': policy_details.get('DosResponseSensitivityLevel'),
        'IsEditable': policy_details.get('IsEditable'),
        'IsLightWeightPolicy': policy_details.get('IsLightWeightPolicy')
    }
    contents = {
        'ID': policy_details.get('ID'),
        'Name': policy_details.get('Name'),
        'Description': policy_details.get('Description'),
        'CreatedTime': policy_details.get('CreatedTime'),
        'IsEditable': policy_details.get('IsEditable'),
        'VisibleToChildren': policy_details.get('VisibleToChildren'),
        'Version': policy_details.get('Version'),
        'InboundRuleSet': policy_details.get('InboundRuleSet'),
        'OutboundRuleSet': policy_details.get('OutboundRuleSet'),
    }
    headers = ['ID', 'Name', 'Description', 'CreatedTime', 'IsEditable', 'VisibleToChildren',
               'Version', 'InboundRuleSet', 'OutboundRuleSet']
    readable_output = tableToMarkdown(
        name=f'IPS Policy no.{policy_id} Details',
        t=contents,
        removeNull=True,
        headers=headers
    )
    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='NSM.IPSPolicies',
        outputs=policy_details,
        raw_response=policy_details,
        outputs_key_field='ID'
    )


def update_alerts_command(client: Client, args: Dict) -> CommandResults:
    """ Updates all the relevant alerts.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with the list of the changed alerts.
    """
    state = args.get('state', 'Any')
    time_period = args.get('time_period', None)
    start_time = args.get('start_time', None)
    end_time = args.get('end_time', None)
    new_state = args.get('new_state', None)
    new_assignee = args.get('new_assignee', None)
    search = args.get('search', None)
    filter_arg = args.get('filter', None)

    if not new_state and not new_assignee:
        raise Exception('Error! You must specify a new alert state or a new assignee')
    if (start_time and not end_time) or (not start_time and end_time):
        raise Exception('If you provide one of the time parameters, you must provide the other as well')
    if start_time and time_period != 'CUSTOM':
        time_period = 'CUSTOM'

    if filter_arg and 'name' in filter_arg:
        filter_arg = update_filter(filter_arg)

    body = {
        'alertState': new_state,
        'assignTo': new_assignee
    }

    response = client.update_alerts_request(time_period, start_time, end_time, state, search, filter_arg, body)

    if response.get('status') != 1:
        raise Exception('Error! Failed to update alerts.')
    return get_alerts_command(client, args)


def list_pcap_file_command(client: Client, args: Dict) -> CommandResults:
    """ Retrieves the list of captured PCAP files.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a list of captured PCAP files.
    """
    sensor_id = int(args.get('sensor_id', ''))
    limit = arg_to_number(args.get('limit', 50)) or 50
    page = arg_to_number(args.get('page'))
    page_size = arg_to_number(args.get('page_size'))
    if (page and not page_size) or (not page and page_size):
        raise Exception('Please provide both page and page_size arguments.')

    response = client.list_pcap_file_request(sensor_id)
    files_list = pagination(response.get('files', []), limit, page, page_size)
    contents = []
    for file_name in files_list:
        d = {
            'FileName': file_name
        }
        contents.append(d)
    readable_output = tableToMarkdown(
        name='PCAP files List',
        t=contents,
        removeNull=True
    )
    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='NSM.PcapFile',
        outputs=files_list,
        raw_response=files_list
    )


def export_pcap_file_command(client: Client, args: Dict) -> List:
    """ Exports the captured PCAP file.
        Args:
            client: client - A McAfeeNSM client.
            args: Dict - The function arguments.
        Returns:
            A CommandResult object with a list of captured PCAP files.
    """
    sensor_id = int(args.get('sensor_id', ''))
    file_name = args.get('file_name')
    body = {
        'fileName': file_name
    }
    response = client.export_pcap_file_request(sensor_id, body)
    file_ = fileResult(filename=file_name, data=response.content, file_type=EntryType.ENTRY_INFO_FILE)
    return [file_]


''' MAIN FUNCTION '''


def main() -> None:  # pragma: no cover

    url = f"{demisto.params().get('url')}/sdkapi"
    user_name = demisto.params().get('credentials', {}).get('identifier', "")
    password = demisto.params().get('credentials', {}).get('password', "")
    verify_certificate = not demisto.params().get('insecure', False)
    proxy = demisto.params().get('proxy', False)
    auth = (user_name, password)

    demisto.debug(f'Command being called is {demisto.command()}')
    try:

        headers: Dict = {
            'Accept': 'application/vnd.nsm.v1.0+json',
            'Content-Type': 'application/json'
        }

        client = Client(url=url, auth=auth, headers=headers, proxy=proxy, verify=verify_certificate)
        if demisto.command() != 'test-module':
            session_str = get_session(client, f'{user_name}:{password}')
            headers['NSM-SDK-API'] = session_str
            client = Client(url=url, auth=auth, headers=headers, proxy=proxy, verify=verify_certificate)

        if demisto.command() == 'test-module':
            # This is the call made when pressing the integration Test button.
            result_str = test_module(client, f'{user_name}:{password}')
            return_results(result_str)
        elif demisto.command() == 'nsm-list-domain-firewall-policy':
            result = list_domain_firewall_policy_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-get-firewall-policy':
            result = get_firewall_policy_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-create-firewall-policy':
            result = create_firewall_policy_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-update-firewall-policy':
            result = update_firewall_policy_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-delete-firewall-policy':
            result = delete_firewall_policy_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-list-domain-rule-object':
            result = list_domain_rule_objects_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-get-rule-object':
            result = get_rule_object_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-create-rule-object':
            result = create_rule_object_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-update-rule-object':
            result = update_rule_object_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-delete-rule-object':
            result = delete_rule_object_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-get-alerts':
            result = get_alerts_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-get-alert-details':
            result = get_alert_details_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-get-attacks':
            results_list = get_attacks_command(client, demisto.args())
            return_results(results_list)
        elif demisto.command() == 'nsm-get-domains':
            result = get_domains_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-get-sensors':
            result = get_sensors_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-get-ips-policies':
            result = get_ips_policies_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-get-ips-policy-details':
            result = get_ips_policy_details_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-update-alerts':
            result = update_alerts_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-list-pcap-file':
            result = list_pcap_file_command(client, demisto.args())
            return_results(result)
        elif demisto.command() == 'nsm-export-pcap-file':
            results_list = export_pcap_file_command(client, demisto.args())
            return_results(results_list)
        else:
            raise NotImplementedError('This command is not implemented yet.')

    # Log exceptions and return errors
    except Exception as e:
        return_error(f'Failed to execute {demisto.command()} command.\nError:\n{str(e)}')


''' ENTRY POINT '''

if __name__ in ('__main__', '__builtin__', 'builtins'):
    main()
