# Copyright 2017 Amazon.com, Inc. or its affiliates. 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. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file 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 argparse
import os

from botocore.session import Session
from botocore.history import HistoryRecorder
from botocore.exceptions import ProfileNotFound

from awscli.testutils import unittest, mock, FileCreator
from awscli.compat import StringIO
from awscli.customizations.history import attach_history_handler
from awscli.customizations.history import add_history_commands
from awscli.customizations.history import HistoryCommand
from awscli.customizations.history.db import DatabaseHistoryHandler


class TestAttachHistoryHandler(unittest.TestCase):
    def setUp(self):
        self.files = FileCreator()

    def tearDown(self):
        self.files.remove_all()

    @mock.patch('awscli.customizations.history.sqlite3')
    @mock.patch('awscli.customizations.history.db.sqlite3')
    @mock.patch('awscli.customizations.history.HISTORY_RECORDER',
                spec=HistoryRecorder)
    def test_attach_history_handler(self, mock_recorder, mock_db_sqlite3, mock_sqlite3):
        mock_session = mock.Mock(Session)
        mock_session.get_scoped_config.return_value = {
            'cli_history': 'enabled'
        }

        parsed_args = argparse.Namespace()
        parsed_args.command = 's3'

        attach_history_handler(session=mock_session, parsed_args=parsed_args)
        self.assertEqual(mock_recorder.add_handler.call_count, 1)
        self.assertIsInstance(
            mock_recorder.add_handler.call_args[0][0], DatabaseHistoryHandler)
        self.assertTrue(mock_db_sqlite3.connect.called)

    @mock.patch('awscli.customizations.history.sqlite3')
    @mock.patch('awscli.customizations.history.db.sqlite3')
    @mock.patch('awscli.customizations.history.HISTORY_RECORDER',
                spec=HistoryRecorder)
    def test_no_attach_history_handler_when_history_not_configured(
            self, mock_recorder, mock_db_sqlite3, mock_sqlite3):
        mock_session = mock.Mock(Session)
        mock_session.get_scoped_config.return_value = {}

        parsed_args = argparse.Namespace()
        parsed_args.command = 's3'

        attach_history_handler(session=mock_session, parsed_args=parsed_args)
        self.assertFalse(mock_recorder.add_handler.called)
        self.assertFalse(mock_db_sqlite3.connect.called)

    @mock.patch('awscli.customizations.history.sqlite3')
    @mock.patch('awscli.customizations.history.db.sqlite3')
    @mock.patch('awscli.customizations.history.HISTORY_RECORDER',
                spec=HistoryRecorder)
    def test_no_attach_history_handler_when_command_is_history(
            self, mock_recorder, mock_db_sqlite3, mock_sqlite3):
        mock_session = mock.Mock(Session)
        mock_session.get_scoped_config.return_value = {
            'cli_history': 'enabled'
        }

        parsed_args = argparse.Namespace()
        parsed_args.command = 'history'

        attach_history_handler(session=mock_session, parsed_args=parsed_args)
        self.assertFalse(mock_recorder.add_handler.called)
        self.assertFalse(mock_db_sqlite3.connect.called)

    @mock.patch('awscli.customizations.history.sqlite3', None)
    @mock.patch('awscli.customizations.history.db.sqlite3')
    @mock.patch('awscli.customizations.history.HISTORY_RECORDER',
                spec=HistoryRecorder)
    def test_no_attach_history_handler_when_no_sqlite3(
            self, mock_recorder, mock_sqlite3):
        mock_session = mock.Mock(Session)
        mock_session.get_scoped_config.return_value = {
            'cli_history': 'enabled'
        }

        parsed_args = argparse.Namespace()
        parsed_args.command = 's3'

        with mock.patch('sys.stderr', StringIO()) as mock_stderr:
            attach_history_handler(
                session=mock_session, parsed_args=parsed_args)
            self.assertIn(
                'enabled but sqlite3 is unavailable', mock_stderr.getvalue())
        self.assertFalse(mock_recorder.add_handler.called)
        self.assertFalse(mock_sqlite3.connect.called)

    @mock.patch('awscli.customizations.history.sqlite3')
    @mock.patch('awscli.customizations.history.db.sqlite3')
    @mock.patch('awscli.customizations.history.HISTORY_RECORDER',
                spec=HistoryRecorder)
    def test_create_directory_no_exists(self, mock_recorder,
                                        mock_db_sqlite3, mock_sqlite3):
        mock_session = mock.Mock(Session)
        mock_session.get_scoped_config.return_value = {
            'cli_history': 'enabled'
        }

        parsed_args = argparse.Namespace()
        parsed_args.command = 's3'

        directory_to_create = os.path.join(self.files.rootdir, 'create-dir')
        db_filename = os.path.join(directory_to_create, 'name.db')
        with mock.patch('os.environ', {'AWS_CLI_HISTORY_FILE': db_filename}):
            attach_history_handler(
                session=mock_session, parsed_args=parsed_args)
            self.assertEqual(mock_recorder.add_handler.call_count, 1)
            # Is should create any missing parent directories of the
            # file as well.
            self.assertTrue(os.path.exists(directory_to_create))
        self.assertTrue(mock_db_sqlite3.connect.called)

    @mock.patch('awscli.customizations.history.sqlite3')
    @mock.patch('awscli.customizations.history.db.sqlite3')
    @mock.patch('awscli.customizations.history.HISTORY_RECORDER',
                spec=HistoryRecorder)
    def test_profile_does_not_exist(self, mock_recorder, mock_db_sqlite3,
                                    mock_sqlite3):
        mock_session = mock.Mock(Session)
        mock_session.get_scoped_config.side_effect = ProfileNotFound(
            profile='no-exist')

        parsed_args = argparse.Namespace()
        parsed_args.command = 'configure'

        attach_history_handler(session=mock_session, parsed_args=parsed_args)
        self.assertFalse(mock_recorder.add_handler.called)
        self.assertFalse(mock_db_sqlite3.connect.called)


class TestAddHistoryCommand(unittest.TestCase):
    def test_add_history_command(self):
        command_table = {}
        mock_session = mock.Mock(Session)
        add_history_commands(
            command_table=command_table, session=mock_session)
        self.assertIn('history', command_table)
        self.assertIsInstance(command_table['history'], HistoryCommand)


class TestHistoryCommand(unittest.TestCase):
    def test_requires_subcommand(self):
        mock_session = mock.Mock(Session)
        history_command = HistoryCommand(mock_session)
        parsed_args = argparse.Namespace()
        parsed_args.subcommand = None
        parsed_globals = argparse.Namespace()
        with self.assertRaises(ValueError):
            history_command._run_main(parsed_args, parsed_globals)
