from __future__ import print_function, division, absolute_import

#
# Copyright (c) 2010 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#
import os

from mock import Mock, NonCallableMock, patch, MagicMock

from .stubs import StubUEP

from subscription_manager.managercli import RegisterCommand
from subscription_manager import injection as inj
from subscription_manager import cache
from subscription_manager.identity import ConsumerIdentity

from .fixture import SubManFixture, Capture, set_up_mock_sp_store

from rhsmlib.services.register import RegisterService
from rhsmlib.services import exceptions


class CliRegistrationTests(SubManFixture):
    def setUp(self):
        super(CliRegistrationTests, self).setUp()
        register_patcher = patch('subscription_manager.managercli.register.RegisterService',
            spec=RegisterService)
        self.mock_register = register_patcher.start().return_value
        self.mock_register.register.return_value = MagicMock(name="MockConsumer")
        self.addCleanup(register_patcher.stop)

        get_supported_resources_patcher = patch('subscription_manager.managercli.get_supported_resources')
        self.mock_get_resources = get_supported_resources_patcher.start()
        self.mock_get_resources.return_value = ['environments']
        self.addCleanup(self.mock_get_resources.stop)

        identity_patcher = patch('subscription_manager.managercli.identity.ConsumerIdentity',
            spec=ConsumerIdentity)
        self.mock_consumer_identity = identity_patcher.start().return_value
        self.addCleanup(identity_patcher.stop)

        from subscription_manager import syspurposelib

        self.syspurposelib = syspurposelib
        self.syspurposelib.USER_SYSPURPOSE = self.write_tempfile("{}").name

        syspurpose_patch = patch('subscription_manager.syspurposelib.SyncedStore')
        self.mock_sp_store = syspurpose_patch.start()
        self.mock_sp_store, self.mock_sp_store_contents = set_up_mock_sp_store(self.mock_sp_store)
        self.addCleanup(syspurpose_patch.stop)

        _syspurpose_patch = patch('subscription_manager.managerlib.SyncedStore')
        self._mock_sp_store = _syspurpose_patch.start()
        self._mock_sp_store, self.mock_sp_store_contents = set_up_mock_sp_store(self._mock_sp_store)
        self.addCleanup(_syspurpose_patch.stop)

        clean_all_data_patch = patch('subscription_manager.managerlib.clean_all_data')
        self.mock_clean_all_data = clean_all_data_patch.start()
        self.addCleanup(clean_all_data_patch.stop)

    def _inject_ipm(self):
        mock_ipm = NonCallableMock(spec=cache.InstalledProductsManager)
        mock_ipm.tags = None
        inj.provide(inj.INSTALLED_PRODUCTS_MANAGER, mock_ipm)
        return mock_ipm

    def test_registration(self):
        """
        Test normal registration (test of proper patching)
        """
        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            self.stub_cp_provider.basic_auth_cp = mock_uep
            cmd = RegisterCommand()
            cmd.main(['--force', '--username', 'admin', '--password', 'admin', '--org', 'admin'])

    @patch('subscription_manager.managercli.EntCertActionInvoker')
    def test_activation_keys_updates_certs_and_repos(self, mock_entcertlib):
        self.stub_cp_provider.basic_auth_cp = Mock('rhsm.connection.UEPConnection', new_callable=StubUEP)
        self._inject_mock_invalid_consumer()

        cmd = RegisterCommand()
        mock_entcertlib = mock_entcertlib.return_value
        self._inject_ipm()

        cmd.main(['--activationkey=test_key', '--org=test_org'])
        self.mock_register.register.assert_called_once()
        mock_entcertlib.update.assert_called_once()

    @patch('subscription_manager.managercli.EntCertActionInvoker')
    def test_consumerid_updates_certs_and_repos(self, mock_entcertlib):
        self.stub_cp_provider.basic_auth_cp = Mock('rhsm.connection.UEPConnection', new_callable=StubUEP)
        self._inject_mock_invalid_consumer()

        cmd = RegisterCommand()
        mock_entcertlib = mock_entcertlib.return_value
        self._inject_ipm()

        cmd.main(['--consumerid=123456', '--username=testuser1', '--password=password', '--org=test_org'])
        self.mock_register.register.assert_called_once_with(None, consumerid='123456')
        mock_entcertlib.update.assert_called_once()

    def test_consumerid_with_distributor_id(self):
        self.stub_cp_provider.basic_auth_cp = Mock('rhsm.connection.UEPConnection', new_callable=StubUEP)

        self._inject_mock_invalid_consumer()
        cmd = RegisterCommand()
        self._inject_ipm()
        self.mock_register.register.side_effect = exceptions.ServiceError()

        with Capture(silent=True):
            with self.assertRaises(SystemExit) as e:
                cmd.main(['--consumerid=TaylorSwift', '--username=testuser1', '--password=password', '--org=test_org'])
                self.assertEqual(e.code, os.EX_USAGE)

    def test_strip_username_and_password(self):
        username, password = RegisterCommand._get_username_and_password(" ", " ")
        self.assertEqual(username, "")
        self.assertEqual(password, "")

        username, password = RegisterCommand._get_username_and_password(" Jar Jar ", " Binks ")
        self.assertEqual(username, "Jar Jar")
        self.assertEqual(password, "Binks")

    def test_get_environment_id_none_available(self):
        def env_list(*args, **kwargs):
            return []

        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            mock_uep.getEnvironmentList = env_list
            mock_uep.supports_resource = Mock(return_value=True)
            self.stub_cp_provider.basic_auth_cp = mock_uep

            rc = RegisterCommand()
            rc.options = Mock()
            rc.options.activation_keys = None
            rc.options.environments = None
            env_id = rc._process_environments(mock_uep, 'owner')

            expected = None
            self.assertEqual(expected, env_id)

    def test_get_environment_id_one_available(self):
        def env_list(*args, **kwargs):
            return [{"id": "1234", "name": "somename"}]

        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            mock_uep.getEnvironmentList = env_list
            mock_uep.supports_resource = Mock(return_value=True)
            self.stub_cp_provider.basic_auth_cp = mock_uep

            rc = RegisterCommand()
            rc.options = Mock()
            rc.options.activation_keys = None
            rc.options.environments = None
            env_id = rc._process_environments(mock_uep, 'owner')

            expected = "1234"
            self.assertEqual(expected, env_id)

    def test_get_environment_id_multi_available(self):
        def env_list(*args, **kwargs):
            return [{"id": "1234", "name": "somename"},
                    {"id": "5678", "name": "othername"}]

        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            mock_uep.getEnvironmentList = env_list
            mock_uep.supports_resource = Mock(return_value=True)
            self.stub_cp_provider.basic_auth_cp = mock_uep

            rc = RegisterCommand()
            rc.cp = mock_uep
            rc.options = Mock()
            rc.options.activation_keys = None
            rc.options.environments = None
            rc._prompt_for_environment = Mock(return_value="othername")
            env_id = rc._process_environments(mock_uep, 'owner')

            expected = "5678"
            self.assertEqual(expected, env_id)

    def test_get_environment_id_multi_available_bad_name(self):
        def env_list(*args, **kwargs):
            return [{"id": "1234", "name": "somename"},
                    {"id": "5678", "name": "othername"}]

        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            mock_uep.getEnvironmentList = env_list
            mock_uep.supports_resource = Mock(return_value=True)
            self.stub_cp_provider.basic_auth_cp = mock_uep

            rc = RegisterCommand()
            rc.cp = mock_uep
            rc.options = Mock()
            rc.options.activation_keys = None
            rc.options.environments = None
            rc._prompt_for_environment = Mock(return_value="not_an_env")

            with Capture(silent=True):
                with self.assertRaises(SystemExit):
                    rc._process_environments(mock_uep, 'owner')

    def test_set_multi_environment_id_multi_available(self):
        def env_list(*args, **kwargs):
            return [{"id": "1234", "name": "somename"},
                    {"id": "5678", "name": "othername"}]

        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            mock_uep.getEnvironmentList = env_list
            mock_uep.supports_resource = Mock(return_value=True)
            mock_uep.has_capability = Mock(return_value=True)
            self.stub_cp_provider.basic_auth_cp = mock_uep

            rc = RegisterCommand()
            rc.cp = mock_uep
            rc.options = Mock()
            rc.options.activation_keys = None
            rc.options.environments = None
            rc._prompt_for_environment = Mock(return_value="somename,othername")
            env_id = rc._process_environments(mock_uep, 'owner')
            expected = "1234,5678"
            self.assertEqual(expected, env_id)

    def test_validate_multi_capable_multi_entry(self):
        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            rc = RegisterCommand()
            rc.cp = mock_uep
            rc.is_registered = Mock(return_value=False)
            rc.options = Mock()
            rc.options.activation_keys = None
            rc.options.force = None
            rc.options.consumertype = None
            rc.options.environments = 'One,Two'

            mock_uep.has_capability = Mock(return_value=False)
            try:
                rc._validate_options()
                self.fail("No Exception Raised")
            except SystemExit as e:
                self.assertEqual(e.code, os.EX_USAGE)

            mock_uep.has_capability = Mock(return_value=True)
            try:
                rc._validate_options()
            except SystemExit:
                self.fail("Exception Raised")

    def test_validate_multi_environment_with_activation_key(self):
        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            mock_uep.supports_resource = Mock(return_value=True)
            self.stub_cp_provider.basic_auth_cp = mock_uep

            rc = RegisterCommand()
            rc.cp = mock_uep
            rc.options = Mock()
            rc.options.activation_keys = 'someAK'
            rc.options.environments = None
            ret = rc._process_environments(mock_uep, 'owner')
            self.assertIsNone(ret)

    def test_set_duplicate_multi_environment(self):
        def env_list(*args, **kwargs):
            return [{"id": "1234", "name": "somename"},
                    {"id": "5678", "name": "othername"}]

        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            mock_uep.getEnvironmentList = env_list
            mock_uep.supports_resource = Mock(return_value=True)
            self.stub_cp_provider.basic_auth_cp = mock_uep

            rc = RegisterCommand()
            rc.cp = mock_uep
            rc.options = Mock()
            rc.options.activation_keys = None
            rc.options.environments = None
            rc._prompt_for_environment = Mock(return_value="somename,othername,somename")

            with Capture(silent=True):
                with self.assertRaises(SystemExit):
                    rc._process_environments(mock_uep, 'owner')

    def test_registration_with_failed_profile_upload(self):

        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            profile_mgr = inj.require(inj.PROFILE_MANAGER)

            def raise_remote_server_exception(*args, **kwargs):
                """Raise remote server exception (uploading of profile failed)"""
                from rhsm.connection import RemoteServerException
                raise RemoteServerException(
                    502,
                    request_type="PUT",
                    handler="/subscription/consumers/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/profiles"
                )

            profile_mgr.update_check = Mock(side_effect=raise_remote_server_exception)
            self.stub_cp_provider.basic_auth_cp = mock_uep
            cmd = RegisterCommand()
            with Capture() as cap:
                cmd.main(['--force', '--username', 'admin', '--password', 'admin', '--org', 'admin'])
                output = cap.out
                self.assertTrue("The system has been registered with ID" in output)
                self.assertTrue("The registered system name is:" in output)

    def test_deprecate_consumer_type(self):
        with patch('rhsm.connection.UEPConnection', new_callable=StubUEP) as mock_uep:
            self.stub_cp_provider.basic_auth_cp = mock_uep

            cmd = RegisterCommand()
            self._inject_mock_invalid_consumer()

            with Capture(silent=True):
                with self.assertRaises(SystemExit) as e:
                    cmd.main(['--type=candlepin'])
                    self.assertEqual(e.code, os.EX_USAGE)
