#  Copyright (c) ZenML GmbH 2021. 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.
#  You may obtain a copy of the License at:
#
#       https://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.
"""ZenML constants."""

import json
import logging
import os
from pathlib import PurePosixPath, PureWindowsPath
from typing import Any, List, Optional, Type, TypeVar

from zenml.enums import AuthScheme

T = TypeVar("T")


def handle_json_env_var(
    var: str,
    expected_type: Type[T],
    default: Optional[List[str]] = None,
) -> Any:
    """Converts a json env var into a Python object.

    Args:
        var:  The environment variable to convert.
        default: The default value to return if the env var is not set.
        expected_type: The type of the expected Python object.

    Returns:
        The converted list value.

    Raises:
        TypeError: In case the value of the environment variable is not of a
                   valid type.

    """
    # this needs to be here to avoid mutable defaults
    if default is None:
        default = []

    value = os.getenv(var)
    if value:
        try:
            loaded_value = json.loads(value)
            # check if loaded value is of correct type
            if expected_type is None or isinstance(
                loaded_value, expected_type
            ):
                return loaded_value
            else:
                raise TypeError  # if not correct type, raise TypeError
        except (TypeError, json.JSONDecodeError):
            # Use raw logging to avoid cyclic dependency
            logging.warning(
                f"Environment Variable {var} could not be loaded, into type "
                f"{expected_type}, defaulting to: {default}."
            )
            return default
    else:
        return default


def is_true_string_value(value: Any) -> bool:
    """Checks if the given value is a string representation of 'True'.

    Args:
        value: the value to check.

    Returns:
        Whether the input value represents a string version of 'True'.
    """
    return value in ["1", "y", "yes", "True", "true"]


def is_false_string_value(value: Any) -> bool:
    """Checks if the given value is a string representation of 'False'.

    Args:
        value: the value to check.

    Returns:
        Whether the input value represents a string version of 'False'.
    """
    return value in ["0", "n", "no", "False", "false"]


def validate_directory_name(name: str) -> str:
    """Validate a directory name.

    Args:
        name: The directory name to validate.

    Raises:
        ValueError: If the name is not a valid directory name.

    Returns:
        The validated directory name.
    """
    if not name:
        raise ValueError("Directory name cannot be empty.")

    if name in {".", ".."}:
        raise ValueError(
            "Directory name must not point to the current or parent directory."
        )

    if PurePosixPath(name).parts != (name,) or PureWindowsPath(name).parts != (
        name,
    ):
        raise ValueError(
            "Directory name must be a single path component without nested "
            "levels."
        )

    return name


def handle_bool_env_var(var: str, default: bool = False) -> bool:
    """Converts normal env var to boolean.

    Args:
        var: The environment variable to convert.
        default: The default value to return if the env var is not set.

    Returns:
        The converted value.
    """
    value = os.getenv(var)
    if is_true_string_value(value):
        return True
    elif is_false_string_value(value):
        return False
    return default


def handle_int_env_var(var: str, default: int = 0) -> int:
    """Converts normal env var to int.

    Args:
        var: The environment variable to convert.
        default: The default value to return if the env var is not set.

    Returns:
        The converted value.
    """
    value = os.getenv(var, "")
    try:
        return int(value)
    except (ValueError, TypeError):
        return default


def handle_float_env_var(var: str, default: float = 0.0) -> float:
    """Converts normal env var to float.

    Args:
        var: The environment variable to convert.
        default: The default value to return if the env var is not set.

    Returns:
        The converted value.
    """
    value = os.getenv(var, "")
    try:
        return float(value)
    except (ValueError, TypeError):
        return default


# Global constants
APP_NAME = "zenml"

