# -*- coding: utf-8 -*-

# Copyright (c) 2014 Baidu.com, Inc. 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. You may obtain a copy
#  of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions
# and limitations under the License.

"""
This module provides a client class for EIP.
"""

import copy
import json
import logging
import uuid

from baidubce import utils
from baidubce.auth import bce_v1_signer
from baidubce.bce_base_client import BceBaseClient
from baidubce.http import bce_http_client
from baidubce.http import handler
from baidubce.http import http_methods
from baidubce.services.eip.model import EipStatus

_logger = logging.getLogger(__name__)


class EipClient(BceBaseClient):
    """
    eip sdk client
    """
    version = b'/v1'
    prefix = b'/eip'

    def __init__(self, config=None):
        """
        :type config: baidubce.BceClientConfiguration
        :param config:
        """
        BceBaseClient.__init__(self, config)

    def create_eip(self, bandwidth_in_mbps, name=None, billing=None,
                   route_type=None, client_token=None, config=None):
        """
        Create an eip with the specified options.

        :type bandwidth_in_mbps: int
        :param bandwidth_in_mbps: specify the bandwidth in Mbps

        :type name: string
        :param name: name of eip. The optional parameter

        :type billing: Billing
        :param billing: billing information. The optional parameter,
         default paymentTiming is Postpaid

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: created eip address, for example,{"eip":"x.x.x.x"}
        """
        body = {
            'name': name,
            'bandwidthInMbps': bandwidth_in_mbps
        }
        if route_type is not None:
            body['routeType'] = route_type
        if billing is None:
            body['billing'] = {
                'paymentTiming': 'Postpaid',
                'billingMethod': 'ByBandwidth'
            }
        else:
            body['billing'] = {
                'paymentTiming': billing.payment_timing,
                'billingMethod': billing.billing_method,
                'reservation': {
                    'reservationLength': billing.reservation_length,
                    'reservationTimeUnit': billing.reservation_time_unit
                }
            }
        path = self._get_path()
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'clientToken': client_token
        }

        return self._send_request(http_methods.POST, path,
                                  body=json.dumps(body), params=params,
                                  config=config)

    def resize_eip(self, eip, new_bandwidth_in_mbps, client_token=None,
                   config=None):
        """
        Resizing eip

        :type eip: string
        :param eip: eip address to be resized

        :type new_bandwidth_in_mbps: int
        :param new_bandwidth_in_mbps: specify new bandwidth in Mbps for eip

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """
        body = {
            'newBandwidthInMbps': new_bandwidth_in_mbps
        }
        path = utils.append_uri(self._get_path(), eip)
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'resize': b'',
            b'clientToken': client_token
        }
        return self._send_request(http_methods.PUT, path, params=params,
                                  body=json.dumps(body), config=config)

    def purchase_reserved_eip(self, eip, billing=None, client_token=None,
                              config=None):
        """
        PurchaseReserved eip with fixed duration,only Prepaid eip can do this

        :type eip: string
        :param eip: eip address to be renewed

        :type billing: Billing
        :param billing: billing information. The optional parameter,
         default reservationLength is 1

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """
        if billing is None:
            body = {
                'billing': {
                    'reservation': {
                        'reservationLength': 1,
                        'reservationTimeUnit': 'Month'
                    }
                }
            }
        else:
            body = {
                'billing': {
                    'reservation': {
                        'reservationLength': billing.reservation_length,
                        'reservationTimeUnit': billing.reservation_time_unit
                    }
                }
            }
        path = utils.append_uri(self._get_path(), eip)
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'purchaseReserved': b'',
            b'clientToken': client_token
        }
        return self._send_request(http_methods.PUT, path, params=params,
                                  body=json.dumps(body), config=config)
    
    def start_auto_renew_eip(self, eip, auto_renew_time_unit=None, auto_renew_time=None, 
                             client_token=None, config=None):
        """
        Enable auto renew for specified EIP.

        :type eip: string
        :param eip: eip address to be enable to auto-renew.

        :type auto_renew_time_unit: string
        :param auto_renew_time_unit: time unit of auto_renew_time, default 'Month'.

        :type auto_renew_time: int
        :param auto_renew_time: the unit of time for auto renew,
        default auto_renew_time is 1

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """
        if auto_renew_time_unit is None:
            auto_renew_time_unit = 'month'
        if auto_renew_time is None:
            auto_renew_time = 1
        body = {
            'autoRenewTimeUnit': auto_renew_time_unit,
            'autoRenewTime': auto_renew_time
        }
        path = utils.append_uri(self._get_path(), eip)
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'startAutoRenew': b'',
            b'clientToken': client_token
        }
        return self._send_request(http_methods.PUT, path, params=params,
                                  body=json.dumps(body), config=config)
    
    def stop_auto_renew_eip(self, eip, client_token=None, config=None):
        """
        Disable auto renew for specified EIP.

        :type eip: string
        :param eip: eip address to be disable to auto-renew.

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """

        path = utils.append_uri(self._get_path(), eip)

        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'stopAutoRenew': b'',
            b'clientToken': client_token
        }

        # Sending the HTTP request
        return self._send_request(http_methods.PUT, path, params=params, config=config)


    def bind_eip(self, eip, instance_type, instance_id, instance_ip, client_token=None,
                 config=None):
        """
        bind the eip to a specified instanceId and instanceType

        :type eip: string
        :param eip: eip address to be bound

        :type instance_type: string
        :param instance_type: type of instance to be bound(BCC BLB et.)

        :type instance_id: string
        :param instance_id: id of instance to be bound

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """
        body = {
            'instanceType': instance_type,
            'instanceId': instance_id
        }
        if instance_ip is not None:
            body['instanceIp'] = instance_ip
        path = utils.append_uri(self._get_path(), eip)
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'bind': b'',
            b'clientToken': client_token
        }
        return self._send_request(http_methods.PUT, path, params=params,
                                  body=json.dumps(body), config=config)

    def unbind_eip(self, eip, client_token=None, config=None):
        """
        unbind the eip from a specified instance

        :type eip: string
        :param eip: eip address to be unbound

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """
        path = utils.append_uri(self._get_path(), eip)
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'unbind': b'',
            b'clientToken': client_token
        }
        return self._send_request(http_methods.PUT, path, params=params,
                                  config=config)

    def release_eip(self, eip, client_token=None, config=None):
        """
        release the eip(delete operation)
        Only the Postpaid instance or Prepaid which is expired can be released.
        if the eip has been bound, must unbind before releasing.

        :type eip: string
        :param eip: eip address to be released

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """
        path = utils.append_uri(self._get_path(), eip)
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'clientToken': client_token
        }
        return self._send_request(http_methods.DELETE, path, params=params,
                                  config=config)
    
    def direct_eip(self, eip, client_token=None, config=None):
        """
        turn on EIP pass through with the specific parameters.

        :type eip: string
        :param eip: the specific EIP.

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user, 
        a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config: None

        :return: BceResponse
        """
        path = utils.append_uri(self._get_path(), eip)
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'direct': b'',
            b'clientToken': client_token
        }
        return self._send_request(http_methods.PUT, path, params=params, config=config)
    
    def undirect_eip(self, eip, client_token, config=None):
        """
        Remove the direction of an EIP from a specific instance.
        
        :type eip: string
        :param eip: the EIP address to undirect.

        :type client_token: string
        :param client_token: If the clientToken is not specified by the user, 
        a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config: None

        :return: BceResponse
        """
        path = utils.append_uri(self._get_path(), eip)
        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'unDirect': b'',
            b'clientToken': client_token
        }
        return self._send_request(http_methods.PUT, path, params=params, config=config)


    def list_eips(self, eip=None, instance_type=None, ip_version=None, instance_id=None,
                  status=None, marker=None, max_keys=1000, config=None):
        """
        get a list of eip owned by the authenticated user and specified
        conditions. we can Also get a single eip function  through this
        interface by eip condition

        :type eip: string
        :param eip: eip address condition

        :type instance_type: string
        :param instance_type: bound instance type condition

        :type instance_id: string
        :param instance_id: bound instance id condition
        if query by the instanceId or instanceType condition, must provides
         both of them at the same time

        :type status: string
        :param status of eip condition
        if query by the status condition, must provides

        :type marker: string
        :param marker: The optional parameter marker specified in the original
         request to specify where in the results to begin listing.

        :type max_keys: int
        :param max_keys: The optional parameter to specifies the max number
         of list result to return. The default value is 1000.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: list of eip model, for example:
                {
                    "eipList": [
                        {
                            "name":"eip-xxxxxxx-1",
                            "eip": "x.x.x.x",
                            "status":"binded",
                            "instanceType": "BCC",
                            "instanceId": "i-xxxxxxx",
                            "bandwidthInMbps": 5,
                            "paymentTiming":"Prepaid",
                            "billingMethod":"ByBandwidth",
                            "createTime":"2016-03-08T08:13:09Z",
                            "expireTime":"2016-04-08T08:13:09Z"
                        },
                        {
                            "name":"eip-xxxxxxx-1",
                            "eip": "x.x.x.x",
                            "status":"binded",
                            "instanceType": "BCC",
                            "instanceId": "i-xxxxxxx",
                            "bandwidthInMbps": 1,
                            "paymentTiming":"Postpaid",
                            "billingMethod":"ByTraffic",
                            "createTime":"2016-03-08T08:13:09Z",
                            "expireTime":null
                        },
                    ],
                    "marker":"eip-xxxxxxx-1",
                    "isTruncated": true,
                    "nextMarker": "eip-DCB50387",
                    "maxKeys": 2
                }
        """
        path = self._get_path()
        params = {}
        if eip is not None:
            params[b'eip'] = eip
        if instance_type is not None:
            params[b'instanceType'] = instance_type
        if instance_id is not None:
            params[b'instanceId'] = instance_id
        if status is not None and isinstance(status, EipStatus):
            params[b'status'] = status.value
        if marker is not None:
            params[b'marker'] = marker
        if max_keys is not None:
            params[b'maxKeys'] = max_keys
        if ip_version is not None:
            params[b'ipVersion'] = ip_version
        return self._send_request(http_methods.GET, path, params=params,
                                  config=config)

    def list_eip_recycle(self, eip=None, name=None, marker=None, max_keys=1000, config=None):
        """
        list all EIP in the recycle bin with the specific parameters.

        :type eip: string
        :param eip: eip address condition

        :type name: string
        :param name: eip name condition

        :type marker: string
        :param marker: The optional parameter marker specified in the original
         request to specify where in the results to begin listing.

        :type max_keys: int
        :param max_keys: The optional parameter to specifies the max number
         of list result to return. The default value is 1000.

        :type config: baidubce.BceClientConfiguration
        :param config: None

        :return: list of eip model, for example:
                {
                    "eipList": [
                        {
                            "name":"eip-xxxxxxx-1",
                            "eip": "x.x.x.x",
                            "eip_id":"ip-xxxxxxxx",
                            "status": "paused",
                            "route_type": "BGP",
                            "bandwidth_in_mbps": 5,
                            "payment_timing":"Prepaid",
                            "billing_method":"ByBandwidth",
                            "recycle_time":"2016-03-08T08:13:09Z",
                            "scheduled_delete_time":"2016-04-08T08:13:09Z"
                        },
                        {
                            "name":"eip-xxxxxxx-2",
                            "eip": "x.x.x.x",
                            "eip_id":"ip-xxxxxxxx",
                            "status": "paused",
                            "route_type": "BGP",
                            "bandwidth_in_mbps": 10,
                            "payment_timing":"Postpaid",
                            "billing_method":"ByBandwidth",
                            "recycle_time":"2016-03-08T08:13:09Z",
                            "scheduled_delete_time":"2016-04-08T08:13:09Z"
                        },
                    ],
                    "marker":"ip-xxxxxxxx",
                    "is_truncated": true,
                    "max_keys": 1000
                }
        """
        path = utils.append_uri(self._get_path(), 'recycle')
        params = {}
        if eip is not None:
            params[b'eip'] = eip
        if name is not None:
            params[b'name'] = name
        if marker is not None:
            params[b'marker'] = marker
        if max_keys is not None:
            params[b'maxKeys'] = max_keys
        return self._send_request(http_methods.GET, path, params=params, config=config)
    
    def optional_delete_eip(self, eip, release_to_recycle=False, client_token=None, config=None):
        """
        delete an EIP with additional options.

        :type eip: string
        :param eip: the EIP address to delete.

        :type release_to_recycle: bool
        :param release_to_recycle: flag to release EIP to recycle bin.

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """

        path = utils.append_uri(self._get_path(), eip)

        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'clientToken': client_token,
            b'releaseToRecycle': release_to_recycle
        }
        return self._send_request(http_methods.DELETE, path, params=params, config=config)

    def restore_recycle_eip(self, eip, client_token=None, config=None):
        """
        restore an EIP from the recycle bin.

        :type eip: string
        :param eip: eip address to be restored from recycle bin.

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """

        path = utils.append_uri(self._get_path(), 'recycle', eip)

        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'restore': b'',
            b'clientToken': client_token
        }
        return self._send_request(http_methods.PUT, path, params=params, config=config)

    def delete_recycle_eip(self, eip, client_token=None, config=None):
        """
        delete an EIP from the recycle bin.

        :type eip: string
        :param eip: eip address to be delete from recycle bin.

        :type client_token: string
        :param client_token: if the clientToken is not specified by the user,
         a random String generated by default algorithm will be used.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: BceResponse
        """

        path = utils.append_uri(self._get_path(), 'recycle', eip)

        if client_token is None:
            client_token = self._generate_default_client_token()
        params = {
            b'clientToken': client_token
        }
        return self._send_request(http_methods.DELETE, path, params=params, config=config)

    @staticmethod
    def _generate_default_client_token():
        """
        default client token by uuid1
        """
        return uuid.uuid1()

    @staticmethod
    def _get_path(prefix=None):
        """
        :type prefix: string
        :param prefix: path prefix
        """
        if prefix is None:
            prefix = EipClient.prefix
        return utils.append_uri(EipClient.version, prefix)

    def _merge_config(self, config):
        """

        :type config: baidubce.BceClientConfiguration
        :param config:
        :return:
        """
        if config is None:
            return self.config
        else:
            new_config = copy.copy(self.config)
            new_config.merge_non_none_values(config)
            return new_config

    def _send_request(self, http_method, path, body=None, headers=None,
                      params=None, config=None, body_parser=None):
        """

        :param http_method:
        :param path:
        :param body:
        :param headers:
        :param params:

        :type config: baidubce.BceClientConfiguration
        :param config:

        :param body_parser:

        :return: baidubce.BceResponse
        """
        config = self._merge_config(config)
        if body_parser is None:
            body_parser = handler.parse_json

        if headers is None:
            headers = {b'Accept': b'*/*',
                       b'Content-Type': b'application/json;charset=utf-8'}

        return bce_http_client.send_request(config, bce_v1_signer.sign,
                                            [handler.parse_error, body_parser],
                                            http_method, path, body, headers,
                                            params)
