# 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.

from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc

from manilaclient.tests.functional.osc import base
from manilaclient.tests.functional import utils

LOCK_DETAIL_ATTRIBUTES = [
    'ID',
    'Resource Id',
    'Resource Type',
    'Resource Action',
    'Lock Context',
    'User Id',
    'Project Id',
    'Created At',
    'Updated At',
    'Lock Reason',
]

LOCK_SUMMARY_ATTRIBUTES = [
    'ID',
    'Resource Id',
    'Resource Type',
    'Resource Action',
]


@utils.skip_if_microversion_not_supported('2.82')
class ResourceLockTests(base.OSCClientTestBase):
    """Lock CLI test cases"""

    def setUp(self):
        super().setUp()
        self.share_type = self.create_share_type(
            name=data_utils.rand_name('lock_tests_type')
        )
        self.share = self.create_share(share_type=self.share_type['id'])

    def test_lock_create_show_use_delete(self):
        """Create a deletion lock on share, view it, try it and remove."""
        lock = self.create_resource_lock(
            self.share['id'],
            lock_reason='tigers rule',
            client=self.user_client,
            add_cleanup=False,
        )

        client_user_id = self.openstack(
            "token issue -c user_id -f value", client=self.user_client
        ).strip()
        client_project_id = self.openstack(
            "token issue -c project_id -f value", client=self.user_client
        ).strip()

        self.assertEqual(self.share['id'], lock['resource_id'])
        self.assertEqual('delete', lock['resource_action'])
        self.assertEqual(client_user_id, lock['user_id'])
        self.assertEqual(client_project_id, lock['project_id'])
        self.assertEqual('user', lock['lock_context'])
        self.assertEqual('tigers rule', lock['lock_reason'])

        lock_show = self.dict_result("share", f"lock show {lock['id']}")
        self.assertEqual(lock['id'], lock_show['ID'])
        self.assertEqual(lock['lock_context'], lock_show['Lock Context'])

        # When a deletion lock exists, share deletion must fail
        self.assertRaises(
            lib_exc.CommandFailed,
            self.openstack,
            f"share delete {self.share['id']}",
        )

        # delete the lock, share will be deleted in cleanup stage
        self.openstack(
            f"share lock delete {lock['id']}", client=self.user_client
        )

        self.assertRaises(
            lib_exc.CommandFailed,
            self.openstack,
            f"share lock show {lock['id']}",
        )

    def test_lock_list_filter_paginate(self):
        lock_1 = self.create_resource_lock(
            self.share['id'],
            lock_reason='tigers rule',
            client=self.user_client,
        )
        lock_2 = self.create_resource_lock(
            self.share['id'],
            lock_reason='tigers still rule',
            client=self.user_client,
        )
        lock_3 = self.create_resource_lock(
            self.share['id'],
            lock_reason='admins rule',
            client=self.admin_client,
        )

        locks = self.listing_result(
            'share', f'lock list --resource {self.share["id"]}'
        )

        self.assertEqual(3, len(locks))
        self.assertEqual(
            sorted(LOCK_SUMMARY_ATTRIBUTES), sorted(locks[0].keys())
        )

        locks = self.listing_result(
            'share',
            f'lock list --lock-context user  --resource {self.share["id"]}',
        )
        self.assertEqual(2, len(locks))
        self.assertNotIn(lock_3['id'], [lock['ID'] for lock in locks])

        locks = self.listing_result(
            'share',
            'lock list --lock-context user'
            f' --resource {self.share["id"]}'
            ' --sort-key created_at '
            ' --sort-dir desc '
            ' --limit 1',
        )
        self.assertEqual(1, len(locks))
        self.assertIn(lock_2['id'], [lock['ID'] for lock in locks])
        self.assertNotIn(lock_1['id'], [lock['ID'] for lock in locks])
        self.assertNotIn(lock_3['id'], [lock['ID'] for lock in locks])

    def test_lock_set_unset_lock_reason(self):
        lock = self.create_resource_lock(
            self.share['id'], client=self.user_client
        )
        self.assertEqual('None', lock['lock_reason'])

        self.openstack(
            f'share lock set --lock-reason \'updated reason\' {lock["id"]}'
        )
        lock_show = self.dict_result("share", f"lock show {lock['id']}")
        self.assertEqual('updated reason', lock_show['Lock Reason'])

        self.openstack(f"share lock unset --lock-reason {lock['id']}")
        lock_show = self.dict_result("share", f"lock show {lock['id']}")
        self.assertEqual('None', lock_show['Lock Reason'])

    def test_lock_restrictions(self):
        """A user can't update or delete a lock created by another user."""
        lock = self.create_resource_lock(
            self.share['id'], client=self.admin_client, add_cleanup=False
        )
        self.assertEqual('admin', lock['lock_context'])

        self.assertRaises(
            lib_exc.CommandFailed,
            self.openstack,
            f"share lock set {lock['id']} --reason 'i cannot do this'",
            client=self.user_client,
        )
        self.assertRaises(
            lib_exc.CommandFailed,
            self.openstack,
            f"share lock unset {lock['id']} --reason",
            client=self.user_client,
        )
        self.assertRaises(
            lib_exc.CommandFailed,
            self.openstack,
            f"share lock delete {lock['id']} ",
            client=self.user_client,
        )

        self.openstack(
            f'share lock set --lock-reason "I can do this" {lock["id"]}',
            client=self.admin_client,
        )

        self.openstack(
            f'share lock delete {lock["id"]}', client=self.admin_client
        )
