# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.

try:
    import simplejson as json
except ImportError:
    import json

import time
from libcloud.utils.py3 import urlparse

from libcloud.common.base import (ConnectionUserAndKey,
                                  JsonResponse,
                                  RawResponse)
from libcloud.http import LibcloudConnection
from libcloud.utils.py3 import basestring, urlencode


class AzureBaseDriver(object):
    name = "Microsoft Azure Resource Management API"


class AzureJsonResponse(JsonResponse):
    def parse_error(self):
        b = self.parse_body()

        if isinstance(b, basestring):
            return b
        elif isinstance(b, dict) and "error" in b:
            return "[%s] %s" % (b["error"].get("code"),
                                b["error"].get("message"))
        else:
            return str(b)


class AzureAuthJsonResponse(JsonResponse):
    def parse_error(self):
        b = self.parse_body()

        if isinstance(b, basestring):
            return b
        elif isinstance(b, dict) and "error_description" in b:
            return b["error_description"]
        else:
            return str(b)


# Based on
# https://github.com/Azure/azure-xplat-cli/blob/master/lib/util/profile/environment.js
publicEnvironments = {
    "default": {
        'name': 'default',
        'portalUrl': 'http://go.microsoft.com/fwlink/?LinkId=254433',
        'publishingProfileUrl':
            'http://go.microsoft.com/fwlink/?LinkId=254432',
        'managementEndpointUrl': 'https://management.core.windows.net',
        'resourceManagerEndpointUrl':
            'https://management.azure.com/',
        'sqlManagementEndpointUrl':
            'https://management.core.windows.net:8443/',
        'sqlServerHostnameSuffix': '.database.windows.net',
        'galleryEndpointUrl': 'https://gallery.azure.com/',
        'activeDirectoryEndpointUrl': 'https://login.microsoftonline.com',
        'activeDirectoryResourceId': 'https://management.core.windows.net/',
        'activeDirectoryGraphResourceId': 'https://graph.windows.net/',
        'activeDirectoryGraphApiVersion': '2013-04-05',
        'storageEndpointSuffix': '.core.windows.net',
        'keyVaultDnsSuffix': '.vault.azure.net',
        'azureDataLakeStoreFileSystemEndpointSuffix': 'azuredatalakestore.net',
        'azureDataLakeAnalyticsCatalogAndJobEndpointSuffix':
            'azuredatalakeanalytics.net'
    },
    "AzureChinaCloud": {
        'name': 'AzureChinaCloud',
        'portalUrl': 'http://go.microsoft.com/fwlink/?LinkId=301902',
        'publishingProfileUrl':
            'http://go.microsoft.com/fwlink/?LinkID=301774',
        'managementEndpointUrl': 'https://management.core.chinacloudapi.cn',
        'resourceManagerEndpointUrl': 'https://management.chinacloudapi.cn',
        'sqlManagementEndpointUrl':
            'https://management.core.chinacloudapi.cn:8443/',
        'sqlServerHostnameSuffix': '.database.chinacloudapi.cn',
        'galleryEndpointUrl': 'https://gallery.chinacloudapi.cn/',
        'activeDirectoryEndpointUrl': 'https://login.chinacloudapi.cn',
        'activeDirectoryResourceId':
            'https://management.core.chinacloudapi.cn/',
        'activeDirectoryGraphResourceId': 'https://graph.chinacloudapi.cn/',
        'activeDirectoryGraphApiVersion': '2013-04-05',
        'storageEndpointSuffix': '.core.chinacloudapi.cn',
        'keyVaultDnsSuffix': '.vault.azure.cn',
        'azureDataLakeStoreFileSystemEndpointSuffix': 'N/A',
        'azureDataLakeAnalyticsCatalogAndJobEndpointSuffix': 'N/A'
    },
    "AzureUSGovernment": {
        'name': 'AzureUSGovernment',
        'portalUrl': 'https://manage.windowsazure.us',
        'publishingProfileUrl':
            'https://manage.windowsazure.us/publishsettings/index',
        'managementEndpointUrl': 'https://management.core.usgovcloudapi.net',
        'resourceManagerEndpointUrl': 'https://management.usgovcloudapi.net',
        'sqlManagementEndpointUrl':
            'https://management.core.usgovcloudapi.net:8443/',
        'sqlServerHostnameSuffix': '.database.usgovcloudapi.net',
        'galleryEndpointUrl': 'https://gallery.usgovcloudapi.net/',
        'activeDirectoryEndpointUrl': 'https://login-us.microsoftonline.com',
        'activeDirectoryResourceId':
            'https://management.core.usgovcloudapi.net/',
        'activeDirectoryGraphResourceId': 'https://graph.windows.net/',
        'activeDirectoryGraphApiVersion': '2013-04-05',
        'storageEndpointSuffix': '.core.usgovcloudapi.net',
        'keyVaultDnsSuffix': '.vault.usgovcloudapi.net',
        'azureDataLakeStoreFileSystemEndpointSuffix': 'N/A',
        'azureDataLakeAnalyticsCatalogAndJobEndpointSuffix': 'N/A'
    },
    "AzureGermanCloud": {
        'name': 'AzureGermanCloud',
        'portalUrl': 'http://portal.microsoftazure.de/',
        'publishingProfileUrl':
        'https://manage.microsoftazure.de/publishsettings/index',
        'managementEndpointUrl': 'https://management.core.cloudapi.de',
        'resourceManagerEndpointUrl': 'https://management.microsoftazure.de',
        'sqlManagementEndpointUrl':
            'https://management.core.cloudapi.de:8443/',
        'sqlServerHostnameSuffix': '.database.cloudapi.de',
        'galleryEndpointUrl': 'https://gallery.cloudapi.de/',
        'activeDirectoryEndpointUrl': 'https://login.microsoftonline.de',
        'activeDirectoryResourceId': 'https://management.core.cloudapi.de/',
        'activeDirectoryGraphResourceId': 'https://graph.cloudapi.de/',
        'activeDirectoryGraphApiVersion': '2013-04-05',
        'storageEndpointSuffix': '.core.cloudapi.de',
        'keyVaultDnsSuffix': '.vault.microsoftazure.de',
        'azureDataLakeStoreFileSystemEndpointSuffix': 'N/A',
        'azureDataLakeAnalyticsCatalogAndJobEndpointSuffix': 'N/A'
    }
}


