# 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.

"""
Cloudwatt driver.
"""
try:
    import simplejson as json
except ImportError:
    import json

from libcloud.utils.py3 import httplib
from libcloud.compute.types import Provider, InvalidCredsError, MalformedResponseError
from libcloud.utils.iso8601 import parse_date
from libcloud.common.openstack_identity import OpenStackIdentityConnection
from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection, OpenStack_1_1_NodeDriver

__all__ = ["CloudwattNodeDriver"]

BASE_URL = "https://identity.fr1.cloudwatt.com/v2.0"
AUTH_URL = BASE_URL + "/tokens"


class CloudwattAuthConnection(OpenStackIdentityConnection):
    """
    AuthConnection class for the Cloudwatt driver.
    """

    name = "Cloudwatt Auth"

    def __init__(self, *args, **kwargs):
        self._ex_tenant_id = kwargs.pop("ex_tenant_id")
        super().__init__(*args, **kwargs)

    def morph_action_hook(self, action):
        (_, _, _, request_path) = self._tuple_from_url(self.auth_url)

        if request_path == "":
            # No path is provided in the auth_url, use action passed to this
            # method.
            return action

        return request_path

    def authenticate(self, force=False):
        reqbody = json.dumps(
            {
                "auth": {
                    "passwordCredentials": {
                        "username": self.user_id,
                        "password": self.key,
                    },
                    "tenantId": self._ex_tenant_id,
                }
            }
        )
        resp = self.request("/tokens", data=reqbody, headers={}, method="POST")

        if resp.status == httplib.UNAUTHORIZED:
            # HTTP UNAUTHORIZED (401): auth failed
            raise InvalidCredsError()
        elif resp.status != httplib.OK:
            body = "code: {} body:{}".format(resp.status, resp.body)
            raise MalformedResponseError("Malformed response", body=body, driver=self.driver)
        else:
            try:
                body = json.loads(resp.body)
            except Exception as e:
                raise MalformedResponseError("Failed to parse JSON", e)

            try:
                expires = body["access"]["token"]["expires"]

                self.auth_token = body["access"]["token"]["id"]
                self.auth_token_expires = parse_date(expires)
                self.urls = body["access"]["serviceCatalog"]
                self.auth_user_info = None
            except KeyError as e:
                raise MalformedResponseError(
                    "Auth JSON response is \
                                             missing required elements",
                    e,
                )

        return self


class CloudwattConnection(OpenStack_1_1_Connection):
    """
    Connection class for the Cloudwatt driver.
    """

    auth_url = BASE_URL
    service_region = "fr1"
    service_type = "compute"

    def __init__(self, *args, **kwargs):
        self.ex_tenant_id = kwargs.pop("ex_tenant_id")
        super().__init__(*args, **kwargs)
        osa = CloudwattAuthConnection(
            auth_url=AUTH_URL,
            user_id=self.user_id,
            key=self.key,
            tenant_name=self._ex_tenant_name,
            timeout=self.timeout,
            ex_tenant_id=self.ex_tenant_id,
            parent_conn=self,
        )
        self._osa = osa
        self._auth_version = "2.0"


class CloudwattNodeDriver(OpenStack_1_1_NodeDriver):
    """
    Implements the :class:`NodeDriver`'s for Cloudwatt.
    """

    name = "Cloudwatt"
    website = "https://www.cloudwatt.com/"
    connectionCls = CloudwattConnection
    type = Provider.CLOUDWATT

    def __init__(
        self,
        key,
        secret,
        tenant_id,
        secure=True,
        tenant_name=None,
        host=None,
        port=None,
        **kwargs,
    ):
        """
        @inherits:  :class:`NodeDriver.__init__`

        :param tenant_id: ID of tenant required for Cloudwatt auth
        :type tenant_id: ``str``
        """
        self.ex_tenant_id = tenant_id
        self.extra = {}
        super().__init__(key=key, secret=secret, secure=secure, host=host, port=port, **kwargs)

    def attach_volume(self, node, volume, device=None):
        return super().attach_volume(node, volume, device)

    def _ex_connection_class_kwargs(self):
        """
        Includes ``tenant_id`` in Connection.
        """
        return {"ex_tenant_id": self.ex_tenant_id}