# Environment variables
ENV_ZENML_LOGGING_COLORS_DISABLED = "ZENML_LOGGING_COLORS_DISABLED"
ENV_ZENML_ANALYTICS_OPT_IN = "ZENML_ANALYTICS_OPT_IN"
ENV_ZENML_USER_ID = "ZENML_USER_ID"
ENV_ZENML_CONFIG_PATH = "ZENML_CONFIG_PATH"
ENV_ZENML_DEBUG = "ZENML_DEBUG"
ENV_ZENML_LOGGING_VERBOSITY = "ZENML_LOGGING_VERBOSITY"
ENV_ZENML_LOGGING_FORMAT = "ZENML_LOGGING_FORMAT"
ENV_ZENML_REPOSITORY_PATH = "ZENML_REPOSITORY_PATH"
ENV_ZENML_REPOSITORY_DIRECTORY_NAME = "ZENML_REPOSITORY_DIRECTORY_NAME"
ENV_ZENML_ENABLE_RICH_TRACEBACK = "ZENML_ENABLE_RICH_TRACEBACK"
ENV_ZENML_ACTIVE_STACK_ID = "ZENML_ACTIVE_STACK_ID"
ENV_ZENML_ACTIVE_PROJECT_ID = "ZENML_ACTIVE_PROJECT_ID"
ENV_ZENML_SUPPRESS_LOGS = "ZENML_SUPPRESS_LOGS"
ENV_ZENML_ENABLE_REPO_INIT_WARNINGS = "ZENML_ENABLE_REPO_INIT_WARNINGS"
ENV_ZENML_SECRET_VALIDATION_LEVEL = "ZENML_SECRET_VALIDATION_LEVEL"
ENV_ZENML_DEFAULT_USER_NAME = "ZENML_DEFAULT_USER_NAME"
ENV_ZENML_DEFAULT_USER_PASSWORD = "ZENML_DEFAULT_USER_PASSWORD"
ENV_ZENML_DEFAULT_PROJECT_NAME = "ZENML_DEFAULT_PROJECT_NAME"
ENV_ZENML_STORE_PREFIX = "ZENML_STORE_"
ENV_ZENML_SECRETS_STORE_PREFIX = "ZENML_SECRETS_STORE_"
ENV_ZENML_BACKUP_SECRETS_STORE_PREFIX = "ZENML_BACKUP_SECRETS_STORE_"
ENV_AUTO_OPEN_DASHBOARD = "AUTO_OPEN_DASHBOARD"
ENV_ZENML_AUTO_OPEN_DASHBOARD = "ZENML_AUTO_OPEN_DASHBOARD"
ENV_ZENML_DISABLE_DATABASE_MIGRATION = "DISABLE_DATABASE_MIGRATION"
ENV_ZENML_LOCAL_STORES_PATH = "ZENML_LOCAL_STORES_PATH"
ENV_ZENML_CONTAINER = "ZENML_CONTAINER"
ENV_ZENML_CONTAINER_PYTHON_EXECUTABLE = "ZENML_CONTAINER_PYTHON_EXECUTABLE"
ENV_ZENML_PAGINATION_DEFAULT_LIMIT = "ZENML_PAGINATION_DEFAULT_LIMIT"
ENV_ZENML_DISABLE_CLIENT_SERVER_MISMATCH_WARNING = (
    "ZENML_DISABLE_CLIENT_SERVER_MISMATCH_WARNING"
)
ENV_ZENML_SKIP_IMAGE_BUILDER_DEFAULT = "ZENML_SKIP_IMAGE_BUILDER_DEFAULT"
ENV_ZENML_SKIP_STACK_VALIDATION = "ZENML_SKIP_STACK_VALIDATION"
ENV_ZENML_SERVER = "ZENML_SERVER"
ENV_ZENML_SERVER_ALLOW_LOCAL_FILE_ACCESS = (
    "ZENML_SERVER_ALLOW_LOCAL_FILE_ACCESS"
)
ENV_ZENML_ENFORCE_TYPE_ANNOTATIONS = "ZENML_ENFORCE_TYPE_ANNOTATIONS"
ENV_ZENML_ENABLE_IMPLICIT_AUTH_METHODS = "ZENML_ENABLE_IMPLICIT_AUTH_METHODS"
ENV_ZENML_DISABLE_INTERACTIVE_INPUT = "ZENML_DISABLE_INTERACTIVE_INPUT"
ENV_ZENML_DISABLE_PIPELINE_LOGS_STORAGE = "ZENML_DISABLE_PIPELINE_LOGS_STORAGE"
ENV_ZENML_DISABLE_STEP_LOGS_STORAGE = "ZENML_DISABLE_STEP_LOGS_STORAGE"
ENV_ZENML_DISABLE_STEP_NAMES_IN_LOGS = "ZENML_DISABLE_STEP_NAMES_IN_LOGS"
ENV_ZENML_STORAGE_LOGGING_VERBOSITY = "ZENML_STORAGE_LOGGING_VERBOSITY"
ENV_ZENML_CAPTURE_PRINTS = "ZENML_CAPTURE_PRINTS"
ENV_ZENML_LOGS_STORAGE_MAX_QUEUE_SIZE = "ZENML_LOGS_STORAGE_MAX_QUEUE_SIZE"
ENV_ZENML_LOGS_STORAGE_QUEUE_TIMEOUT = "ZENML_LOGS_STORAGE_QUEUE_TIMEOUT"
ENV_ZENML_LOGS_MAX_ENTRIES_PER_REQUEST = "ZENML_LOGS_MAX_ENTRIES_PER_REQUEST"
ENV_ZENML_LOGS_WRITE_INTERVAL_SECONDS = "ZENML_LOGS_WRITE_INTERVAL_SECONDS"
ENV_ZENML_LOGS_MERGE_INTERVAL_SECONDS = "ZENML_LOGS_MERGE_INTERVAL_SECONDS"

