#
# 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 os
from enum import Enum


class StorageType(Enum):
    """
    Represents a Storage Type within the Polaris API -- `s3`, `azure`, `gcs`, or `file`.
    """

    S3 = "s3"
    AZURE = "azure"
    GCS = "gcs"
    FILE = "file"


class CatalogType(Enum):
    """
    Represents a Catalog Type within the Polaris API -- `internal` or `external`
    """

    INTERNAL = "internal"
    EXTERNAL = "external"


class PrincipalType(Enum):
    """
    Represents a Principal Type within the Polaris API -- currently only `service`
    """

    SERVICE = "service"


class CatalogConnectionType(Enum):
    """
    Represents a ConnectionType for an EXTERNAL catalog -- see ConnectionConfigInfo in the spec
    """

    HADOOP = "hadoop"
    ICEBERG = "iceberg-rest"
    HIVE = "hive"


class AuthenticationType(Enum):
    """
    Represents a AuthenticationType for an EXTERNAL catalog -- see AuthenticationParameters in the spec
    """

    OAUTH = "oauth"
    BEARER = "bearer"
    SIGV4 = "sigv4"
    IMPLICIT = "implicit"


class ServiceIdentityType(Enum):
    """
    Represents a Service Identity Type for an EXTERNAL catalog -- see ServiceIdentityInfo in the spec
    """

    AWS_IAM = "aws_iam"


class Commands:
    """
    Represents the various commands available in the CLI
    """

    CATALOGS = "catalogs"
    PRINCIPALS = "principals"
    PRINCIPAL_ROLES = "principal-roles"
    CATALOG_ROLES = "catalog-roles"
    PRIVILEGES = "privileges"
    NAMESPACES = "namespaces"
    PROFILES = "profiles"
    POLICIES = "policies"
    SETUP = "setup"


class Subcommands:
    """
    Represents the various subcommands available in the CLI. This is a flattened view, and no one command supports
    all these subcommands.
    """

    CREATE = "create"
    DELETE = "delete"
    GET = "get"
    LIST = "list"
    UPDATE = "update"
    ROTATE_CREDENTIALS = "rotate-credentials"
    CATALOG = "catalog"
    NAMESPACE = "namespace"
    TABLE = "table"
    VIEW = "view"
    GRANT = "grant"
    REVOKE = "revoke"
    ACCESS = "access"
    RESET = "reset"
    ATTACH = "attach"
    DETACH = "detach"
    APPLY = "apply"
    EXPORT = "export"


class Actions:
    """
    Represents actions available to different subcommands available in the CLI. Currently, only some subcommands of the
    `privileges` command support actions.
    """

    GRANT = "grant"
    REVOKE = "revoke"


