#!/usr/bin/python
# Copyright (c) 2017-present Alibaba Group Holding Limited. <xiaozhu36>
# 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 = '''
---
module: ali_slb_vsg
short_description: Create, Delete VServerGroup and Modify its name or backend servers.
description:
  - Create and delete a VServer group
  - Add or remove backend servers or network interfaces to/from the VServer group
options:
    state:
      description:
        - Create and delete a VServer group.
      default: 'present'
      choices: ['present', 'absent']
      type: str
    load_balancer_id:
      description:
        - The Server Load Balancer instance ID.
          This is used in combination with C(name) to determine if a VServer group already exists.
      required: True
      aliases: ['lb_id']
      type: str
    vserver_group_name:
      description:
        - Virtual server group name.
        - One of I(vserver_group_name) and I(vserver_group_id) must be specified when operate existing slb group.
      aliases: ['group_name', 'name']
      type: str
    backend_servers:
      description:
        - List of  that need to be added or.
        - List of hash/dictionaries backend servers or network interfaces to add in this group (see example).
          If none are supplied, no backend servers will be enabled. Each server has several keys and refer to
          https://www.alibabacloud.com/help/doc-detail/35215.htm. Each key should be format as under_score.
          Currently the valid keys including "server_ids", "server_id", "port", "weight" and "type".
        - If you have multiple servers to add and they have the same port, weight, type, you can use the server_ids parameter, which is a list of ids.
      type: list
      elements: dict
    purge_backend_servers:
      description:
        - Purge existing backend servers or ENIs on VServer group that are not found in backend_servers.
        - If True, existing servers or ENIs will be purged from the resource to match exactly what is defined by
          I(backend_servers). If the I(backend_servers) is not set then servers will not be modified.
        - If True, it means you have to specify all the desired backend servers or ENIs on each task affecting a VServer group.
      default: False
      type: bool
    vserver_group_id:
      description:
        - Virtual server group id.
        - One of I(vserver_group_name) and I(vserver_group_id) must be specified when operate existing slb group.
      aliases: ['group_id']
      type: str
    multi_ok:
      description:
        - By default the module will not create another Load Balancer if there is another Load Balancer
          with the same I(name). Specify this as true if you want duplicate Load Balancers created.
      default: False
      type: bool
requirements:
    - "python >= 3.6"
    - "footmark >= 1.19.0"
extends_documentation_fragment:
    - alibaba.alicloud.alicloud
author:
  - "He Guimin (@xiaozhu36)"
'''

EXAMPLES = '''
# Note: These examples do not set authentication details, see the Alibaba Cloud Guide for details.
- name: Create VServer Group in SLB
  alibaba.alicloud.ali_slb_vsg:
    load_balancer_id: 'lb-cnqnc234'
    name: 'ansible-vsg'

- name: Add backend servers to vserver group
  alibaba.alicloud.ali_slb_vsg:
    load_balancer_id: 'lb-cnqnc234'
    name: 'ansible-vsg'
    backend_servers:
      - instance_id: 'i-f2n3cn34c'
        port: 8080
        weight: 100
        type: ecs
      - instance_id: 'eni-n34cjf4vd'
        port: 8081
        weight: 100
        type: eni

- name: Purge backend servers from vserver group
  alibaba.alicloud.ali_slb_vsg:
    load_balancer_id: 'lb-cnqnc234'
    name: 'ansible-vsg'
    backend_servers:
      - instance_id: 'eni-f2n3cn34c'
        port: 8080
        weight: 100
        type: eni
      - instance_id: 'eni-n34cjf4vd'
        port: 8081
        weight: 100
        type: eni
    purge_backend_servers: True

- name: Delete VServer Group in SLB
  alibaba.alicloud.ali_slb_vsg:
    load_balancer_id: 'lb-cnqnc234'
    name: 'ansible-vsg'
    state: absent
'''

RETURN = '''
vserver_group:
    description:
      - info about the virtual server group that was created or deleted.
    returned: on present
    type: complex
    contains:
        address:
            description: The IP address of the loal balancer
            returned: always
            type: str
            sample: "47.94.26.126"
        backend_servers:
            description: The load balancer's backend servers
            returned: always
            type: complex
            contains:
                port:
                    description: The backend server port
                    returned: always
                    type: int
                    sample: 22
                server_id:
                    description: The backend server id
                    returned: always
                    type: str
                    sample: "i-vqunci342"
                type:
                    description: The backend server type, ecs or eni
                    returned: always
                    type: str
                    sample: "ecs"
                weight:
                    description: The backend server weight
                    returned: always
                    type: int
                    sample: 100
        id:
            description: The ID of the virtual server group was created. Same as vserver_group_id.
            returned: always
            type: str
            sample: "rsp-2zehblhcv"
        vserver_group_id:
            description: The ID of the virtual server group was created.
            returned: always
            type: str
            sample: "rsp-2zehblhcv"
        vserver_group_name:
            description: The name of the virtual server group was created.
            returned: always
            type: str
            sample: "ansible-ali_slb_vsg"
        name:
            description: The name of the virtual server group was created.
            returned: always
            type: str
            sample: "ansible-ali_slb_vsg"
        tags:
            description: The load balancer tags
            returned: always
            type: dict
            sample: {}
