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

# Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see http://www.gnu.org/licenses/.

from __future__ import (absolute_import, division, print_function)

__metaclass__ = type

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

DOCUMENTATION = """
---
module: ali_vpc
short_description: Configure Alibaba Cloud virtual private cloud(VPC)
description:
    - Create, Delete Alicloud virtual private cloud(VPC).
      It supports updating VPC description.
options:
  state:
    description:
      -  Whether or not to create, delete VPC.
    choices: ['present', 'absent']
    type: str
    default: 'present'
  name:
    description:
      - The name to give your VPC, which is a string of 2 to 128 Chinese or English characters. It must begin with an
        uppercase/lowercase letter or a Chinese character and can contain numerals, "_" or "-".
        It cannot begin with http:// or https://.
        This is used in combination with C(cidr_block) to determine if a VPC already exists.
    aliases: ['vpc_name']
    type: str
  vpc_id:
    description:
      - The id of VPC, required when operate existing vpc.
    type: str
    aliases: ['id']
  description:
    description:
      - The description of VPC, which is a string of 2 to 256 characters. It cannot begin with http:// or https://.
    type: str
  cidr_block:
    description:
      - The primary CIDR of the VPC. This is used in conjunction with the C(name) to ensure idempotence.
    aliases: ['cidr']
    required: True
    type: str
  user_cidrs:
    description:
      - List of user custom cidr in the VPC. It no more than three.
    type: list
    elements: str
  multi_ok:
    description:
      - By default the module will not create another VPC if there is another VPC with the same name and CIDR block.
        Specify this as true if you want duplicate VPCs created.
    default: False
    type: bool
  recent:
    description:
      - By default the module will not choose the recent one if there is another VPC with the same I(name) and
       I(cidr_block). Specify this as true if you want to target the recent VPC.
        There will be conflict when I(multi_ok=True) and I(recent=True).
    default: False
    type: bool
  tags:
    description:
      - A hash/dictionaries of vpc tags. C({"key":"value"})
    type: dict
  purge_tags:
    description:
      - Delete existing tags on the vpc that are not specified in the task.
        If True, it means you have to specify all the desired tags on each task affecting a vpc.
    default: False
    type: bool
notes:
    - There will be launch a virtual router along with creating a vpc successfully.
    - There is only one virtual router in one vpc and one route table in one virtual router.
requirements:
    - "python >= 3.6"
    - "footmark >= 1.14.1"
extends_documentation_fragment:
    - alicloud
author:
    - "He Guimin (@xiaozhu36)"
"""

EXAMPLES = """
# Note: These examples do not set authentication details, see the Alibaba Cloud Guide for details.
- name: Create a new vpc
  ali_vpc:
    cidr_block: '192.168.0.0/16'
    name: 'Demo_VPC'
    description: 'Demo VPC'

- name: Choose the latest VPC as target when there are several vpcs with same name and cidr block
  ali_vpc:
    cidr_block: '192.168.0.0/16'
    name: 'Demo_VPC'
    recent: True

- name: Delete a vpc
  ali_vpc:
    state: absent
    cidr_block: '192.168.0.0/16'
    name: 'Demo_VPC'
"""

RETURN = '''
vpc:
    description: info about the VPC that was created or deleted
    returned: always
    type: complex
    contains:
        cidr_block:
            description: The CIDR of the VPC
            returned: always
            type: str
            sample: 10.0.0.0/8
        creation_time:
            description: The time the VPC was created.
            returned: always
            type: str
            sample: '2018-06-24T15:14:45Z'
        description:
            description: The VPC description.
            returned: always
            type: str
            sample: "my ansible vpc"
        id:
            description: alias of 'vpc_id'.
            returned: always
            type: str
            sample: vpc-c2e00da5
        is_default:
            description: indicates whether this is the default VPC
            returned: always
            type: bool
            sample: false
        state:
            description: state of the VPC
            returned: always
            type: str
            sample: available
        tags:
            description: tags attached to the VPC, includes name
            returned: always
            type: dict
            sample:
        user_cidrs:
            description: The custom CIDR of the VPC
            returned: always
            type: list
            sample: []
        vpc_id:
            description: VPC resource id
            returned: always
            type: str
            sample: vpc-c2e00da5
        vpc_name:
            description: Name of the VPC
            returned: always
            type: str
            sample: my-vpc
        vrouter_id:
            description: The ID of virtual router which in the VPC
            returned: always
            type: str
            sample: available
        vswitch_ids:
            description: List IDs of virtual switch which in the VPC
            returned: always
            type: list
            sample: [vsw-123cce3, vsw-34cet4v]
'''