class Arguments:
    """
    Constants to represent different arguments used by various commands. This is a flattened view, and no one
    subcommand supports all these arguments. These argument names map directly to the parameters that the CLI expects
    and to the attribute names within the argparse Namespace generated by parsing.

    These values should be snake_case, but they will get mapped to kebab-case in `Parser.parse`
    """

    TYPE = "type"
    DEFAULT_BASE_LOCATION = "default_base_location"
    STORAGE_TYPE = "storage_type"
    ALLOWED_LOCATION = "allowed_location"
    ROLE_ARN = "role_arn"
    EXTERNAL_ID = "external_id"
    USER_ARN = "user_arn"
    TENANT_ID = "tenant_id"
    MULTI_TENANT_APP_NAME = "multi_tenant_app_name"
    HIERARCHICAL = "hierarchical"
    CONSENT_URL = "consent_url"
    SERVICE_ACCOUNT = "service_account"
    CATALOG_ROLE = "catalog_role"
    CATALOG = "catalog"
    PRINCIPAL = "principal"
    CLIENT_ID = "client_id"
    PRINCIPAL_ROLE = "principal_role"
    PROPERTY = "property"
    SET_PROPERTY = "set_property"
    REMOVE_PROPERTY = "remove_property"
    PRIVILEGE = "privilege"
    NAMESPACE = "namespace"
    TABLE = "table"
    VIEW = "view"
    CASCADE = "cascade"
    CLIENT_SECRET = "client_secret"
    NEW_CLIENT_ID = "new_client_id"
    NEW_CLIENT_SECRET = "new_client_secret"
    ACCESS_TOKEN = "access_token"
    HOST = "host"
    PORT = "port"
    BASE_URL = "base_url"
    PARENT = "parent"
    LOCATION = "location"
    REGION = "region"
    PROFILE = "profile"
    PROXY = "proxy"
    DEBUG = "debug"
    HADOOP_WAREHOUSE = "hadoop_warehouse"
    HIVE_WAREHOUSE = "hive_warehouse"
    ICEBERG_REMOTE_CATALOG_NAME = "iceberg_remote_catalog_name"
    ENDPOINT = "endpoint"
    ENDPOINT_INTERNAL = "endpoint_internal"
    STS_ENDPOINT = "sts_endpoint"
    STS_UNAVAILABLE = "no_sts"
    KMS_UNAVAILABLE = "no_kms"
    KMS_KEY_CURRENT = "current_kms_key"
    KMS_KEY_ALLOWED = "allowed_kms_key"
    PATH_STYLE_ACCESS = "path_style_access"
    CATALOG_CONNECTION_TYPE = "catalog_connection_type"
    CATALOG_AUTHENTICATION_TYPE = "catalog_authentication_type"
    CATALOG_SERVICE_IDENTITY_TYPE = "catalog_service_identity_type"
    CATALOG_SERVICE_IDENTITY_IAM_ARN = "catalog_service_identity_iam_arn"
    CATALOG_URI = "catalog_uri"
    CATALOG_TOKEN_URI = "catalog_token_uri"
    CATALOG_CLIENT_ID = "catalog_client_id"
    CATALOG_CLIENT_SECRET = "catalog_client_secret"
    CATALOG_CLIENT_SCOPE = "catalog_client_scope"
    CATALOG_BEARER_TOKEN = "catalog_bearer_token"
    CATALOG_ROLE_ARN = "catalog_role_arn"
    CATALOG_ROLE_SESSION_NAME = "catalog_role_session_name"
    CATALOG_EXTERNAL_ID = "catalog_external_id"
    CATALOG_SIGNING_REGION = "catalog_signing_region"
    CATALOG_SIGNING_NAME = "catalog_signing_name"
    POLICY = "policy"
    POLICY_FILE = "policy_file"
    POLICY_TYPE = "policy_type"
    POLICY_DESCRIPTION = "policy_description"
    TARGET_NAME = "target_name"
    PARAMETERS = "parameters"
    DETACH_ALL = "detach_all"
    APPLICABLE = "applicable"
    ATTACHMENT_TYPE = "attachment_type"
    ATTACHMENT_PATH = "attachment_path"
    SETUP_CONFIG = "setup_config"
    DRY_RUN = "dry_run"
    REALM = "realm"
    HEADER = "header"