ENV_ZENML_CUSTOM_SOURCE_ROOT = "ZENML_CUSTOM_SOURCE_ROOT"
ENV_ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION = (
    "ZENML_PIPELINE_API_TOKEN_EXPIRATION"
)
ENV_ZENML_CODE_REPOSITORY_IGNORE_UNTRACKED_FILES = (
    "ZENML_CODE_REPOSITORY_IGNORE_UNTRACKED_FILES"
)
# Environment variable that indicates whether the current environment is running
# a step operator.
ENV_ZENML_STEP_OPERATOR = "ZENML_STEP_OPERATOR"

# Materializer environment variables
ENV_ZENML_MATERIALIZER_ALLOW_NON_ASCII_JSON_DUMPS = (
    "ZENML_MATERIALIZER_ALLOW_NON_ASCII_JSON_DUMPS"
)
ENV_ZENML_DISABLE_PATH_MATERIALIZER = "ZENML_DISABLE_PATH_MATERIALIZER"

# ZenML Server environment variables
ENV_ZENML_SERVER_PREFIX = "ZENML_SERVER_"
ENV_ZENML_SERVER_PRO_PREFIX = "ZENML_SERVER_PRO_"
ENV_ZENML_PRODUCER_PREFIX = "ZENML_PRODUCER_"
ENV_ZENML_CONSUMER_PREFIX = "ZENML_CONSUMER_"
ENV_ZENML_CONSUMER_POLLING_PREFIX = "ZENML_CONSUMER_POLLING_"
ENV_ZENML_SERVER_DEPLOYMENT_TYPE = f"{ENV_ZENML_SERVER_PREFIX}DEPLOYMENT_TYPE"
ENV_ZENML_SERVER_AUTH_SCHEME = f"{ENV_ZENML_SERVER_PREFIX}AUTH_SCHEME"
ENV_ZENML_SERVER_AUTO_ACTIVATE = f"{ENV_ZENML_SERVER_PREFIX}AUTO_ACTIVATE"
ENV_DAEMON_ZENML_SERVER_DEFAULT_TIMEOUT = "ZENML_DAEMON_SERVER_TIMEOUT"

