#    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 re
import sys

import fixtures
from keystoneauth1 import fixture
from keystoneauth1.fixture import v2 as ks_v2_fixture
import mock
from oslo_log import handlers
from oslo_log import log
import six
from testtools import matchers

from karborclient.common.apiclient import exceptions
import karborclient.shell
from karborclient.tests.unit import base


FAKE_ENV = {'OS_USERNAME': 'username',
            'OS_PASSWORD': 'password',
            'OS_TENANT_NAME': 'tenant_name',
            'OS_AUTH_URL': 'http://no.where/v2.0'}

FAKE_ENV2 = {'OS_USERNAME': 'username',
             'OS_PASSWORD': 'password',
             'OS_TENANT_ID': 'tenant_id',
             'OS_AUTH_URL': 'http://no.where/v2.0'}

FAKE_ENV_v3 = {'OS_USERNAME': 'username',
               'OS_PASSWORD': 'password',
               'OS_TENANT_ID': 'tenant_id',
               'OS_USER_DOMAIN_NAME': 'domain_name',
               'OS_AUTH_URL': 'http://no.where/v3'}


def _create_ver_list(versions):
    return {'versions': {'values': versions}}


class TestArgs(object):
    package_version = ''
    karbor_repo_url = 'http://127.0.0.1'
    exists_action = ''
    is_public = False
    categories = []


class ShellTest(base.TestCaseShell):

    def make_env(self, exclude=None, fake_env=FAKE_ENV):
        env = dict((k, v) for k, v in fake_env.items() if k != exclude)
        self.useFixture(fixtures.MonkeyPatch('os.environ', env))


class ShellCommandTest(ShellTest):

    _msg_no_tenant_project = ('You must provide a project name or project'
                              ' id via --os-project-name, --os-project-id,'
                              ' env[OS_PROJECT_ID] or env[OS_PROJECT_NAME].'
                              ' You may use os-project and os-tenant'
                              ' interchangeably.',)

    def setUp(self):
        super(ShellCommandTest, self).setUp()

        def get_auth_endpoint(bound_self, args):
            return ('test', {})
        self.useFixture(fixtures.MonkeyPatch(
            'karborclient.shell.KarborShell._get_endpoint_and_kwargs',
            get_auth_endpoint))
        self.client = mock.MagicMock()

        # To prevent log descriptors from being closed during
        # shell tests set a custom StreamHandler
        self.logger = log.getLogger(None).logger
        self.logger.level = logging.DEBUG
        self.color_handler = handlers.ColorHandler(sys.stdout)
        self.logger.addHandler(self.color_handler)

    def tearDown(self):
        super(ShellTest, self).tearDown()
        self.logger.removeHandler(self.color_handler)

    def shell(self, argstr, exitcodes=(0,)):
        orig = sys.stdout
        orig_stderr = sys.stderr
        try:
            sys.stdout = six.StringIO()
            sys.stderr = six.StringIO()
            _shell = karborclient.shell.KarborShell()
            _shell.main(argstr.split())
        except SystemExit:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            self.assertIn(exc_value.code, exitcodes)
        finally:
            stdout = sys.stdout.getvalue()
            sys.stdout.close()
            sys.stdout = orig
            stderr = sys.stderr.getvalue()
            sys.stderr.close()
            sys.stderr = orig_stderr
        return (stdout, stderr)

    def register_keystone_discovery_fixture(self, mreq):
        v2_url = "http://no.where/v2.0"
        v2_version = fixture.V2Discovery(v2_url)
        mreq.register_uri('GET', v2_url, json=_create_ver_list([v2_version]),
                          status_code=200)

    def register_keystone_token_fixture(self, mreq):
        v2_token = ks_v2_fixture.Token(token_id='token')
        service = v2_token.add_service('application-catalog')
        service.add_endpoint('http://no.where', region='RegionOne')
        mreq.register_uri('POST',
                          'http://no.where/v2.0/tokens',
                          json=v2_token,
                          status_code=200)

    def test_help_unknown_command(self):
        self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')

    def test_help(self):
        required = [
            r'.*?^usage: karbor',
            r'.*?^\s+plan-create\s+Creates a plan.',
            r'.*?^See "karbor help COMMAND" for help on a specific command',
        ]
        stdout, stderr = self.shell('help')
        for r in required:
            self.assertThat((stdout + stderr),
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_help_on_subcommand(self):
        required = [
            r'.*?^usage: karbor plan-create',
            r'.*?^Creates a plan.',
        ]
        stdout, stderr = self.shell('help plan-create')
        for r in required:
            self.assertThat((stdout + stderr),
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_help_no_options(self):
        required = [
            r'.*?^usage: karbor',
            r'.*?^\s+plan-create\s+Creates a plan',
            r'.*?^See "karbor help COMMAND" for help on a specific command',
        ]
        stdout, stderr = self.shell('')
        for r in required:
            self.assertThat((stdout + stderr),
                            matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))

    def test_no_username(self):
        required = ('You must provide a username via either --os-username or '
                    'env[OS_USERNAME] or a token via --os-auth-token or '
                    'env[OS_AUTH_TOKEN]',)
        self.make_env(exclude='OS_USERNAME')
        try:
            self.shell('plan-list')
        except exceptions.CommandError as message:
            self.assertEqual(required, message.args)
        else:
            self.fail('CommandError not raised')

    def test_no_tenant_name(self):
        required = self._msg_no_tenant_project
        self.make_env(exclude='OS_TENANT_NAME')
        try:
            self.shell('plan-list')
        except exceptions.CommandError as message:
            self.assertEqual(required, message.args)
        else:
            self.fail('CommandError not raised')

    def test_no_tenant_id(self):
        required = self._msg_no_tenant_project
        self.make_env(exclude='OS_TENANT_ID', fake_env=FAKE_ENV2)
        try:
            self.shell('plan-list')
        except exceptions.CommandError as message:
            self.assertEqual(required, message.args)
        else:
            self.fail('CommandError not raised')

    def test_no_auth_url(self):
        required = ('You must provide an auth url'
                    ' via either --os-auth-url or via env[OS_AUTH_URL]',)
        self.make_env(exclude='OS_AUTH_URL')
        try:
            self.shell('plan-list')
        except exceptions.CommandError as message:
            self.assertEqual(required, message.args)
        else:
            self.fail('CommandError not raised')
