# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# 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 copy
import os

from unittest import mock

import pecan

from oslo_config import cfg
from oslo_log import log
from oslo_messaging import conffixture
from pecan import testing

from watcher.common import context as watcher_context
from watcher.common import service
from watcher.objects import base as objects_base
from watcher.tests import base as watcher_base
from watcher.tests.local_fixtures import conf_fixture
from watcher.tests.local_fixtures import policy_fixture


CONF = cfg.CONF
try:
    log.register_options(CONF)
except cfg.ArgsAlreadyParsedError:
    pass
CONF.set_override('use_stderr', False)


class BaseTestCase(watcher_base.WatcherBaseTestCase):
    """Test base class."""


class TestCase(BaseTestCase):
    """Test case base class for all unit tests."""

    def setUp(self):
        super().setUp()
        self.useFixture(conf_fixture.ConfReloadFixture())
        self.policy = self.useFixture(policy_fixture.PolicyFixture())
        self.messaging_conf = self.useFixture(conffixture.ConfFixture(CONF))
        self.messaging_conf.transport_url = 'fake:/'

        cfg.CONF.set_override(
            "auth_type", "admin_token", group='keystone_authtoken'
        )

        app_config_path = os.path.join(os.path.dirname(__file__), 'config.py')
        self.app = testing.load_test_app(app_config_path)
        self.token_info = {
            'token': {
                'project': {'id': 'fake_project'},
                'user': {'id': 'fake_user'},
            }
        }

        objects_base.WatcherObject.indirection_api = None

        self.context = self._create_context()

        self.policy = self.useFixture(policy_fixture.PolicyFixture())

        p_context = mock.patch.object(
            watcher_context, 'make_context', side_effect=self._create_context
        )
        p_context.start()
        self.addCleanup(p_context.stop)

        p_env = mock.patch.object(
            watcher_context.RequestContext,
            'from_environ',
            side_effect=self._create_context,
        )
        p_env.start()
        self.addCleanup(p_env.stop)

        self.useFixture(conf_fixture.ConfFixture(cfg.CONF))
        self._reset_singletons()

        self._base_test_obj_backup = copy.copy(
            objects_base.WatcherObjectRegistry._registry._obj_classes
        )
        self.addCleanup(self._restore_obj_registry)
        self.addCleanup(self._reset_singletons)

    def _create_context(self, *args, **kwargs):
        return watcher_context.RequestContext(
            *args,
            project_id='fake_project',
            project_domain_id='fake_proj_domain_id',
            user_id='fake_user',
            user_domain_id='fake_user_domain_id',
            roles=['member'],
            **kwargs,
        )

    def _reset_singletons(self):
        service.Singleton._instances.clear()

        def reset_pecan():
            pecan.set_config({}, overwrite=True)

        self.addCleanup(reset_pecan)

    def _restore_obj_registry(self):
        objects_base.WatcherObjectRegistry._registry._obj_classes = (
            self._base_test_obj_backup
        )

    def config(self, **kw):
        """Override config options for a test."""
        group = kw.pop('group', None)
        for k, v in kw.items():
            CONF.set_override(k, v, group)

    def get_path(self, project_file=None):
        """Get the absolute path to a file. Used for testing the API.

        :param project_file: File whose path to return. Default: None.
        :returns: path to the specified file, or path to project root.
        """
        root = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..', '..')
        )
        if project_file:
            return os.path.join(root, project_file)
        else:
            return root