import time
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.alicloud_ecs import ecs_argument_spec, vpc_connect

HAS_FOOTMARK = False

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


def vpc_exists(module, vpc, vpc_id, name, cidr_block, multi, recent):
    """Returns None or a vpc object depending on the existence of a VPC. When supplied
    with a CIDR and Name, it will check them to determine if it is a match
    otherwise it will assume the VPC does not exist and thus return None.
    """
    if multi:
        return None
    matching_vpcs = []
    try:
        for v in vpc.describe_vpcs():
            if cidr_block and v.cidr_block != cidr_block:
                continue
            if name and v.vpc_name != name:
                continue
            if vpc_id and v.vpc_id != vpc_id:
                continue
            matching_vpcs.append(v)

    except Exception as e:
        module.fail_json(msg="Failed to describe VPCs: {0}".format(e))

    if len(matching_vpcs) == 1:
        return matching_vpcs[0]
    elif len(matching_vpcs) > 1:
        if recent:
            return matching_vpcs[-1]
        module.fail_json(msg='Currently there are {0} VPCs that have the same name and '
                             'CIDR block you specified. If you would like to create '
                             'the VPC anyway please pass True to the multi_ok param. '
                             'Or, please pass True to the recent param to choose the recent one.'.format(len(matching_vpcs)))
    return None


def main():
    argument_spec = ecs_argument_spec()
    argument_spec.update(dict(
        state=dict(default='present', choices=['present', 'absent']),
        cidr_block=dict(type='str', required=True, aliases=['cidr']),
        user_cidrs=dict(type='list', elements='str'),
        name=dict(type='str', aliases=['vpc_name']),
        vpc_id=dict(type='str', aliases=['id']),
        multi_ok=dict(type='bool', default=False),
        description=dict(type='str'),
        recent=dict(type='bool', default=False),
        tags=dict(type='dict'),
        purge_tags=dict(type='bool', default=False)
    ))

    module = AnsibleModule(argument_spec=argument_spec)

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

    vpc_conn = vpc_connect(module)

    # Get values of variable
    state = module.params['state']
    vpc_name = module.params['name']
    description = module.params['description']
    vpc_id = module.params['vpc_id']

    if str(description).startswith('http://') or str(description).startswith('https://'):
        module.fail_json(msg='description can not start with http:// or https://')
    if str(vpc_name).startswith('http://') or str(vpc_name).startswith('https://'):
        module.fail_json(msg='vpc_name can not start with http:// or https://')

    changed = False

    # Check if VPC exists
    vpc = vpc_exists(module, vpc_conn, vpc_id, vpc_name, module.params['cidr_block'], module.params['multi_ok'], module.params['recent'])

    if state == 'absent':
        if not vpc:
            module.exit_json(changed=changed, vpc={})

        try:
            module.exit_json(changed=vpc.delete(), vpc={})
        except VPCResponseError as ex:
            module.fail_json(msg='Unable to delete vpc {0}, error: {1}'.format(vpc.id, ex))

    if not vpc:
        params = module.params
        params['client_token'] = "Ansible-Alicloud-%s-%s" % (hash(str(module.params)), str(time.time()))
        params['vpc_name'] = vpc_name
        try:
            vpc = vpc_conn.create_vpc(**params)
            module.exit_json(changed=True, vpc=vpc.get().read())
        except VPCResponseError as e:
            module.fail_json(msg='Unable to create vpc, error: {0}'.format(e))

    if not description:
        description = vpc.description

    try:
        if vpc.modify(vpc_name, description):
            changed = True
    except VPCResponseError as e:
        module.fail_json(msg='Unable to modify vpc {0}, error: {1}'.format(vpc.id, e))

    tags = module.params['tags']
    if module.params['purge_tags']:
        if not tags:
            tags = vpc.tags
        try:
            if vpc.remove_tags(tags):
                changed = True
            module.exit_json(changed=changed, vpc=vpc.get().read())
        except Exception as e:
            module.fail_json(msg="{0}".format(e))

    if tags:
        try:
            if vpc.add_tags(tags):
                changed = True
        except Exception as e:
            module.fail_json(msg="{0}".format(e))
    module.exit_json(changed=changed, vpc=vpc.get().read())


if __name__ == '__main__':
    main()
