# Copyright (c) 2017-2018 Wind River Systems, Inc.
#
# 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.
import logging
import json
import traceback

from django.core.cache import cache

from keystoneauth1 import access
from keystoneauth1.access import service_catalog
from keystoneauth1.exceptions import HttpError
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from common.exceptions import VimDriverNewtonException
from newton_base.util import VimDriverUtils
from newton_base.proxy.proxy_utils import ProxyUtils

logger = logging.getLogger(__name__)

# DEBUG=True

v3_version_detail = {
    "version": {
        "status": "stable",
        "updated": "2014-04-17T00:00:00Z",
        "media-types": [
            {
                "base": "application/json",
                "type": "application/vnd.openstack.identity-v3+json"
            }
        ],
        "id": "v3",
        "links": [
        ]
    }
}

class Tokens(APIView):
    service = {'service_type': 'identity',
               'interface': 'public'}

    def __init__(self):
        self.proxy_prefix = "multicloud"
        self._logger = logger

    def get(self, request, vimid=""):
        self._logger.info("vimid> %s" % vimid)
        self._logger.debug("META> %s" % request.META)
        self._logger.debug("data> %s" % request.data)

        self._logger.info("RESP with status> %s" % status.HTTP_200_OK)

        # compose the links
        v3_version_detail["version"]["links"] = [{
                "href": request.META.get("REQUEST_URI", ""),
                "rel": "self"
            }]
        return Response(data=v3_version_detail, status=status.HTTP_200_OK)

    def post(self, request, vimid=""):
        self._logger.info("vimid> %s" % vimid)
        self._logger.debug("META> %s" % request.META)
        self._logger.debug("data> %s" % request.data)

        sess = None
        resp = None
        resp_body = None
        try:
            tenant_name = request.data.get("tenant_name")
            tenant_id = request.data.get("tenant_id")

            #backward support for keystone v2.0 API
            if not tenant_name and request.data.get("auth"):
                tenant_name = request.data["auth"].get("tenantName")
                tenant_id = request.data["auth"].get("tenantId")

            #keystone v3 API
            if not tenant_name and request.data.get("auth") \
                    and request.data["auth"].get("scope")\
                    and request.data["auth"]["scope"].get("project"):
                if request.data["auth"]["scope"]["project"].get("name"):
                    tenant_name = request.data["auth"]["scope"]["project"].get("name")
                else:
                    tenant_id = request.data["auth"]["scope"]["project"].get("id")

            # Get the specified tenant id
            specified_project_idorname = request.META.get("Project", None)

            # prepare request resource to vim instance
            vim = VimDriverUtils.get_vim_info(vimid)
            sess = None
            if specified_project_idorname:
                try:
                    # check if specified with tenant id
                    sess = VimDriverUtils.get_session(
                        vim, tenant_name=None,
                        tenant_id=specified_project_idorname
                    )
                except Exception as e:
                    pass

                if not sess:
                    try:
                        # check if specified with tenant name
                        sess = VimDriverUtils.get_session(
                            vim, tenant_name=specified_project_idorname,
                            tenant_id=None
                        )
                    except Exception as e:
                        pass

            if not sess:
                sess = VimDriverUtils.get_session(
                    vim, tenant_name=tenant_name, tenant_id=tenant_id)

            #tmp_auth_state = VimDriverUtils.get_auth_state(vim, sess)
            tmp_auth_state = VimDriverUtils.get_auth_state(sess)
            tmp_auth_info = json.loads(tmp_auth_state)
            tmp_auth_token = tmp_auth_info['auth_token']
            tmp_auth_data = tmp_auth_info['body']

            #store the auth_state, redis/memcached
            #set expiring in 1 hour

            #update the catalog
            tmp_auth_data['token']['catalog'], tmp_metadata_catalog = ProxyUtils.update_catalog(
                vimid, tmp_auth_data['token']['catalog'], self.proxy_prefix)

            VimDriverUtils.update_token_cache(
                tmp_auth_token, tmp_auth_state, json.dumps(tmp_metadata_catalog))

            tmp_auth_data['token']['catalog'] = ProxyUtils.update_catalog_dnsaas(
                vimid,tmp_auth_data['token']['catalog'], self.proxy_prefix, vim)

            resp = Response(headers={'X-Subject-Token': tmp_auth_token},
                            data=tmp_auth_data, status=status.HTTP_201_CREATED)

            self._logger.info("RESP with status> %s" % status.HTTP_201_CREATED)

            return resp
        except VimDriverNewtonException as e:

            self._logger.error("Plugin exception> status:%s,error:%s"
                                  % (e.status_code, e.content))
            return Response(data={'error': e.content}, status=e.status_code)
        except HttpError as e:
            self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json()))
            return Response(data=e.response.json(), status=e.http_status)
        except Exception as e:
            self._logger.error(traceback.format_exc())

            return Response(data={'error': str(e)},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

v2_version_detail = {
    "version": {
        "status": "deprecated",
        "updated": "2016-08-04T00:00:00Z",
        "media-types": [
            {
                "base": "application/json",
                "type": "application/vnd.openstack.identity-v2.0+json"
            }
        ],
        "id": "v2.0",
        "links": [
        ],
        "type": "text/html",
        "rel": "describedby"

    }
}

class TokensV2(Tokens):
    '''
    Backward compatible API for /v2.0/tokens
    '''

    def __init__(self):
        self.proxy_prefix = "multicloud"
        self._logger = logger

    def get(self, request, vimid=""):
        self._logger.info("vimid> %s" % vimid)
        self._logger.debug("TokensV2--get::META> %s" % request.META)

        self._logger.info("RESP with status> %s" % status.HTTP_200_OK)
        # compose the links
        v2_version_detail["version"]["links"] = [{
                "href": request.META.get("REQUEST_URI", ""),
                "rel": "self"
            }]
        return Response(data=v2_version_detail, status=status.HTTP_200_OK)

    def post(self, request, vimid=""):
        self._logger.info("vimid > %s" % vimid)
        self._logger.debug("META> %s" % request.META)
        self._logger.debug("data> %s" % request.data)


        try:
            resp = super(TokensV2,self).post(request, vimid)
            self._logger.debug("Token(v3)returns> headers:%s, data:%s" % (resp._headers, resp.data))
            if resp.status_code == status.HTTP_201_CREATED:
                v3_content =  resp.data
                v3_token = v3_content['token']

                #convert catalog
                v2_catalog = []
                for v3_catalog in v3_token['catalog']:
                    v2_catalog1 = {
                        "type": v3_catalog["type"],
                        "name": v3_catalog["name"],
                        "endpoints": []
                    }

                    #convert endpoints
                    v2_catalog1_endpoints = None
                    for v3_endpoint in v3_catalog['endpoints']:
                        v2_catalog1_endpoints = {
                            "id": v3_endpoint['id'],
                            "region":v3_endpoint['region'],
                            "region_name": v3_endpoint['region_id'],
                            'interface':v3_endpoint['interface']
                        }
                        if v3_endpoint['interface'] == 'public':
                            v2_catalog1_endpoints['publicURL'] = v3_endpoint['url']
                        elif v3_endpoint['interface'] == 'admin':
                            v2_catalog1_endpoints['adminURL'] = v3_endpoint['url']
                        elif v3_endpoint['interface'] == 'internal':
                            v2_catalog1_endpoints['internalURL'] = v3_endpoint['url']

                        if v2_catalog1_endpoints:
                            v2_catalog1['endpoints'].append(v2_catalog1_endpoints)

                    v2_catalog.append(v2_catalog1)


                #conversion between v3 tokens response and v2.0 tokens response
                v3_token["project"]['enabled'] = 'true'
                v2_content = {
                    "access": {
                        "token": {
                            "id" : resp.get('X-Subject-Token', None),
                            "issued_at": v3_token["issued_at"],
                            "expires" : v3_token["expires_at"],
                            "tenant" : v3_token["project"],
                        },
                        "serviceCatalog": v2_catalog,
                        "user": v3_token["user"],
                    }
                }

                self._logger.info("RESP with status> %s" % status.HTTP_200_OK)
                return Response(data=v2_content,
                                status=status.HTTP_200_OK \
                                    if resp.status_code==status.HTTP_201_CREATED \
                                    else resp.status_code)

            else:
                self._logger.info("RESP with status> %s" % resp.status_code)
                return resp
        except VimDriverNewtonException as e:

            self._logger.error("Plugin exception> status:%s,error:%s"
                                  % (e.status_code, e.content))
            return Response(data={'error': e.content}, status=e.status_code)
        except HttpError as e:
            self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json()))
            return Response(data=e.response.json(), status=e.http_status)
        except Exception as e:
            self._logger.error(traceback.format_exc())

            return Response(data={'error': str(e)},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)