# Copyright 2013 OpenStack Foundation
# Copyright 2013 Rackspace Hosting
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# 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 datetime import datetime

from nose.tools import assert_equal
from nose.tools import assert_true
from oslo_utils import timeutils
from proboscis import before_class
from proboscis import test
from troveclient.compat import exceptions

from trove.common import cfg
from trove.tests.fakes import limits as fake_limits
from trove.tests.util import create_dbaas_client
from trove.tests.util.users import Users


CONF = cfg.CONF

GROUP = "dbaas.api.limits"
DEFAULT_RATE = CONF.http_get_rate

DEFAULT_MAX_VOLUMES = CONF.max_volumes_per_tenant
DEFAULT_MAX_INSTANCES = CONF.max_instances_per_tenant
DEFAULT_MAX_BACKUPS = CONF.max_backups_per_tenant
DEFAULT_MAX_RAM = CONF.max_ram_per_tenant


def ensure_limits_are_not_faked(func):
    def _cd(*args, **kwargs):
        fake_limits.ENABLED = True
        try:
            return func(*args, **kwargs)
        finally:
            fake_limits.ENABLED = False


@test(groups=[GROUP])
class Limits(object):

    @before_class
    def setUp(self):

        users = [
            {
                "auth_user": "rate_limit",
                "auth_key": "password",
                "tenant": "4000",
                "requirements": {
                    "is_admin": False,
                    "services": ["trove"]
                }
            },
            {
                "auth_user": "rate_limit_exceeded",
                "auth_key": "password",
                "tenant": "4050",
                "requirements": {
                    "is_admin": False,
                    "services": ["trove"]
                }
            }]

        self._users = Users(users)

        rate_user = self._get_user('rate_limit')
        self.rd_client = create_dbaas_client(rate_user)

    def _get_user(self, name):
        return self._users.find_user_by_name(name)

    def __is_available(self, next_available):
        dt_next = timeutils.parse_isotime(next_available)
        dt_now = datetime.now()
        return dt_next.time() < dt_now.time()

    def _get_limits_as_dict(self, limits):
        d = {}
        for limit in limits:
            d[l.verb] = limit
        return d

    @test
    @ensure_limits_are_not_faked
    def test_limits_index(self):
        """Test_limits_index."""

        limits = self.rd_client.limits.list()
        d = self._get_limits_as_dict(limits)

        # remove the abs_limits from the rate limits
        abs_limits = d.pop("ABSOLUTE", None)
        assert_equal(abs_limits.verb, "ABSOLUTE")
        assert_equal(int(abs_limits.max_instances), DEFAULT_MAX_INSTANCES)
        assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS)
        assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES)
        assert_equal(int(abs_limits.max_ram), DEFAULT_MAX_RAM)

        for k in d:
            assert_equal(d[k].verb, k)
            assert_equal(d[k].unit, "MINUTE")
            assert_true(int(d[k].remaining) <= DEFAULT_RATE)
            assert_true(d[k].nextAvailable is not None)

    @test
    @ensure_limits_are_not_faked
    def test_limits_get_remaining(self):
        """Test_limits_get_remaining."""

        limits = ()
        for i in range(5):
            limits = self.rd_client.limits.list()

        d = self._get_limits_as_dict(limits)
        abs_limits = d["ABSOLUTE"]
        get = d["GET"]

        assert_equal(int(abs_limits.max_instances), DEFAULT_MAX_INSTANCES)
        assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS)
        assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES)
        assert_equal(int(abs_limits.max_ram), DEFAULT_MAX_RAM)
        assert_equal(get.verb, "GET")
        assert_equal(get.unit, "MINUTE")
        assert_true(int(get.remaining) <= DEFAULT_RATE - 5)
        assert_true(get.nextAvailable is not None)

    @test
    @ensure_limits_are_not_faked
    def test_limits_exception(self):
        """Test_limits_exception."""

        # use a different user to avoid throttling tests run out of order
        rate_user_exceeded = self._get_user('rate_limit_exceeded')
        rd_client = create_dbaas_client(rate_user_exceeded)

        get = None
        encountered = False
        for i in range(DEFAULT_RATE + 50):
            try:
                limits = rd_client.limits.list()
                d = self._get_limits_as_dict(limits)
                get = d["GET"]
                abs_limits = d["ABSOLUTE"]

                assert_equal(get.verb, "GET")
                assert_equal(get.unit, "MINUTE")
                assert_equal(int(abs_limits.max_instances),
                             DEFAULT_MAX_INSTANCES)
                assert_equal(int(abs_limits.max_backups),
                             DEFAULT_MAX_BACKUPS)
                assert_equal(int(abs_limits.max_volumes),
                             DEFAULT_MAX_VOLUMES)
                assert_equal(int(abs_limits.max_ram,),
                             DEFAULT_MAX_RAM)

            except exceptions.OverLimit:
                encountered = True

        assert_true(encountered)
        assert_true(int(get.remaining) <= 50)