class Hints:
    """
    Constants used as hints by the various --help outputs. These are arranged within subclasses for readability, but
    there is no strict mapping between these subclasses and commands. For example, the hint for the `--catalog`
    parameter used by `catalog-roles create` and `catalog-roles delete` may be the same.
    """

    PROPERTY = (
        "A key/value pair such as: tag=value. Multiple can be provided by specifying this option"
        " more than once"
    )
    SET_PROPERTY = (
        "A key/value pair such as: tag=value. Merges the specified key/value into an existing"
        " properties map by updating the value if the key already exists or creating a new"
        " entry if not. Multiple can be provided by specifying this option more than once"
    )
    REMOVE_PROPERTY = (
        "A key to remove from a properties map. If the key already does not exist then"
        " no action is takn for the specified key. If properties are also being set in"
        " the same update command then the list of removals is applied last. Multiple"
        " can be provided by specifying this option more than once"
    )

    class Setup:
        DRY_RUN = "If specified, the command will only print the actions to be taken without executing them."

    class Catalogs:
        GRANT = "Grant a catalog role to a catalog"
        REVOKE = "Revoke a catalog role from a catalog"

        class Create:
            TYPE = "The type of catalog to create in [INTERNAL, EXTERNAL]. INTERNAL by default."
            DEFAULT_BASE_LOCATION = "(Required) Default base location of the catalog"
            STORAGE_TYPE = "(Required) The type of storage to use for the catalog"
            ALLOWED_LOCATION = (
                "An allowed location for files tracked by the catalog. "
                "Multiple locations can be provided by specifying this option more than once."
            )

            ROLE_ARN = "(Only for AWS S3) A role ARN to use when connecting to S3"
            EXTERNAL_ID = "(Only for S3) The external ID to use when connecting to S3"
            REGION = "(Only for S3) The region to use when connecting to S3"
            USER_ARN = "(Only for S3) A user ARN to use when connecting to S3"
            ENDPOINT = "(Only for S3) The S3 endpoint to use when connecting to S3"
            ENDPOINT_INTERNAL = "(Only for S3) The S3 endpoint used by Polaris to use when connecting to S3, if different from the one that clients use"
            STS_ENDPOINT = (
                "(Only for S3) The STS endpoint to use when connecting to STS"
            )
            STS_UNAVAILABLE = "(Only for S3) Indicates that Polaris should not use STS (e.g. if STS is not available)"
            KMS_UNAVAILABLE = "(Only for S3) Indicates that Polaris should not use KMS (e.g. if KMS is not available)"
            PATH_STYLE_ACCESS = "(Only for S3) Whether to use path-style-access for S3"
            KMS_KEY_CURRENT = "(Only for AWS S3) The AWS KMS key ARN to be used for encrypting new S3 data"
            KMS_KEY_ALLOWED = "(Only for AWS S3) AWS KMS key ARN(s) that this catalog and its clients are allowed to use for reading S3 data (zero or more)"

            TENANT_ID = "(Required for Azure) A tenant ID to use when connecting to Azure Storage"
            MULTI_TENANT_APP_NAME = (
                "(Only for Azure) The app name to use when connecting to Azure Storage"
            )
            HIERARCHICAL = "(Only for Azure) Indicates whether the referenced Azure storage location(s) support hierarchical namespaces"
            CONSENT_URL = "(Only for Azure) A consent URL granting permissions for the Azure Storage location"

            SERVICE_ACCOUNT = (
                "(Only for GCS) The service account to use when connecting to GCS"
            )

        class Update:
            DEFAULT_BASE_LOCATION = "A new default base location for the catalog"

        class External:
            CATALOG_CONNECTION_TYPE = (
                "The type of external catalog in [ICEBERG-REST, HADOOP, HIVE]."
            )
            CATALOG_AUTHENTICATION_TYPE = (
                "The type of authentication in [OAUTH, BEARER, SIGV4, IMPLICIT]"
            )
            CATALOG_SERVICE_IDENTITY_TYPE = "The type of service identity in [AWS_IAM]"

            CATALOG_SERVICE_IDENTITY_IAM_ARN = (
                "When using the AWS_IAM service identity type, this is the ARN "
                "of the IAM user or IAM role Polaris uses to assume roles and "
                "then access external resources."
            )

            CATALOG_URI = "The URI of the external catalog"
            HADOOP_WAREHOUSE = (
                "The warehouse to use when federating to a HADOOP catalog"
            )
            HIVE_WAREHOUSE = "The warehouse to use when federating to a HIVE catalog"
            ICEBERG_REMOTE_CATALOG_NAME = (
                "The remote catalog name when federating to an Iceberg REST catalog"
            )

            CATALOG_TOKEN_URI = "(For authentication type OAUTH) Token server URI"
            CATALOG_CLIENT_ID = "(For authentication type OAUTH) oauth client id"
            CATALOG_CLIENT_SECRET = (
                "(For authentication type OAUTH) oauth client secret (input-only)"
            )
            CATALOG_CLIENT_SCOPE = (
                "(For authentication type OAUTH) oauth scopes to specify when exchanging "
                "for a short-lived access token. Multiple can be provided by specifying"
                " this option more than once"
            )

            CATALOG_BEARER_TOKEN = (
                "(For authentication type BEARER) Bearer token (input-only)"
            )

            CATALOG_ROLE_ARN = (
                "(For authentication type SIGV4) The aws IAM role arn assumed by polaris "
                "userArn when signing requests"
            )
            CATALOG_ROLE_SESSION_NAME = (
                "(For authentication type SIGV4) The role session name to be used "
                "by the SigV4 protocol for signing requests"
            )
            CATALOG_EXTERNAL_ID = (
                "(For authentication type SIGV4) An optional external id used to establish "
                "a trust relationship with AWS in the trust policy"
            )
            CATALOG_SIGNING_REGION = (
                "(For authentication type SIGV4) Region to be used by the SigV4 protocol "
                "for signing requests"
            )
            CATALOG_SIGNING_NAME = (
                "(For authentication type SIGV4) The service name to be used by the SigV4 "
                'protocol for signing requests, the default signing name is "execute-api" '
                "is if not provided"
            )

    class Principals:
        class Create:
            TYPE = "The type of principal to create in [SERVICE]"
            NAME = "The principal name"
            CLIENT_ID = "The output-only OAuth clientId associated with this principal if applicable"

        class Revoke:
            PRINCIPAL_ROLE = "A principal role to revoke from this principal"

        class Reset:
            CLIENT_ID = "The new client ID for the principal"
            CLIENT_SECRET = "The new client secret for the principal"

    class PrincipalRoles:
        PRINCIPAL_ROLE = "The name of a principal role"
        LIST = (
            "List principal roles, optionally limited to those held a given principal"
        )

        GRANT = "Grant a principal role to a principal"
        REVOKE = "Revoke a principal role from a principal"

        class Grant:
            PRINCIPAL = "A principal to grant this principal role to"

        class Revoke:
            PRINCIPAL = "A principal to revoke this principal role from"

        class List:
            CATALOG_ROLE = (
                "The name of a catalog role. If provided, show only principal roles assigned to this"
                " catalog role."
            )
            PRINCIPAL_NAME = (
                "The name of a principal. If provided, show only principal roles assigned to this"
                " principal."
            )

    class CatalogRoles:
        CATALOG_NAME = "The name of an existing catalog"
        CATALOG_ROLE = "The name of a catalog role"
        LIST = (
            "List catalog roles within a catalog. Optionally, specify a principal role."
        )
        REVOKE_CATALOG_ROLE = "Revoke a catalog role from a principal role"
        GRANT_CATALOG_ROLE = "Grant a catalog role to a principal role"

    class Grant:
        CATALOG_NAME = "The name of a catalog"
        CATALOG_ROLE = "The name of a catalog role"
        PRIVILEGE = "The privilege to grant or revoke"
        NAMESPACE = "A period-delimited namespace"
        TABLE = "The name of a table"
        VIEW = "The name of a view"
        CASCADE = "When revoking privileges, additionally revoke privileges that depend on the specified privilege"

    class Namespaces:
        LOCATION = "If specified, the location at which to store the namespace and entities inside it"
        PARENT = "If specified, list namespaces inside this parent namespace"

    class Policies:
        POLICY = "The name of a policy"
        POLICY_FILE = "The path to a JSON file containing the policy definition"
        POLICY_TYPE = "The type of the policy, e.g., 'system.data-compaction'"
        POLICY_DESCRIPTION = "An optional description for the policy."
        TARGET_NAME = (
            "The name of the target entity (e.g., table name, namespace name)."
        )
        PARAMETERS = "Optional key-value pairs for the attachment/detachment, e.g., key=value. Can be specified multiple times."
        DETACH_ALL = "When set to true, the policy will be deleted along with all its attached mappings."
        APPLICABLE = "When set, lists policies applicable to the target entity (considering inheritance) instead of policies defined directly in the target."
        ATTACHMENT_TYPE = "The type of entity to attach the policy to, e.g., 'catalog', 'namespace', or table-like."
        ATTACHMENT_PATH = "The path of the entity to attach the policy to, e.g., 'ns1.tb1'. Not required for catalog-level attachment."


UNIT_SEPARATOR = chr(0x1F)
CLIENT_ID_ENV = "CLIENT_ID"
CLIENT_SECRET_ENV = "CLIENT_SECRET"
CLIENT_PROFILE_ENV = "CLIENT_PROFILE"
REALM_ENV = "REALM"
HEADER_ENV = "HEADER"
DEFAULT_HEADER = "Polaris-Realm"
DEFAULT_HOSTNAME = "localhost"
DEFAULT_PORT = 8181
CONFIG_DIR = (os.environ.get("POLARIS_HOME") or "").strip() or os.path.expanduser(
    "~/.polaris"
)
CONFIG_FILE = os.path.join(CONFIG_DIR, ".polaris.json")
