#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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 io
import unittest
from functools import reduce
from typing import List, Any, Callable, Dict, cast
from unittest.mock import patch, MagicMock

from apache_polaris.cli.command import Command
from apache_polaris.cli.command.policies import PoliciesCommand
from apache_polaris.cli.options.parser import Parser
from apache_polaris.sdk.management import PolarisDefaultApi

INVALID_ARGS = 2


class TestCliParsing(unittest.TestCase):
    def test_invalid_commands(self) -> None:
        with self.assertRaises(SystemExit) as cm:
            Parser.parse(["not-real-command!", "list"])
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(["catalogs", "not-real-subcommand"])
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(["catalogs", "create"])  # missing required input
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(
                ["catalogs", "create", "catalog_name", "--type", "BANANA"]
            )  # invalid catalog type
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(
                ["catalogs", "create", "catalog_name", "--set-property", "foo=bar"]
            )  # can't use --set-property on create
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(["catalogs", "get", "catalog_name", "--fake-flag"])
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(
                ["catalogs", "update", "catalog_name", "--property", "foo=bar"]
            )
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(
                [
                    "catalogs",
                    "create",
                    "catalog_name",
                    "--type",
                    "EXTERNAL",
                    "--remote-url",
                    "gone",
                ]
            )  # remote-url deprecated
            self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(["principals", "create", "name", "--type", "bad"])
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(["principals", "update", "name", "--client-id", "something"])
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(
                [
                    "privileges",
                    "catalog",
                    "--catalog",
                    "c",
                    "--catalog-role",
                    "r",
                    "privilege",
                    "grant",
                ]
            )
        self.assertEqual(cm.exception.code, INVALID_ARGS)

        with self.assertRaises(SystemExit) as cm:
            Parser.parse(
                [
                    "privileges",
                    "--catalog",
                    "c",
                    "--catalog-role",
                    "r",
                    "catalog",
                    "grant",
                    "privilege",
                    "--namespace",
                    "unexpected!",
                ]
            )
        self.assertEqual(cm.exception.code, INVALID_ARGS)

    def _check_usage_output(self, f: Callable[[], Any], needle: str = "usage:") -> None:
        with (
            patch("sys.stdout", new_callable=io.StringIO) as mock_stdout,
            patch("sys.stderr", new_callable=io.StringIO),
        ):
            with self.assertRaises(SystemExit) as cm:
                f()
            self.assertEqual(cm.exception.code, 0)
            help_output = str(mock_stdout.getvalue())
            self.assertIn("usage:", help_output)
        print(help_output)

    def test_usage(self) -> None:
        self._check_usage_output(lambda: Parser.parse(["--help"]))
        self._check_usage_output(lambda: Parser.parse(["catalogs", "--help"]))
        self._check_usage_output(lambda: Parser.parse(["catalogs", "create", "--help"]))
        self._check_usage_output(
            lambda: Parser.parse(["catalogs", "create", "something", "--help"])
        )

    def test_extended_usage(self) -> None:
        self._check_usage_output(
            lambda: Parser._build_parser().parse_args(["--help"], "input:")
        )
        self._check_usage_output(
            lambda: Parser._build_parser().parse_args(["catalogs", "--help"], "input:")
        )
        self._check_usage_output(
            lambda: Parser._build_parser().parse_args(
                ["catalogs", "create", "--help"], "input:"
            )
        )
        self._check_usage_output(
            lambda: Parser._build_parser().parse_args(
                ["catalogs", "create", "c", "--help"], "input:"
            )
        )
        self._check_usage_output(
            lambda: Parser._build_parser().parse_args(
                ["privileges", "table", "grant", "--help"], "input:"
            )
        )
        self._check_usage_output(
            lambda: Parser.parse(["catalogs", "create", "something", "--help"]),
            "input:",
        )

    def test_parsing_valid_commands(self) -> None:
        Parser.parse(["catalogs", "create", "catalog_name"])
        Parser.parse(["catalogs", "create", "catalog_name", "--type", "internal"])
        Parser.parse(["catalogs", "create", "catalog_name", "--type", "INTERNAL"])
        Parser.parse(["catalogs", "list"])
        Parser.parse(["catalogs", "get", "catalog_name"])
        Parser.parse(["principals", "list"])
        Parser.parse(["--host", "some-host", "catalogs", "list"])
        Parser.parse(
            ["--base-url", "https://customservice.com/subpath", "catalogs", "list"]
        )
        Parser.parse(
            [
                "privileges",
                "catalog",
                "grant",
                "--catalog",
                "foo",
                "--catalog-role",
                "bar",
                "TABLE_READ_DATA",
            ]
        )
        Parser.parse(
            [
                "privileges",
                "table",
                "grant",
                "--catalog",
                "foo",
                "--catalog-role",
                "bar",
                "--namespace",
                "n",
                "--table",
                "t",
                "TABLE_READ_DATA",
            ]
        )
        Parser.parse(
            [
                "privileges",
                "table",
                "revoke",
                "--catalog",
                "foo",
                "--catalog-role",
                "bar",
                "--namespace",
                "n",
                "--table",
                "t",
                "TABLE_READ_DATA",
            ]
        )

    # These commands are valid for parsing, but may cause errors within the command itself
    def test_parse_argparse_valid_commands(self) -> None:
        Parser.parse(["catalogs", "create", "catalog_name", "--type", "internal"])
        Parser.parse(
            [
                "privileges",
                "table",
                "grant",
                "--namespace",
                "n",
                "--table",
                "t",
                "TABLE_READ_DATA",
            ]
        )
        Parser.parse(
            [
                "privileges",
                "catalog",
                "grant",
                "--catalog",
                "c",
                "--catalog-role",
                "r",
                "fake-privilege",
            ]
        )

    def test_commands(self) -> None:
        def build_mock_client() -> MagicMock:
            client = MagicMock(spec=PolarisDefaultApi)
            client.call_tracker = dict()

            def capture_method(method_name: str) -> Callable[..., None]:
                def _capture(*args: Any, **kwargs: Any) -> None:
                    client.call_tracker["_method"] = method_name
                    for i, arg in enumerate(args):
                        client.call_tracker[i] = arg

                return _capture

            for method_name in dir(client):
                if callable(
                    getattr(client, method_name)
                ) and not method_name.startswith("__"):
                    setattr(
                        client,
                        method_name,
                        MagicMock(
                            name=method_name, side_effect=capture_method(method_name)
                        ),
                    )
            return client

        mock_client = build_mock_client()

        def mock_execute(input: List[str]) -> Dict[Any, Any]:
            mock_client.call_tracker = dict()

            # Assuming Parser and Command are used to parse input and generate commands
            options = Parser.parse(input)
            command = Command.from_options(options)

            try:
                command.execute(mock_client)
            except AttributeError as e:
                # Some commands may fail due to the mock, but the results should still match expectations
                print(f"Suppressed error: {e}")
            return mock_client.call_tracker

        def check_exception(f: Callable[[], Any], exception_str: str) -> None:
            throws = True
            try:
                f()
                throws = False
            except Exception as e:
                self.assertIn(exception_str, str(e))
            self.assertTrue(throws, "Exception should be raised")

        def check_arguments(
            result: Dict[Any, Any], method_name: str, args: Dict[Any, Any] = {}
        ) -> None:
            self.assertEqual(method_name, result["_method"])

            def get(obj: Any, arg_string: str) -> Any:
                attributes = arg_string.split(".")
                return reduce(getattr, attributes, obj)

            for arg, value in args.items():
                index, path = arg
                if path is not None:
                    self.assertEqual(value, get(result[index], path))
                else:
                    self.assertEqual(value, result[index])

        # Test various failing commands:
        check_exception(
            lambda: mock_execute(["catalogs", "create", "my-catalog"]), "--storage-type"
        )
        check_exception(
            lambda: mock_execute(
                ["catalogs", "create", "my-catalog", "--storage-type", "gcs"]
            ),
            "--default-base-location",
        )
        check_exception(
            lambda: mock_execute(["catalog-roles", "get", "foo"]), "--catalog"
        )
        check_exception(
            lambda: mock_execute(
                ["catalogs", "update", "foo", "--set-property", "bad-format"]
            ),
            "bad-format",
        )
        check_exception(
            lambda: mock_execute(
                [
                    "privileges",
                    "catalog",
                    "grant",
                    "--catalog",
                    "foo",
                    "--catalog-role",
                    "bar",
                    "TABLE_READ_MORE_BOOKS",
                ]
            ),
            "catalog privilege: TABLE_READ_MORE_BOOKS",
        )
        check_exception(
            lambda: mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--storage-type",
                    "gcs",
                    "--allowed-location",
                    "a",
                    "--allowed-location",
                    "b",
                    "--role-arn",
                    "ra",
                    "--default-base-location",
                    "x",
                ]
            ),
            "gcs",
        )
        check_exception(
            lambda: mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "file",
                    "--default-base-location",
                    "dbl",
                    "--catalog-connection-type",
                    "hive",
                    "--catalog-authentication-type",
                    "implicit",
                ]
            ),
            "--hive-warehouse",
        )

        # Test various correct commands:
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--storage-type",
                    "gcs",
                    "--default-base-location",
                    "x",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.storage_config_info.storage_type"): "GCS",
                (0, "catalog.properties.default_base_location"): "x",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "gcs",
                    "--default-base-location",
                    "dbl",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.type"): "EXTERNAL",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--storage-type",
                    "s3",
                    "--allowed-location",
                    "a",
                    "--allowed-location",
                    "b",
                    "--role-arn",
                    "ra",
                    "--external-id",
                    "ei",
                    "--default-base-location",
                    "x",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.storage_config_info.storage_type"): "S3",
                (0, "catalog.properties.default_base_location"): "x",
                (0, "catalog.storage_config_info.allowed_locations"): ["a", "b"],
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--storage-type",
                    "s3",
                    "--allowed-location",
                    "a",
                    "--role-arn",
                    "ra",
                    "--region",
                    "us-west-2",
                    "--external-id",
                    "ei",
                    "--default-base-location",
                    "x",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.storage_config_info.storage_type"): "S3",
                (0, "catalog.properties.default_base_location"): "x",
                (0, "catalog.storage_config_info.allowed_locations"): ["a"],
                (0, "catalog.storage_config_info.region"): "us-west-2",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--storage-type",
                    "gcs",
                    "--allowed-location",
                    "a",
                    "--allowed-location",
                    "b",
                    "--service-account",
                    "sa",
                    "--default-base-location",
                    "x",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.storage_config_info.storage_type"): "GCS",
                (0, "catalog.properties.default_base_location"): "x",
                (0, "catalog.storage_config_info.allowed_locations"): ["a", "b"],
                (0, "catalog.storage_config_info.gcs_service_account"): "sa",
            },
        )
        check_arguments(mock_execute(["catalogs", "list"]), "list_catalogs")
        check_arguments(
            mock_execute(
                ["--base-url", "https://customservice.com/subpath", "catalogs", "list"]
            ),
            "list_catalogs",
        )
        check_arguments(
            mock_execute(["catalogs", "delete", "foo"]),
            "delete_catalog",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["catalogs", "get", "foo"]),
            "get_catalog",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["catalogs", "update", "foo", "--default-base-location", "x"]),
            "get_catalog",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["catalogs", "update", "foo", "--set-property", "key=value"]),
            "get_catalog",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(
                ["catalogs", "update", "foo", "--set-property", "listkey=k1=v1,k2=v2"]
            ),
            "get_catalog",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["catalogs", "update", "foo", "--remove-property", "key"]),
            "get_catalog",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "update",
                    "foo",
                    "--set-property",
                    "key=value",
                    "--default-base-location",
                    "x",
                ]
            ),
            "get_catalog",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "update",
                    "foo",
                    "--set-property",
                    "key=value",
                    "--default-base-location",
                    "x",
                    "--region",
                    "us-west-1",
                ]
            ),
            "get_catalog",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principals", "create", "foo", "--property", "key=value"]),
            "create_principal",
            {
                (0, "principal.name"): "foo",
                (0, "principal.client_id"): None,
                (0, "principal.properties"): {"key": "value"},
            },
        )
        check_arguments(
            mock_execute(["principals", "delete", "foo"]),
            "delete_principal",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principals", "get", "foo"]),
            "get_principal",
            {
                (0, None): "foo",
            },
        )
        check_arguments(mock_execute(["principals", "list"]), "list_principals")
        check_arguments(
            mock_execute(["principals", "rotate-credentials", "foo"]),
            "rotate_credentials",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(
                ["principals", "update", "foo", "--set-property", "key=value"]
            ),
            "get_principal",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principals", "update", "foo", "--remove-property", "key"]),
            "get_principal",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principal-roles", "create", "foo"]),
            "create_principal_role",
            {
                (0, "principal_role.name"): "foo",
            },
        )
        check_arguments(
            mock_execute(["principal-roles", "delete", "foo"]),
            "delete_principal_role",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principal-roles", "delete", "foo"]),
            "delete_principal_role",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principal-roles", "get", "foo"]),
            "get_principal_role",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principal-roles", "get", "foo"]),
            "get_principal_role",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principal-roles", "list"]), "list_principal_roles"
        )
        check_arguments(
            mock_execute(["principal-roles", "list", "--principal", "foo"]),
            "list_principal_roles_assigned",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(
                ["principal-roles", "update", "foo", "--set-property", "key=value"]
            ),
            "get_principal_role",
            {(0, None): "foo"},
        )
        check_arguments(
            mock_execute(
                ["principal-roles", "update", "foo", "--remove-property", "key"]
            ),
            "get_principal_role",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["principal-roles", "grant", "bar", "--principal", "foo"]),
            "assign_principal_role",
            {
                (0, None): "foo",
                (1, "principal_role.name"): "bar",
            },
        )
        check_arguments(
            mock_execute(["principal-roles", "revoke", "bar", "--principal", "foo"]),
            "revoke_principal_role",
            {
                (0, None): "foo",
                (1, None): "bar",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalog-roles",
                    "create",
                    "foo",
                    "--catalog",
                    "bar",
                    "--property",
                    "key=value",
                ]
            ),
            "create_catalog_role",
            {
                (0, None): "bar",
                (1, "catalog_role.name"): "foo",
                (1, "catalog_role.properties"): {"key": "value"},
            },
        )
        check_arguments(
            mock_execute(["catalog-roles", "delete", "foo", "--catalog", "bar"]),
            "delete_catalog_role",
            {
                (0, None): "bar",
                (1, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["catalog-roles", "get", "foo", "--catalog", "bar"]),
            "get_catalog_role",
            {
                (0, None): "bar",
                (1, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["catalog-roles", "list", "foo"]),
            "list_catalog_roles",
            {
                (0, None): "foo",
            },
        )
        check_arguments(
            mock_execute(["catalog-roles", "list", "foo", "--principal-role", "bar"]),
            "list_catalog_roles_for_principal_role",
            {
                (0, None): "bar",
                (1, None): "foo",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalog-roles",
                    "update",
                    "foo",
                    "--catalog",
                    "bar",
                    "--set-property",
                    "key=value",
                ]
            ),
            "get_catalog_role",
            {
                (0, None): "bar",
                (1, None): "foo",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalog-roles",
                    "update",
                    "foo",
                    "--catalog",
                    "bar",
                    "--remove-property",
                    "key",
                ]
            ),
            "get_catalog_role",
            {
                (0, None): "bar",
                (1, None): "foo",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalog-roles",
                    "grant",
                    "--principal-role",
                    "foo",
                    "--catalog",
                    "bar",
                    "baz",
                ]
            ),
            "assign_catalog_role_to_principal_role",
            {
                (0, None): "foo",
                (1, None): "bar",
                (2, "catalog_role.name"): "baz",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalog-roles",
                    "revoke",
                    "--principal-role",
                    "foo",
                    "--catalog",
                    "bar",
                    "baz",
                ]
            ),
            "revoke_catalog_role_from_principal_role",
            {
                (0, None): "foo",
                (1, None): "bar",
                (2, None): "baz",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "privileges",
                    "catalog",
                    "grant",
                    "--catalog",
                    "foo",
                    "--catalog-role",
                    "bar",
                    "TABLE_READ_DATA",
                ]
            ),
            "add_grant_to_catalog_role",
            {
                (0, None): "foo",
                (1, None): "bar",
                (2, "grant.privilege.value"): "TABLE_READ_DATA",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "privileges",
                    "catalog",
                    "revoke",
                    "--catalog",
                    "foo",
                    "--catalog-role",
                    "bar",
                    "TABLE_READ_DATA",
                ]
            ),
            "revoke_grant_from_catalog_role",
            {
                (0, None): "foo",
                (1, None): "bar",
                (2, None): False,
                (3, "grant.privilege.value"): "TABLE_READ_DATA",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "privileges",
                    "namespace",
                    "grant",
                    "--namespace",
                    "a.b.c",
                    "--catalog",
                    "foo",
                    "--catalog-role",
                    "bar",
                    "TABLE_READ_DATA",
                ]
            ),
            "add_grant_to_catalog_role",
            {
                (0, None): "foo",
                (1, None): "bar",
                (2, "grant.privilege.value"): "TABLE_READ_DATA",
                (2, "grant.namespace"): ["a", "b", "c"],
            },
        )
        check_arguments(
            mock_execute(
                [
                    "privileges",
                    "table",
                    "grant",
                    "--namespace",
                    "a.b.c",
                    "--table",
                    "t",
                    "--catalog",
                    "foo",
                    "--catalog-role",
                    "bar",
                    "TABLE_READ_DATA",
                ]
            ),
            "add_grant_to_catalog_role",
            {
                (0, None): "foo",
                (1, None): "bar",
                (2, "grant.privilege.value"): "TABLE_READ_DATA",
                (2, "grant.namespace"): ["a", "b", "c"],
                (2, "grant.table_name"): "t",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "privileges",
                    "table",
                    "revoke",
                    "--namespace",
                    "a.b.c",
                    "--catalog",
                    "foo",
                    "--catalog-role",
                    "bar",
                    "--table",
                    "t",
                    "--cascade",
                    "TABLE_READ_DATA",
                ]
            ),
            "revoke_grant_from_catalog_role",
            {
                (0, None): "foo",
                (1, None): "bar",
                (2, None): True,
                (3, "grant.privilege.value"): "TABLE_READ_DATA",
                (3, "grant.namespace"): ["a", "b", "c"],
                (3, "grant.table_name"): "t",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "privileges",
                    "view",
                    "grant",
                    "--namespace",
                    "a.b.c",
                    "--catalog",
                    "foo",
                    "--catalog-role",
                    "bar",
                    "--view",
                    "v",
                    "VIEW_FULL_METADATA",
                ]
            ),
            "add_grant_to_catalog_role",
            {
                (0, None): "foo",
                (1, None): "bar",
                (2, "grant.privilege.value"): "VIEW_FULL_METADATA",
                (2, "grant.namespace"): ["a", "b", "c"],
                (2, "grant.view_name"): "v",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "gcs",
                    "--default-base-location",
                    "dbl",
                    "--catalog-connection-type",
                    "iceberg-rest",
                    "--iceberg-remote-catalog-name",
                    "i",
                    "--catalog-uri",
                    "u",
                    "--catalog-authentication-type",
                    "bearer",
                    "--catalog-bearer-token",
                    "b",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.type"): "EXTERNAL",
                (0, "catalog.connection_config_info.connection_type"): "ICEBERG_REST",
                (0, "catalog.connection_config_info.remote_catalog_name"): "i",
                (0, "catalog.connection_config_info.uri"): "u",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "gcs",
                    "--default-base-location",
                    "dbl",
                    "--catalog-connection-type",
                    "iceberg-rest",
                    "--iceberg-remote-catalog-name",
                    "c",
                    "--catalog-uri",
                    "u",
                    "--catalog-authentication-type",
                    "oauth",
                    "--catalog-token-uri",
                    "u",
                    "--catalog-client-id",
                    "i",
                    "--catalog-client-secret",
                    "k",
                    "--catalog-client-scope",
                    "s1",
                    "--catalog-client-scope",
                    "s2",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.type"): "EXTERNAL",
                (0, "catalog.connection_config_info.connection_type"): "ICEBERG_REST",
                (0, "catalog.connection_config_info.remote_catalog_name"): "c",
                (0, "catalog.connection_config_info.uri"): "u",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.authentication_type",
                ): "OAUTH",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.token_uri",
                ): "u",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.client_id",
                ): "i",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.scopes",
                ): ["s1", "s2"],
                (0, "catalog.storage_config_info.storage_type"): "GCS",
                (0, "catalog.properties.default_base_location"): "dbl",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "gcs",
                    "--default-base-location",
                    "dbl",
                    "--catalog-connection-type",
                    "iceberg-rest",
                    "--iceberg-remote-catalog-name",
                    "i",
                    "--catalog-uri",
                    "u",
                    "--catalog-authentication-type",
                    "sigv4",
                    "--catalog-role-arn",
                    "a",
                    "--catalog-signing-region",
                    "s",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.type"): "EXTERNAL",
                (0, "catalog.connection_config_info.connection_type"): "ICEBERG_REST",
                (0, "catalog.connection_config_info.remote_catalog_name"): "i",
                (0, "catalog.connection_config_info.uri"): "u",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.role_arn",
                ): "a",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.signing_region",
                ): "s",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "gcs",
                    "--default-base-location",
                    "dbl",
                    "--catalog-connection-type",
                    "iceberg-rest",
                    "--iceberg-remote-catalog-name",
                    "i",
                    "--catalog-uri",
                    "u",
                    "--catalog-authentication-type",
                    "sigv4",
                    "--catalog-role-arn",
                    "a",
                    "--catalog-signing-region",
                    "s",
                    "--catalog-role-session-name",
                    "n",
                    "--catalog-external-id",
                    "i",
                    "--catalog-signing-name",
                    "g",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.type"): "EXTERNAL",
                (0, "catalog.connection_config_info.connection_type"): "ICEBERG_REST",
                (0, "catalog.connection_config_info.remote_catalog_name"): "i",
                (0, "catalog.connection_config_info.uri"): "u",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.role_arn",
                ): "a",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.signing_region",
                ): "s",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.role_session_name",
                ): "n",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.external_id",
                ): "i",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.signing_name",
                ): "g",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "file",
                    "--default-base-location",
                    "dbl",
                    "--catalog-connection-type",
                    "hadoop",
                    "--hadoop-warehouse",
                    "h",
                    "--catalog-authentication-type",
                    "implicit",
                    "--catalog-uri",
                    "u",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.type"): "EXTERNAL",
                (0, "catalog.connection_config_info.connection_type"): "HADOOP",
                (0, "catalog.connection_config_info.warehouse"): "h",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.authentication_type",
                ): "IMPLICIT",
                (0, "catalog.connection_config_info.uri"): "u",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "file",
                    "--default-base-location",
                    "dbl",
                    "--catalog-connection-type",
                    "hive",
                    "--hive-warehouse",
                    "h",
                    "--catalog-authentication-type",
                    "implicit",
                    "--catalog-uri",
                    "u",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.type"): "EXTERNAL",
                (0, "catalog.connection_config_info.connection_type"): "HIVE",
                (0, "catalog.connection_config_info.warehouse"): "h",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.authentication_type",
                ): "IMPLICIT",
                (0, "catalog.connection_config_info.uri"): "u",
            },
        )
        check_arguments(
            mock_execute(
                [
                    "catalogs",
                    "create",
                    "my-catalog",
                    "--type",
                    "external",
                    "--storage-type",
                    "file",
                    "--default-base-location",
                    "dbl",
                    "--catalog-connection-type",
                    "hive",
                    "--hive-warehouse",
                    "/warehouse/path",
                    "--catalog-authentication-type",
                    "oauth",
                    "--catalog-uri",
                    "thrift://hive-metastore:9083",
                    "--catalog-token-uri",
                    "http://auth-server/token",
                    "--catalog-client-id",
                    "test-client",
                    "--catalog-client-secret",
                    "test-secret",
                    "--catalog-client-scope",
                    "read",
                    "--catalog-client-scope",
                    "write",
                ]
            ),
            "create_catalog",
            {
                (0, "catalog.name"): "my-catalog",
                (0, "catalog.type"): "EXTERNAL",
                (0, "catalog.connection_config_info.connection_type"): "HIVE",
                (0, "catalog.connection_config_info.warehouse"): "/warehouse/path",
                (
                    0,
                    "catalog.connection_config_info.authentication_parameters.authentication_type",
                ): "OAUTH",
                (
                    0,
                    "catalog.connection_config_info.uri",
                ): "thrift://hive-metastore:9083",
            },
        )

        check_arguments(
            mock_execute(
                [
                    "principals",
                    "reset",
                    "test",
                    "--new-client-id",
                    "e469c048cf866df1",
                    "--new-client-secret",
                    "e469c048cf866dfae469c048cf866df1",
                ]
            ),
            "reset_credentials",
            {
                (0, None): "test",
                (1, "client_id"): "e469c048cf866df1",
                (1, "client_secret"): "e469c048cf866dfae469c048cf866df1",
            },
        )

        check_arguments(
            mock_execute(["principals", "reset", "test"]),
            "reset_credentials",
            {
                (0, None): "test",
                (1, None): None,
            },
        )

        check_arguments(
            mock_execute(
                ["principals", "reset", "test", "--new-client-id", "e469c048cf866df1"]
            ),
            "reset_credentials",
            {
                (0, None): "test",
                (1, "client_id"): "e469c048cf866df1",
                (1, "client_secret"): None,
            },
        )

        check_arguments(
            mock_execute(
                [
                    "principals",
                    "reset",
                    "test",
                    "--new-client-secret",
                    "e469c048cf866dfae469c048cf866df1",
                ]
            ),
            "reset_credentials",
            {
                (0, None): "test",
                (1, "client_id"): None,
                (1, "client_secret"): "e469c048cf866dfae469c048cf866df1",
            },
        )

    def test_policies_attach_parameters_parsed_to_dict(self) -> None:
        options = Parser.parse(
            [
                "policies",
                "attach",
                "policy-name",
                "--catalog",
                "cat",
                "--attachment-type",
                "catalog",
                "--parameters",
                "key=value",
            ]
        )
        command = Command.from_options(options)
        command = cast(PoliciesCommand, command)
        self.assertIsInstance(command.parameters, dict)
        self.assertEqual({"key": "value"}, command.parameters)


if __name__ == "__main__":
    unittest.main()