ENV_ZENML_RUN_SINGLE_STEPS_WITHOUT_STACK = (
    "ZENML_RUN_SINGLE_STEPS_WITHOUT_STACK"
)
ENV_ZENML_PREVENT_CLIENT_SIDE_CACHING = "ZENML_PREVENT_CLIENT_SIDE_CACHING"
ENV_ZENML_DISABLE_CREDENTIALS_DISK_CACHING = "DISABLE_CREDENTIALS_DISK_CACHING"
ENV_ZENML_RUNNER_PARENT_IMAGE = "ZENML_RUNNER_PARENT_IMAGE"
ENV_ZENML_RUNNER_IMAGE_DISABLE_UV = "ZENML_RUNNER_IMAGE_DISABLE_UV"
ENV_ZENML_RUNNER_POD_TIMEOUT = "ZENML_RUNNER_POD_TIMEOUT"
ENV_ZENML_WORKLOAD_TOKEN_EXPIRATION_LEEWAY = (
    "ZENML_WORKLOAD_TOKEN_EXPIRATION_LEEWAY"
)

ENV_ZENML_DEFAULT_OUTPUT = "ZENML_DEFAULT_OUTPUT"
ENV_ZENML_DEFAULT_ANALYTICS_SOURCE = "ZENML_DEFAULT_ANALYTICS_SOURCE"
ENV_ZENML_CLI_COLUMN_WIDTH = "ZENML_CLI_COLUMN_WIDTH"

ENV_ZENML_DYNAMIC_PIPELINE_WORKER_COUNT = "ZENML_DYNAMIC_PIPELINE_WORKER_COUNT"
ENV_ZENML_DYNAMIC_PIPELINE_MONITORING_INTERVAL = (
    "ZENML_DYNAMIC_PIPELINE_MONITORING_INTERVAL"
)
ENV_ZENML_DYNAMIC_PIPELINE_MONITORING_DELAY = (
    "ZENML_DYNAMIC_PIPELINE_MONITORING_DELAY"
)
ENV_ZENML_STEP_RUNS_FETCH_MAX_CHUNK_LENGTH = (
    "ZENML_STEP_RUNS_FETCH_MAX_CHUNK_LENGTH"
)

# Logging variables
IS_DEBUG_ENV: bool = handle_bool_env_var(ENV_ZENML_DEBUG, default=False)

if IS_DEBUG_ENV:
    ZENML_LOGGING_VERBOSITY = os.getenv(
        ENV_ZENML_LOGGING_VERBOSITY, default="DEBUG"
    ).upper()
    ZENML_STORAGE_LOGGING_VERBOSITY = os.getenv(
        ENV_ZENML_STORAGE_LOGGING_VERBOSITY, default=None
    )
else:
    ZENML_LOGGING_VERBOSITY = os.getenv(
        ENV_ZENML_LOGGING_VERBOSITY, default="INFO"
    ).upper()
    ZENML_STORAGE_LOGGING_VERBOSITY = os.getenv(
        ENV_ZENML_STORAGE_LOGGING_VERBOSITY, default=None
    )

INSIDE_ZENML_CONTAINER = handle_bool_env_var(ENV_ZENML_CONTAINER, False)

# Analytics constants
VALID_OPERATING_SYSTEMS = ["Windows", "Darwin", "Linux"]

# Path utilities constants
REMOTE_FS_PREFIX = ["gs://", "hdfs://", "s3://", "az://", "abfs://"]

# ZenML Analytics Server - URL
ANALYTICS_SERVER_URL = "https://analytics.zenml.io/"

# Repository and local store directory paths:
REPOSITORY_DIRECTORY_NAME = validate_directory_name(
    os.getenv(ENV_ZENML_REPOSITORY_DIRECTORY_NAME, ".zen")
)
LOCAL_STORES_DIRECTORY_NAME = "local_stores"

# Config file name
CONFIG_FILE_NAME = "config.yaml"

# Default store directory subpath:
DEFAULT_STORE_DIRECTORY_NAME = "default_zen_store"

# SQL Store backup directory subpath:
SQL_STORE_BACKUP_DIRECTORY_NAME = "database_backup"

DEFAULT_USERNAME = "default"
DEFAULT_PASSWORD = ""
DEFAULT_PROJECT_NAME = "default"
DEFAULT_STACK_AND_COMPONENT_NAME = "default"

