# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
# with the License. A copy of the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
# OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and
# limitations under the License.
import functools
import sys

import boto3
from botocore.exceptions import BotoCoreError, ClientError


class VpcFactory:
    """This class handles vpc automation related to pcluster."""

    class _ExceptionHandler:
        @staticmethod
        def handle_client_exception(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                try:
                    return func(*args, **kwargs)
                except (BotoCoreError, ClientError) as e:
                    print("ERROR during handling of the VPC in the {0} phase.".format(func.__name__))
                    print(e.response["Error"]["Message"])
                    sys.exit(1)

            return wrapper

    @_ExceptionHandler.handle_client_exception
    def __init__(self, aws_region_name):
        """
        Inizialize the VpcHandler with the specified region.

        :param aws_region_name: the region in which you want to use the VpcHandler
        """
        self.__client = boto3.client("ec2", region_name=aws_region_name)
        self.ec2 = boto3.resource("ec2", region_name=aws_region_name)

    @_ExceptionHandler.handle_client_exception
    def create(self, cidr_block="10.0.0.0/16"):
        """
        Create a vpc for the given region name.

        :return: the id of the created vpc
        :raise RuntimeError: if some problems occurred during vpc creation
        """
        response = self.__client.create_vpc(CidrBlock=cidr_block)
        return response["Vpc"]["VpcId"]

    @_ExceptionHandler.handle_client_exception
    def setup(self, vpc_id, name=None):
        """
        Set the parameters necessary for a vpc to be pcluster-compatible.

        :param vpc_id: the target vpc_id
        :param name: the name that you want to give to the vpc
        :raise RuntimeError: if some problems occurred during the operation
        """
        vpc = self.ec2.Vpc(vpc_id)
        if name:
            self.ec2.create_tags(Resources=[vpc_id], Tags=[{"Key": "Name", "Value": name}])
        vpc.modify_attribute(EnableDnsHostnames={"Value": True})
        vpc.modify_attribute(EnableDnsSupport={"Value": True})

    @_ExceptionHandler.handle_client_exception
    def check(self, vpc_id):
        """
        Check whether the given vpc respects the condition needed for pcluster.

        :param vpc_id: the target vpc_id
        :return: True if the vpc is pcluster compatible
        :raise RuntimeError: if some problems occurred during the operation
        """
        vpc = self.ec2.Vpc(vpc_id)
        dns_resolution = vpc.describe_attribute(Attribute="enableDnsSupport")["EnableDnsSupport"]["Value"]
        dns_hostnames = vpc.describe_attribute(Attribute="enableDnsHostnames")["EnableDnsHostnames"]["Value"]

        if not dns_hostnames:
            print(f"DNS Hostnames of the VPC {vpc_id} must be set to True")
        if not dns_resolution:
            print(f"DNS Resolution of the VPC {vpc_id} must be set to True")
        if vpc.dhcp_options_id == "default":
            print(f"DHCP options of the VPC {vpc_id} must be set.")

        # default is equal to NO dhcp options set
        return dns_resolution and dns_hostnames and vpc.dhcp_options_id != "default"
