# Copyright 2015 Huawei Technologies Co., Ltd.
# 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.

from six.moves.urllib import parse as urlparse

import requests

from oslo_log import log as logging

from tricircle.common import client
from tricircle.common import constants as cons
from tricircle.db import api as db_api


LOG = logging.getLogger(__name__)


# the url could be endpoint registered in the keystone
# or url sent to tricircle service, which is stored in
# pecan.request.url
def get_version_from_url(url):

    components = urlparse.urlsplit(url)

    path = components.path
    pos = path.find('/')

    ver = ''
    if pos == 0:
        path = path[1:]
        i = path.find('/')
        if i >= 0:
            ver = path[:i]
        else:
            ver = path
    elif pos > 0:
        ver = path[:pos]
    else:
        ver = path

    return ver


def get_bottom_url(t_ver, t_url, b_ver, b_endpoint):
    """get_bottom_url

    convert url received by Tricircle service to bottom OpenStack
    request url through the configured endpoint in the KeyStone

    :param t_ver: version of top service
    :param t_url: request url to the top service
    :param b_ver: version of bottom service
    :param b_endpoint: endpoint registered in keystone for bottom service
    :return: request url to bottom service
    """
    t_parse = urlparse.urlsplit(t_url)

    after_ver = t_parse.path

    remove_ver = '/' + t_ver + '/'
    pos = after_ver.find(remove_ver)

    if pos == 0:
        after_ver = after_ver[len(remove_ver):]
    else:
        remove_ver = t_ver + '/'
        pos = after_ver.find(remove_ver)
        if pos == 0:
            after_ver = after_ver[len(remove_ver):]

    if after_ver == t_parse.path:
        # wrong t_url
        return ''

    b_parse = urlparse.urlsplit(b_endpoint)

    scheme = b_parse.scheme
    netloc = b_parse.netloc
    path = '/' + b_ver + '/' + after_ver
    if b_ver == '':
        path = '/' + after_ver

    # Remove availability_zone filter since it is handled by VolumeController.
    # VolumeController will send GET request only to bottom pods whose AZ
    # is specified in availability_zone filter.
    query_filters = []
    for k, v in urlparse.parse_qsl(t_parse.query):
        if k == 'availability_zone':
            continue
        query_filters.append((k, v))
    query = urlparse.urlencode(query_filters)

    fragment = t_parse.fragment

    b_url = urlparse.urlunsplit((scheme,
                                 netloc,
                                 path,
                                 query,
                                 fragment))
    return b_url


def get_pod_service_endpoint(context, region_name, st):

    pod = db_api.get_pod_by_name(context, region_name)

    if pod:
        c = client.Client()
        return c.get_endpoint(context, pod['pod_id'], st)

    return ''


def get_pod_service_ctx(context, t_url, region_name, s_type=cons.ST_NEUTRON):
    t_ver = get_version_from_url(t_url)
    b_endpoint = get_pod_service_endpoint(context,
                                          region_name,
                                          s_type)
    b_ver = get_version_from_url(b_endpoint)
    b_url = ''
    if b_endpoint != '':
        b_url = get_bottom_url(t_ver, t_url, b_ver, b_endpoint)

    return {'t_ver': t_ver, 'b_ver': b_ver,
            't_url': t_url, 'b_url': b_url}


def forward_req(context, action, b_headers, b_url, b_body):
    s = requests.Session()
    req = requests.Request(action, b_url,
                           data=b_body,
                           headers=b_headers)
    prepped = req.prepare()

    # do something with prepped.body
    # do something with prepped.headers
    resp = s.send(prepped,
                  timeout=60)

    return resp


def get_res_routing_ref(context, _id, t_url, s_type):
    """Get the service context according to resource routing.

    :param _id: the top id of resource
    :param t_url: request url
    :param s_type: service type
    :returns: service context
    """
    pod = db_api.get_pod_by_top_id(context, _id)

    if not pod:
        return None

    region_name = pod['region_name']

    s_ctx = get_pod_service_ctx(context, t_url, region_name,
                                s_type=s_type)

    if s_ctx['b_url'] == '':
        LOG.error("bottom pod endpoint incorrect %s" %
                  region_name)

    return s_ctx


def convert_header(from_release, to_release, header):
    b_header = {}

    # remove invalid header item, requests lib will strictly check
    # header for security purpose, non-string or non-bytes value
    # will lead to exception, and leading space will also be removed
    # by requests.util.check_header_validity function
    for k, v in header.items():
        if v:
            b_header[k] = v

    return b_header


def convert_object(from_release, to_release, res_object,
                   res_type=cons.RT_NETWORK):
    return res_object