# Rich config
ENABLE_RICH_TRACEBACK = handle_bool_env_var(
    ENV_ZENML_ENABLE_RICH_TRACEBACK, True
)

DISABLE_CLIENT_SERVER_MISMATCH_WARNING = handle_bool_env_var(
    ENV_ZENML_DISABLE_CLIENT_SERVER_MISMATCH_WARNING, default=False
)

ENFORCE_TYPE_ANNOTATIONS = handle_bool_env_var(
    ENV_ZENML_ENFORCE_TYPE_ANNOTATIONS, default=False
)

# Services
DEFAULT_SERVICE_START_STOP_TIMEOUT = 60
DEFAULT_LOCAL_SERVICE_IP_ADDRESS = "127.0.0.1"
ZEN_SERVER_ENTRYPOINT = "zenml.zen_server.zen_server_api:app"

CODE_HASH_PARAMETER_NAME = "step_source"

# Server settings
DEFAULT_ZENML_SERVER_NAME = "default"
DEFAULT_ZENML_SERVER_THREAD_POOL_SIZE = 40
DEFAULT_ZENML_SERVER_AUTH_THREAD_POOL_SIZE = 5
DEFAULT_ZENML_JWT_TOKEN_LEEWAY = 10
DEFAULT_ZENML_JWT_TOKEN_ALGORITHM = "HS256"
DEFAULT_ZENML_AUTH_SCHEME = AuthScheme.OAUTH2_PASSWORD_BEARER
EXTERNAL_AUTHENTICATOR_TIMEOUT = 10  # seconds
DEFAULT_ZENML_SERVER_MAX_DEVICE_AUTH_ATTEMPTS = 3
DEFAULT_ZENML_SERVER_DEVICE_AUTH_TIMEOUT = 60 * 5  # 5 minutes
DEFAULT_ZENML_SERVER_DEVICE_AUTH_POLLING = 5  # seconds
DEFAULT_HTTP_TIMEOUT = 30
DEFAULT_ZENML_SERVER_REQUEST_TIMEOUT = 20  # seconds
DEFAULT_ZENML_SERVER_REQUEST_CACHE_TIMEOUT = 300  # seconds
SERVICE_CONNECTOR_VERIFY_REQUEST_TIMEOUT = 120  # seconds
ZENML_API_KEY_PREFIX = "ZENKEY_"
ZENML_PRO_API_KEY_PREFIX = "ZENPROKEY_"
DEFAULT_ZENML_SERVER_PIPELINE_RUN_AUTH_WINDOW = 60 * 48  # 48 hours
DEFAULT_ZENML_SERVER_LOGIN_RATE_LIMIT_MINUTE = 5
DEFAULT_ZENML_SERVER_LOGIN_RATE_LIMIT_DAY = 1000
DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME = 60 * 60  # 1 hour
DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_MAX_LIFETIME = (
    60 * 60 * 24 * 7
)  # 7 days
DEFAULT_ZENML_SERVER_FILE_DOWNLOAD_SIZE_LIMIT = 2 * 1024 * 1024 * 1024  # 20 GB
DEFAULT_ZENML_SERVER_MAX_CONCURRENT_SNAPSHOT_RUNS = 2

DEFAULT_ZENML_SERVER_API_TXN_CLEANUP_INTERVAL = 15  # seconds

