#
#   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 mock
from mock import call

from osc_lib import exceptions
from osc_lib import utils

from openstackclient.tests.unit import utils as tests_utils
from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
from openstackclient.volume.v1 import volume_snapshot


class TestSnapshot(volume_fakes.TestVolumev1):

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

        self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
        self.snapshots_mock.reset_mock()
        self.volumes_mock = self.app.client_manager.volume.volumes
        self.volumes_mock.reset_mock()


class TestSnapshotCreate(TestSnapshot):

    columns = (
        'created_at',
        'display_description',
        'display_name',
        'id',
        'properties',
        'size',
        'status',
        'volume_id',
    )

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

        self.volume = volume_fakes.FakeVolume.create_one_volume()
        self.new_snapshot = volume_fakes.FakeSnapshot.create_one_snapshot(
            attrs={'volume_id': self.volume.id})

        self.data = (
            self.new_snapshot.created_at,
            self.new_snapshot.display_description,
            self.new_snapshot.display_name,
            self.new_snapshot.id,
            utils.format_dict(self.new_snapshot.metadata),
            self.new_snapshot.size,
            self.new_snapshot.status,
            self.new_snapshot.volume_id,
        )

        self.volumes_mock.get.return_value = self.volume
        self.snapshots_mock.create.return_value = self.new_snapshot
        # Get the command object to test
        self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)

    def test_snapshot_create(self):
        arglist = [
            "--volume", self.new_snapshot.volume_id,
            "--description", self.new_snapshot.display_description,
            "--force",
            self.new_snapshot.display_name,
        ]
        verifylist = [
            ("volume", self.new_snapshot.volume_id),
            ("description", self.new_snapshot.display_description),
            ("force", True),
            ("snapshot_name", self.new_snapshot.display_name),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.snapshots_mock.create.assert_called_with(
            self.new_snapshot.volume_id,
            True,
            self.new_snapshot.display_name,
            self.new_snapshot.display_description,
        )
        self.assertEqual(self.columns, columns)
        self.assertEqual(self.data, data)

    def test_snapshot_create_without_name(self):
        arglist = [
            "--volume", self.new_snapshot.volume_id,
        ]
        verifylist = [
            ("volume", self.new_snapshot.volume_id),
        ]
        self.assertRaises(
            tests_utils.ParserException,
            self.check_parser,
            self.cmd,
            arglist,
            verifylist,
        )

    def test_snapshot_create_without_volume(self):
        arglist = [
            "--description", self.new_snapshot.display_description,
            "--force",
            self.new_snapshot.display_name
        ]
        verifylist = [
            ("description", self.new_snapshot.display_description),
            ("force", True),
            ("snapshot_name", self.new_snapshot.display_name)
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.volumes_mock.get.assert_called_once_with(
            self.new_snapshot.display_name)
        self.snapshots_mock.create.assert_called_once_with(
            self.new_snapshot.volume_id,
            True,
            self.new_snapshot.display_name,
            self.new_snapshot.display_description,
        )
        self.assertEqual(self.columns, columns)
        self.assertEqual(self.data, data)


class TestSnapshotDelete(TestSnapshot):

    snapshots = volume_fakes.FakeSnapshot.create_snapshots(count=2)

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

        self.snapshots_mock.get = (
            volume_fakes.FakeSnapshot.get_snapshots(self.snapshots))
        self.snapshots_mock.delete.return_value = None

        # Get the command object to mock
        self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)

    def test_snapshot_delete(self):
        arglist = [
            self.snapshots[0].id
        ]
        verifylist = [
            ("snapshots", [self.snapshots[0].id])
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        result = self.cmd.take_action(parsed_args)

        self.snapshots_mock.delete.assert_called_with(
            self.snapshots[0].id)
        self.assertIsNone(result)

    def test_delete_multiple_snapshots(self):
        arglist = []
        for s in self.snapshots:
            arglist.append(s.id)
        verifylist = [
            ('snapshots', arglist),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        result = self.cmd.take_action(parsed_args)

        calls = []
        for s in self.snapshots:
            calls.append(call(s.id))
        self.snapshots_mock.delete.assert_has_calls(calls)
        self.assertIsNone(result)

    def test_delete_multiple_snapshots_with_exception(self):
        arglist = [
            self.snapshots[0].id,
            'unexist_snapshot',
        ]
        verifylist = [
            ('snapshots', arglist),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        find_mock_result = [self.snapshots[0], exceptions.CommandError]
        with mock.patch.object(utils, 'find_resource',
                               side_effect=find_mock_result) as find_mock:
            try:
                self.cmd.take_action(parsed_args)
                self.fail('CommandError should be raised.')
            except exceptions.CommandError as e:
                self.assertEqual('1 of 2 snapshots failed to delete.',
                                 str(e))

            find_mock.assert_any_call(
                self.snapshots_mock, self.snapshots[0].id)
            find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot')

            self.assertEqual(2, find_mock.call_count)
            self.snapshots_mock.delete.assert_called_once_with(
                self.snapshots[0].id
            )


class TestSnapshotList(TestSnapshot):

    volume = volume_fakes.FakeVolume.create_one_volume()
    snapshots = volume_fakes.FakeSnapshot.create_snapshots(
        attrs={'volume_id': volume.display_name}, count=3)

    columns = [
        "ID",
        "Name",
        "Description",
        "Status",
        "Size"
    ]
    columns_long = columns + [
        "Created At",
        "Volume",
        "Properties"
    ]

    data = []
    for s in snapshots:
        data.append((
            s.id,
            s.display_name,
            s.display_description,
            s.status,
            s.size,
        ))
    data_long = []
    for s in snapshots:
        data_long.append((
            s.id,
            s.display_name,
            s.display_description,
            s.status,
            s.size,
            s.created_at,
            s.volume_id,
            utils.format_dict(s.metadata),
        ))

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

        self.volumes_mock.list.return_value = [self.volume]
        self.volumes_mock.get.return_value = self.volume
        self.snapshots_mock.list.return_value = self.snapshots
        # Get the command to test
        self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)

    def test_snapshot_list_without_options(self):
        arglist = []
        verifylist = [
            ('all_projects', False),
            ("long", False)
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.snapshots_mock.list.assert_called_once_with(
            search_opts={
                'all_tenants': False,
                'display_name': None,
                'status': None,
                'volume_id': None
            }
        )
        self.assertEqual(self.columns, columns)
        self.assertEqual(self.data, list(data))

    def test_snapshot_list_with_long(self):
        arglist = [
            "--long",
        ]
        verifylist = [
            ("long", True),
            ('all_projects', False),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.snapshots_mock.list.assert_called_once_with(
            search_opts={
                'all_tenants': False,
                'display_name': None,
                'status': None,
                'volume_id': None
            }
        )
        self.assertEqual(self.columns_long, columns)
        self.assertEqual(self.data_long, list(data))

    def test_snapshot_list_name_option(self):
        arglist = [
            '--name', self.snapshots[0].display_name,
        ]
        verifylist = [
            ('all_projects', False),
            ('long', False),
            ('name', self.snapshots[0].display_name),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.snapshots_mock.list.assert_called_once_with(
            search_opts={
                'all_tenants': False,
                'display_name': self.snapshots[0].display_name,
                'status': None,
                'volume_id': None
            }
        )
        self.assertEqual(self.columns, columns)
        self.assertEqual(self.data, list(data))

    def test_snapshot_list_status_option(self):
        arglist = [
            '--status', self.snapshots[0].status,
        ]
        verifylist = [
            ('all_projects', False),
            ('long', False),
            ('status', self.snapshots[0].status),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.snapshots_mock.list.assert_called_once_with(
            search_opts={
                'all_tenants': False,
                'display_name': None,
                'status': self.snapshots[0].status,
                'volume_id': None
            }
        )
        self.assertEqual(self.columns, columns)
        self.assertEqual(self.data, list(data))

    def test_snapshot_list_volumeid_option(self):
        arglist = [
            '--volume', self.volume.id,
        ]
        verifylist = [
            ('all_projects', False),
            ('long', False),
            ('volume', self.volume.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.snapshots_mock.list.assert_called_once_with(
            search_opts={
                'all_tenants': False,
                'display_name': None,
                'status': None,
                'volume_id': self.volume.id
            }
        )
        self.assertEqual(self.columns, columns)
        self.assertEqual(self.data, list(data))

    def test_snapshot_list_all_projects(self):
        arglist = [
            '--all-projects',
        ]
        verifylist = [
            ('long', False),
            ('all_projects', True)
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)

        self.snapshots_mock.list.assert_called_once_with(
            search_opts={
                'all_tenants': True,
                'display_name': None,
                'status': None,
                'volume_id': None
            }
        )
        self.assertEqual(self.columns, columns)
        self.assertEqual(self.data, list(data))


class TestSnapshotSet(TestSnapshot):

    snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()

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

        self.snapshots_mock.get.return_value = self.snapshot
        self.snapshots_mock.set_metadata.return_value = None
        # Get the command object to mock
        self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)

    def test_snapshot_set_all(self):
        arglist = [
            "--name", "new_snapshot",
            "--description", "new_description",
            "--property", "foo_1=foo_1",
            "--property", "foo_2=foo_2",
            "--no-property",
            self.snapshot.id,
        ]
        new_property = {"foo_1": "foo_1", "foo_2": "foo_2"}
        verifylist = [
            ("name", "new_snapshot"),
            ("description", "new_description"),
            ("property", new_property),
            ("no_property", True),
            ("snapshot", self.snapshot.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        result = self.cmd.take_action(parsed_args)

        kwargs = {
            "display_name": "new_snapshot",
            "display_description": "new_description",
        }
        self.snapshot.update.assert_called_with(**kwargs)
        self.snapshots_mock.delete_metadata.assert_called_with(
            self.snapshot.id, ["foo"]
        )
        self.snapshots_mock.set_metadata.assert_called_with(
            self.snapshot.id, {"foo_2": "foo_2", "foo_1": "foo_1"}
        )
        self.assertIsNone(result)

    def test_snapshot_set_nothing(self):
        arglist = [
            self.snapshot.id,
        ]
        verifylist = [
            ("snapshot", self.snapshot.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        result = self.cmd.take_action(parsed_args)
        self.assertIsNone(result)

    def test_snapshot_set_fail(self):
        self.snapshots_mock.set_metadata.side_effect = (
            exceptions.CommandError())
        arglist = [
            "--name", "new_snapshot",
            "--description", "new_description",
            "--property", "x=y",
            "--property", "foo=foo",
            self.snapshot.id,
        ]
        new_property = {"x": "y", "foo": "foo"}
        verifylist = [
            ("name", "new_snapshot"),
            ("description", "new_description"),
            ("property", new_property),
            ("snapshot", self.snapshot.id),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        self.assertRaises(exceptions.CommandError,
                          self.cmd.take_action, parsed_args)


class TestSnapshotShow(TestSnapshot):

    columns = (
        'created_at',
        'display_description',
        'display_name',
        'id',
        'properties',
        'size',
        'status',
        'volume_id',
    )

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

        self.snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()

        self.data = (
            self.snapshot.created_at,
            self.snapshot.display_description,
            self.snapshot.display_name,
            self.snapshot.id,
            utils.format_dict(self.snapshot.metadata),
            self.snapshot.size,
            self.snapshot.status,
            self.snapshot.volume_id,
        )

        self.snapshots_mock.get.return_value = self.snapshot
        # Get the command object to test
        self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)

    def test_snapshot_show(self):
        arglist = [
            self.snapshot.id
        ]
        verifylist = [
            ("snapshot", self.snapshot.id)
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        columns, data = self.cmd.take_action(parsed_args)
        self.snapshots_mock.get.assert_called_with(self.snapshot.id)

        self.assertEqual(self.columns, columns)
        self.assertEqual(self.data, data)


class TestSnapshotUnset(TestSnapshot):

    snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()

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

        self.snapshots_mock.get.return_value = self.snapshot
        self.snapshots_mock.delete_metadata.return_value = None
        # Get the command object to mock
        self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)

    def test_snapshot_unset(self):
        arglist = [
            "--property", "foo",
            self.snapshot.id,
        ]
        verifylist = [
            ("property", ["foo"]),
            ("snapshot", self.snapshot.id),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        result = self.cmd.take_action(parsed_args)

        self.snapshots_mock.delete_metadata.assert_called_with(
            self.snapshot.id, ["foo"]
        )
        self.assertIsNone(result)

    def test_snapshot_unset_nothing(self):
        arglist = [
            self.snapshot.id,
        ]
        verifylist = [
            ("snapshot", self.snapshot.id),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        result = self.cmd.take_action(parsed_args)
        self.assertIsNone(result)