'''

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.alibaba.alicloud.plugins.module_utils.alicloud_ecs import ecs_argument_spec, slb_connect


HAS_FOOTMARK = False

try:
    from footmark.exception import SLBResponseError
    HAS_FOOTMARK = True
except ImportError:
    HAS_FOOTMARK = False


VALID_SERVER_PARAMS = ["server_id", "port", "weight", "type", "server_ids"]


def check_backend_servers(module, servers):
    for s in servers:
        for key in list(s.keys()):
            if key not in VALID_SERVER_PARAMS:
                module.fail_json(msg='Invalid backend server key {0}. Valid keys: {1}.'.format(key, VALID_SERVER_PARAMS))


def parse_server_ids(servers):
    parse_server = []
    if servers:
        for s in servers:
            if "server_ids" in s:
                ids = s.pop("server_ids")
                for id in ids:
                    server = {"server_id": id}
                    server.update(s)
                    parse_server.append(server)
            else:
                parse_server.append(s)
    return parse_server


def format_backend_servers(servers):
    backend_servers = []
    if servers:
        parse_server = parse_server_ids(servers)
        for s in parse_server:
            server = {}
            for key, value in list(s.items()):
                split = []
                for k in str(key).split("_"):
                    split.append(str.upper(k[0]) + k[1:])
                server["".join(split)] = value
            backend_servers.append(server)
    return backend_servers


def filter_backend_servers(existing, inputting):
    old = []
    new = []
    removed = []
    existingList = []
    inputtingList = []
    oldList = []
    inputting = parse_server_ids(inputting)

    for s in existing:
        existingList.append(s['server_id'])

    for s in inputting:
        inputtingList.append(s['server_id'])

    for s in inputting:
        if s['server_id'] in existingList:
            old.append(s)
            oldList.append([s['server_id']])
            continue
        new.append(s)

    for s in existing:
        key = s['server_id']
        if key in inputtingList:
            if key not in oldList:
                old.append(s)
            continue
        removed.append(s)

    return old, new, removed


def main():
    argument_spec = ecs_argument_spec()
    argument_spec.update(dict(
        state=dict(type='str', default='present', choices=['present', 'absent']),
        load_balancer_id=dict(type='str', required=True, aliases=['lb_id']),
        vserver_group_name=dict(type='str', required=True, aliases=['group_name', 'name']),
        backend_servers=dict(type='list', elements='dict'),
        vserver_group_id=dict(type='str', aliases=['group_id']),
        purge_backend_servers=dict(type='bool', default=False),
        multi_ok=dict(type='bool', default=False)
    ))

    module = AnsibleModule(argument_spec=argument_spec,
                           required_if=([
                               ('state', 'present', ['backend_servers'])
                           ])
                           )

    if HAS_FOOTMARK is False:
        module.fail_json(msg='footmark required for the module ali_slb_vsg.')

    slb = slb_connect(module)
    state = module.params['state']
    lb_id = module.params['load_balancer_id']
    vsg_name = module.params['vserver_group_name']
    vserver_group_id = module.params['vserver_group_id']
    changed = False
    matching = None

    if not module.params['multi_ok']:
        try:
            matching_vsgs = []
            for group in slb.describe_vserver_groups(**{'load_balancer_id': lb_id}):
                if vsg_name and group.name != vsg_name:
                    continue
                if vserver_group_id and group.id != vserver_group_id:
                    continue
                matching_vsgs.append(group)
            if len(matching_vsgs) == 1:
                matching = matching_vsgs[0]
            elif len(matching_vsgs) > 1:
                module.fail_json(msg='Currently there are {0} virtual server groups that have the same name {1}. '
                                     'If you would like to create anyway '
                                     'please pass True to the multi_ok param.'.format(len(matching_vsgs), vsg_name))
        except Exception as e:
            module.fail_json(msg=str("Unable to describe vserver group attribute, error:{0}".format(e)))

    if state == 'absent':
        if matching:
            try:
                changed = matching.delete()
            except Exception as e:
                module.fail_json(msg=str("Unable to delete vserver group, error: {0}".format(e)))
        module.exit_json(changed=changed, vserver_group={})

    backend_servers = module.params['backend_servers']
    check_backend_servers(module, backend_servers)

    if not matching:
        try:
            params = module.params
            params['backend_servers'] = format_backend_servers(backend_servers[:20])
            matching = slb.create_vserver_group(**params)
            changed = True
        except Exception as e:
            module.fail_json(msg=str("Unable to create vserver group error:{0}".format(e)))

    if backend_servers:
        old, new, removed = filter_backend_servers(matching.backend_servers['backend_server'], backend_servers)
        if old:
            try:
                if matching.modify(backend_servers=old):
                    changed = True
            except Exception as e:
                module.fail_json(msg='Modify backend servers failed: {0}'.format(e))

        if new:
            try:
                if matching.add(backend_servers=new):
                    changed = True
            except Exception as e:
                module.fail_json(msg='Add backend servers failed: {0}'.format(e))

        if module.params['purge_backend_servers'] and removed:
            try:
                if matching.remove(backend_servers=removed):
                    changed = True
            except Exception as e:
                module.fail_json(msg='Remove backend servers failed: {0}'.format(e))
    module.exit_json(changed=changed, vserver_group=matching.get().read())


if __name__ == '__main__':
    main()