DEFAULT_ZENML_SERVER_SECURE_HEADERS_HSTS = (
    "max-age=63072000; includeSubdomains"
)
DEFAULT_ZENML_SERVER_SECURE_HEADERS_XFO = "SAMEORIGIN"
DEFAULT_ZENML_SERVER_SECURE_HEADERS_CONTENT = "nosniff"
_csp_script_src_urls = [
    "https://widgets-v3.featureos.app",
    "https://static.reo.dev",
]
_csp_connect_src_urls = [
    # We need a wildcard here to allow the dashboard deployment playground
    # to send requests to deployments on any URL.
    "*",
]
_csp_img_src_urls = [
    "*"
    # "https://public-flavor-logos.s3.eu-central-1.amazonaws.com",
    # "https://avatar.vercel.sh",
]
_csp_frame_src_urls = [
    "https://zenml.hellonext.co",
    "https://sdkdocs.zenml.io",
    "https://widgets-v3.hellonext.co",
    "https://widgets-v3.featureos.app",
    "https://zenml.portal.trainn.co",
]
DEFAULT_ZENML_SERVER_SECURE_HEADERS_CSP = (
    "default-src 'none'; "
    f"script-src 'self' 'unsafe-inline' 'unsafe-eval' {' '.join(_csp_script_src_urls)}; "
    f"connect-src 'self' {' '.join(_csp_connect_src_urls)}; "
    f"img-src 'self' data: {' '.join(_csp_img_src_urls)}; "
    "style-src 'self' 'unsafe-inline'; "
    "base-uri 'self'; "
    "form-action 'self'; "
    "font-src 'self';"
    f"frame-src {' '.join(_csp_frame_src_urls)}"
)
DEFAULT_ZENML_SERVER_SECURE_HEADERS_REFERRER = "no-referrer-when-downgrade"
DEFAULT_ZENML_SERVER_SECURE_HEADERS_CACHE = (
    "no-store, no-cache, must-revalidate"
)
DEFAULT_ZENML_SERVER_SECURE_HEADERS_PERMISSIONS = (
    "accelerometer=(), autoplay=(), camera=(), encrypted-media=(), "
    "geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), "
    "payment=(), sync-xhr=(), usb=()"
)
DEFAULT_ZENML_SERVER_SECURE_HEADERS_REPORT_TO = "default"
DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS = 30
DEFAULT_ZENML_SERVER_MAX_REQUEST_BODY_SIZE_IN_BYTES = 256 * 1024 * 1024

DEFAULT_REPORTABLE_RESOURCES = ["project", "pipeline", "pipeline_run", "model"]
RUN_TEMPLATE_TRIGGERS_FEATURE_NAME = "template_run"

# API Endpoint paths:
ACTIVATE = "/activate"
API = "/api"
API_KEYS = "/api_keys"
API_KEY_ROTATE = "/rotate"
API_TOKEN = "/api_token"
ARTIFACTS = "/artifacts"
ARTIFACT_VERSIONS = "/artifact_versions"
ARTIFACT_VISUALIZATIONS = "/artifact_visualizations"
AUTH = "/auth"
BATCH = "/batch"
CODE = "/code"
CODE_REFERENCES = "/code_references"
CODE_REPOSITORIES = "/code_repositories"
COMPONENT_TYPES = "/component-types"
CONFIG = "/config"
CURRENT_USER = "/current-user"
DATA = "/data"
DEACTIVATE = "/deactivate"
DEVICES = "/devices"
DEVICE_AUTHORIZATION = "/device_authorization"
DEVICE_VERIFY = "/verify"
DISABLE_HEARTBEAT = "/disable_heartbeat"
DOWNLOAD_TOKEN = "/download-token"
EMAIL_ANALYTICS = "/email-opt-in"
EVENT_FLAVORS = "/event-flavors"
FLAVORS = "/flavors"
HEALTH = "/health"
READY = "/ready"
INFO = "/info"
LOAD_INFO = "/load-info"
LOGIN = "/login"
LOGOUT = "/logout"
LOGS = "/logs"
PIPELINE_BUILDS = "/pipeline_builds"
PIPELINE_CONFIGURATION = "/pipeline-configuration"
PIPELINE_DEPLOYMENTS = "/pipeline_deployments"
DEPLOYMENTS = "/deployments"
CURATED_VISUALIZATIONS = "/curated_visualizations"
PIPELINE_SNAPSHOTS = "/pipeline_snapshots"
PIPELINES = "/pipelines"
PIPELINE_SPEC = "/pipeline-spec"
PROJECTS = "/projects"
REFRESH = "/refresh"
RESOLVE = "/resolve"
RUNS = "/runs"
RUN_TEMPLATES = "/run_templates"
RUN_METADATA = "/run-metadata"
RUN_WAIT_CONDITIONS = "/run_wait_conditions"
SCHEDULES = "/schedules"
SECRETS = "/secrets"
SECRETS_OPERATIONS = "/secrets_operations"
SECRETS_BACKUP = "/backup"
SECRETS_RESTORE = "/restore"
SERVER_SETTINGS = "/settings"
SERVICE_ACCOUNTS = "/service_accounts"
SERVICE_CONNECTOR_CLIENT = "/client"
SERVICE_CONNECTOR_RESOURCES = "/resources"
SERVICE_CONNECTOR_TYPES = "/service_connector_types"
SERVICE_CONNECTOR_VERIFY = "/verify"
SERVICE_CONNECTOR_FULL_STACK = "/full_stack_resources"
MODELS = "/models"
MODEL_VERSIONS = "/model_versions"
MODEL_VERSION_ARTIFACTS = "/model_version_artifacts"
MODEL_VERSION_PIPELINE_RUNS = "/model_version_pipeline_runs"
SERVICES = "/services"
SERVICE_CONNECTORS = "/service_connectors"
STACK = "/stack"
STACK_DEPLOYMENT = "/stack-deployment"
STACKS = "/stacks"
STACK_COMPONENTS = "/components"
STATISTICS = "/statistics"
STATUS = "/status"
STEP_CONFIGURATION = "/step-configuration"
STEPS = "/steps"
HEARTBEAT = "/heartbeat"
STOP = "/stop"
TAGS = "/tags"
TAG_RESOURCES = "/tag_resources"
TRIGGERS = "/triggers"
ONBOARDING_STATE = "/onboarding_state"
USERS = "/users"
URL = "/url"
VERSION_1 = "/v1"
VISUALIZE = "/visualize"
WEBHOOKS = "/webhooks"
WORKSPACES = "/workspaces"
PROJECTS = "/projects"