class AzureResourceManagementConnection(ConnectionUserAndKey):
    """
    Represents a single connection to Azure
    """

    conn_class = LibcloudConnection
    driver = AzureBaseDriver
    name = 'Azure AD Auth'
    responseCls = AzureJsonResponse
    rawResponseCls = RawResponse

    def __init__(self, key, secret, secure=True, tenant_id=None,
                 subscription_id=None, cloud_environment=None, **kwargs):
        super(AzureResourceManagementConnection, self) \
            .__init__(key, secret, **kwargs)
        if not cloud_environment:
            cloud_environment = "default"
        if isinstance(cloud_environment, basestring):
            cloud_environment = publicEnvironments[cloud_environment]
        if not isinstance(cloud_environment, dict):
            raise Exception("cloud_environment must be one of '%s' or a dict "
                            "containing keys 'resourceManagerEndpointUrl', "
                            "'activeDirectoryEndpointUrl', "
                            "'activeDirectoryResourceId', "
                            "'storageEndpointSuffix'" % (
                                "', '".join(publicEnvironments.keys())))
        self.host = urlparse.urlparse(
            cloud_environment['resourceManagerEndpointUrl']).hostname
        self.login_host = urlparse.urlparse(
            cloud_environment['activeDirectoryEndpointUrl']).hostname
        self.login_resource = cloud_environment['activeDirectoryResourceId']
        self.storage_suffix = cloud_environment['storageEndpointSuffix']
        self.tenant_id = tenant_id
        self.subscription_id = subscription_id

    def add_default_headers(self, headers):
        headers['Content-Type'] = "application/json"
        headers['Authorization'] = "Bearer %s" % self.access_token
        return headers

    def encode_data(self, data):
        """Encode data to JSON"""
        return json.dumps(data)

    def get_token_from_credentials(self):
        """
        Log in and get bearer token used to authorize API requests.
        """

        conn = self.conn_class(self.login_host, 443, timeout=self.timeout)
        conn.connect()
        params = urlencode({
            "grant_type": "client_credentials",
            "client_id": self.user_id,
            "client_secret": self.key,
            "resource": self.login_resource
        })
        headers = {"Content-type": "application/x-www-form-urlencoded"}
        conn.request("POST", "/%s/oauth2/token" % self.tenant_id,
                     params, headers)
        js = AzureAuthJsonResponse(conn.getresponse(), conn)
        self.access_token = js.object["access_token"]
        self.expires_on = js.object["expires_on"]

    def connect(self, **kwargs):
        self.get_token_from_credentials()
        return super(AzureResourceManagementConnection, self).connect(**kwargs)

    def request(self, action, params=None, data=None, headers=None,
                method='GET', raw=False):

        # Log in again if the token has expired or is going to expire soon
        # (next 5 minutes).
        if (time.time() + 300) >= int(self.expires_on):
            self.get_token_from_credentials()

        return super(AzureResourceManagementConnection, self) \
            .request(action, params=params,
                     data=data, headers=headers,
                     method=method, raw=raw)
