#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2019, Kevin Breit (@kbreit) <kevin.breit@kevinbreit.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type

ANSIBLE_METADATA = {
    'metadata_version': '1.1',
    'status': ['preview'],
    'supported_by': 'community'
}

DOCUMENTATION = r'''
---
module: meraki_malware
short_description: Manage Malware Protection in the Meraki cloud
version_added: "2.9"
description:
- Fully configure malware protection in a Meraki environment.
notes:
- Some of the options are likely only used for developers within Meraki.
options:
    state:
      description:
      - Specifies whether object should be queried, created/modified, or removed.
      choices: [absent, present, query]
      default: query
      type: str
    net_name:
      description:
      - Name of network which configuration is applied to.
      aliases: [network]
      type: str
    net_id:
      description:
      - ID of network which configuration is applied to.
      type: str
    allowed_urls:
      description:
      - List of URLs to whitelist.
      suboptions:
        url:
          description:
          - URL string to allow.
          type: str
        comment:
          description:
          - Human readable information about URL.
          type: str
    allowed_files:
      description:
      - List of files to whitelist.
      suboptions:
        sha256:
          description:
          - 256-bit hash of file.
          type: str
          aliases: [ hash ]
        comment:
          description:
          - Human readable information about file.
          type: str
    mode:
      description:
      - Enabled or disabled state of malware protection.
      choices: [disabled, enabled]


author:
- Kevin Breit (@kbreit)
extends_documentation_fragment: meraki
'''

EXAMPLES = r'''
  - name: Enable malware protection
    meraki_malware:
      auth_key: abc123
      state: present
      org_name: YourOrg
      net_name: YourNet
      mode: enabled
    delegate_to: localhost

  - name: Set whitelisted url
    meraki_malware:
      auth_key: abc123
      state: present
      org_name: YourOrg
      net_name: YourNet
      mode: enabled
      allowed_urls:
        - url: www.google.com
          comment: Google
    delegate_to: localhost

  - name: Set whitelisted file
    meraki_malware:
      auth_key: abc123
      state: present
      org_name: YourOrg
      net_name: YourNet
      mode: enabled
      allowed_files:
        - sha256: e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503
          comment: random zip
    delegate_to: localhost

  - name: Get malware settings
    meraki_malware:
      auth_key: abc123
      state: query
      org_name: YourNet
      net_name: YourOrg
    delegate_to: localhost
'''

RETURN = r'''
data:
    description: List of administrators.
    returned: success
    type: complex
    contains:
        mode:
            description: Mode to enable or disable malware scanning.
            returned: success
            type: str
            sample: enabled
        allowed_files:
            description: List of files which are whitelisted.
            returned: success
            type: complex
            contains:
                sha256:
                    description: sha256 hash of whitelisted file.
                    returned: success
                    type: str
                    sample: e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503
                comment:
                    description: Comment about the whitelisted entity
                    returned: success
                    type: str
                    sample: TPS report
        allowed_urls:
            description: List of URLs which are whitelisted.
            returned: success
            type: complex
            contains:
                url:
                    description: URL of whitelisted site.
                    returned: success
                    type: str
                    sample: site.com
                comment:
                    description: Comment about the whitelisted entity
                    returned: success
                    type: str
                    sample: Corporate HQ
'''

import os
from ansible.module_utils.basic import AnsibleModule, json, env_fallback
from ansible.module_utils._text import to_native
from ansible.module_utils.common.dict_transformations import recursive_diff
from ansible.module_utils.network.meraki.meraki import MerakiModule, meraki_argument_spec


def main():
    # define the available arguments/parameters that a user can pass to
    # the module

    urls_arg_spec = dict(url=dict(type='str'),
                         comment=dict(type='str'),
                         )

    files_arg_spec = dict(sha256=dict(type='str', aliases=['hash']),
                          comment=dict(type='str'),
                          )

    argument_spec = meraki_argument_spec()
    argument_spec.update(state=dict(type='str', choices=['absent', 'present', 'query'], default='query'),
                         net_name=dict(type='str', aliases=['network']),
                         net_id=dict(type='str'),
                         mode=dict(type='str', choices=['enabled', 'disabled']),
                         allowed_urls=dict(type='list', default=None, elements='dict', options=urls_arg_spec),
                         allowed_files=dict(type='list', default=None, elements='dict', options=files_arg_spec),
                         )

    # seed the result dict in the object
    # we primarily care about changed and state
    # change is if this module effectively modified the target
    # state will include any data that you want your module to pass back
    # for consumption, for example, in a subsequent task
    result = dict(
        changed=False,
    )
    # the AnsibleModule object will be our abstraction working with Ansible
    # this includes instantiation, a couple of common attr would be the
    # args/params passed to the execution, as well as if the module
    # supports check mode
    module = AnsibleModule(argument_spec=argument_spec,
                           supports_check_mode=True,
                           )
    meraki = MerakiModule(module, function='malware')

    meraki.params['follow_redirects'] = 'all'

    query_url = {'malware': '/networks/{net_id}/security/malwareSettings'}
    update_url = {'malware': '/networks/{net_id}/security/malwareSettings'}

    meraki.url_catalog['get_one'].update(query_url)
    meraki.url_catalog['update'] = update_url

    org_id = meraki.params['org_id']
    if org_id is None:
        org_id = meraki.get_org_id(meraki.params['org_name'])
    net_id = meraki.params['net_id']
    if net_id is None:
        nets = meraki.get_nets(org_id=org_id)
        net_id = meraki.get_net_id(net_name=meraki.params['net_name'], data=nets)

    # Check for argument completeness
    if meraki.params['state'] == 'present':
        if meraki.params['allowed_files'] is not None or meraki.params['allowed_urls'] is not None:
            if meraki.params['mode'] is None:
                meraki.fail_json(msg="mode must be set when allowed_files or allowed_urls is set.")

    # Assemble payload
    if meraki.params['state'] == 'present':
        payload = dict()
        if meraki.params['mode'] is not None:
            payload['mode'] = meraki.params['mode']
        if meraki.params['allowed_urls'] is not None:
            payload['allowedUrls'] = meraki.params['allowed_urls']
        if meraki.params['allowed_files'] is not None:
            payload['allowedFiles'] = meraki.params['allowed_files']

    if meraki.params['state'] == 'query':
        path = meraki.construct_path('get_one', net_id=net_id)
        data = meraki.request(path, method='GET')
        if meraki.status == 200:
            meraki.result['data'] = data
    elif meraki.params['state'] == 'present':
        path = meraki.construct_path('get_one', net_id=net_id)
        original = meraki.request(path, method='GET')
        if meraki.is_update_required(original, payload):
            if meraki.module.check_mode is True:
                diff = recursive_diff(original, payload)
                original.update(payload)
                meraki.result['diff'] = {'before': diff[0],
                                         'after': diff[1],
                                         }
                meraki.result['data'] = original
                meraki.result['changed'] = True
                meraki.exit_json(**meraki.result)
            path = meraki.construct_path('update', net_id=net_id)
            data = meraki.request(path, method='PUT', payload=json.dumps(payload))
            if meraki.status == 200:
                diff = recursive_diff(original, payload)
                meraki.result['diff'] = {'before': diff[0],
                                         'after': diff[1],
                                         }
                meraki.result['data'] = data
                meraki.result['changed'] = True
        else:
            meraki.result['data'] = original

    # in the event of a successful module execution, you will want to
    # simple AnsibleModule.exit_json(), passing the key/value results
    meraki.exit_json(**meraki.result)


if __name__ == '__main__':
    main()