# model metadata yaml file name
MODEL_METADATA_YAML_FILE_NAME = "model_metadata.yaml"

# orchestrator constants
ORCHESTRATOR_DOCKER_IMAGE_KEY = "orchestrator"
ENV_ZENML_DAG_RUNNER_WORKER_COUNT = "ZENML_DAG_RUNNER_WORKER_COUNT"

# deployer constants
DEPLOYER_DOCKER_IMAGE_KEY = "deployer"
IN_MEMORY_ARTIFACT_URI_PREFIX = "memory://"

# Secret constants
SECRET_VALUES = "values"

# Pagination and filtering defaults
PAGINATION_STARTING_PAGE: int = 1
PAGE_SIZE_DEFAULT: int = handle_int_env_var(
    ENV_ZENML_PAGINATION_DEFAULT_LIMIT, default=20
)
PAGE_SIZE_MAXIMUM: int = handle_int_env_var(
    ENV_ZENML_PAGINATION_DEFAULT_LIMIT, default=10000
)
FILTERING_DATETIME_FORMAT: str = "%Y-%m-%d %H:%M:%S"
SORT_PIPELINES_BY_LATEST_RUN_KEY = "latest_run"
SORT_BY_LATEST_VERSION_KEY = "latest_version"

# Metadata constants
METADATA_ORCHESTRATOR_URL = "orchestrator_url"
METADATA_ORCHESTRATOR_LOGS_URL = "orchestrator_logs_url"
METADATA_ORCHESTRATOR_RUN_ID = "orchestrator_run_id"
METADATA_EXPERIMENT_TRACKER_URL = "experiment_tracker_url"
METADATA_DEPLOYED_MODEL_URL = "deployed_model_url"

# Model registries constants
MLFLOW_MODEL_FORMAT = "MLflow"

# Service connector constants
DOCKER_REGISTRY_RESOURCE_TYPE = "docker-registry"
KUBERNETES_CLUSTER_RESOURCE_TYPE = "kubernetes-cluster"

# Stack Recipe constants
STACK_RECIPES_GITHUB_REPO = "https://github.com/zenml-io/mlops-stacks.git"

# Parameters for internal ZenML Models
TEXT_FIELD_MAX_LENGTH = 65535
STR_ID_FIELD_MAX_LENGTH = 50
STR_FIELD_MAX_LENGTH = 255
MEDIUMTEXT_MAX_LENGTH = 2**24 - 1

# Model Control Plane constants
LATEST_MODEL_VERSION_PLACEHOLDER = "__latest__"


# Service connector constants
SERVICE_CONNECTOR_SKEW_TOLERANCE_SECONDS = 60 * 5  # 5 minutes

# Versioned entities
MAX_RETRIES_FOR_VERSIONED_ENTITY_CREATION = (
    10  # empirical value to pass heavy parallelized tests
)


FINISHED_ONBOARDING_SURVEY_KEY = "finished_onboarding_survey"

# Name validation
BANNED_NAME_CHARACTERS = "\t\n\r\v\f"


STACK_DEPLOYMENT_API_TOKEN_EXPIRATION = 60 * 6  # 6 hours

ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION = handle_int_env_var(
    ENV_ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION, default=0
)

# Logs constants
# Maximum number of log batches to queue.
LOGS_STORAGE_MAX_QUEUE_SIZE = handle_int_env_var(
    ENV_ZENML_LOGS_STORAGE_MAX_QUEUE_SIZE, default=100000
)

LOGS_MAX_ENTRIES_PER_REQUEST = handle_int_env_var(
    ENV_ZENML_LOGS_MAX_ENTRIES_PER_REQUEST, default=50000
)

# Queue timeout controls log dropping vs application blocking:
# - Positive value (e.g., 30): Wait N seconds, then drop logs if queue full
# - Negative value (e.g., -1): Block indefinitely until queue has space (never drop logs)
LOGS_STORAGE_QUEUE_TIMEOUT = handle_int_env_var(
    ENV_ZENML_LOGS_STORAGE_QUEUE_TIMEOUT, default=-1
)
# Log batch-write and merge windows in seconds.
LOGS_WRITE_INTERVAL_SECONDS = handle_int_env_var(
    ENV_ZENML_LOGS_WRITE_INTERVAL_SECONDS, default=1
)
LOGS_MERGE_INTERVAL_SECONDS = handle_int_env_var(
    ENV_ZENML_LOGS_MERGE_INTERVAL_SECONDS, default=10 * 60
)

# OpenTelemetry log storage constants
ENV_ZENML_LOGS_OTEL_MAX_QUEUE_SIZE = "ZENML_LOGS_OTEL_MAX_QUEUE_SIZE"
ENV_ZENML_LOGS_OTEL_SCHEDULE_DELAY_MILLIS = (
    "ZENML_LOGS_OTEL_SCHEDULE_DELAY_MILLIS"
)
ENV_ZENML_LOGS_OTEL_MAX_EXPORT_BATCH_SIZE = (
    "ZENML_LOGS_OTEL_MAX_EXPORT_BATCH_SIZE"
)
ENV_ZENML_LOGS_OTEL_EXPORT_TIMEOUT_MILLIS = (
    "ZENML_LOGS_OTEL_EXPORT_TIMEOUT_MILLIS"
)

LOGS_OTEL_MAX_QUEUE_SIZE = handle_int_env_var(
    ENV_ZENML_LOGS_OTEL_MAX_QUEUE_SIZE, default=100000
)
LOGS_OTEL_SCHEDULE_DELAY_MILLIS = handle_int_env_var(
    ENV_ZENML_LOGS_OTEL_SCHEDULE_DELAY_MILLIS, default=5000
)
LOGS_OTEL_MAX_EXPORT_BATCH_SIZE = handle_int_env_var(
    ENV_ZENML_LOGS_OTEL_MAX_EXPORT_BATCH_SIZE, default=5000
)
LOGS_OTEL_EXPORT_TIMEOUT_MILLIS = handle_int_env_var(
    ENV_ZENML_LOGS_OTEL_EXPORT_TIMEOUT_MILLIS, default=15000
)

# Subscription-based features
SCHEDULE_FEATURE = "schedule"

LOGS_RUNNER_SOURCE = "runner"
