#!/usr/bin/env bash

# Common variables
SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE[0]});pwd)
export PROJECT_ROOT=$(cd ${SCRIPT_DIR}/../..;pwd)
export ASSET_DIR=${ASSET_DIR:-"${PROJECT_ROOT}"}
export TEST_OUTPUT_DIR=${TEST_OUTPUT_DIR:-"${SCRIPT_DIR}/../output"}

# Default configuration
DEFAULT_TIDB_PORT=4000
DEFAULT_PD_PORT=2379
DEFAULT_MINIO_PORT=9000
DEFAULT_MINIO_ADMIN_PORT=9001
DEFAULT_CDC_PORT=8300
DEFAULT_META_PORT=8500
DEFAULT_META_STATUS_PORT=8501
DEFAULT_WORKER_PORT=8510
DEFAULT_WORKER_STATUS_PORT=8511

get_playground_port_offset() {
    echo "${PLAYGROUND_PORT_OFFSET:-0}"
}

extract_playground_port_from_log() {
    local component=$1
    local log_file="${TEST_OUTPUT_DIR}/log/playground.log"
    [ -f "${log_file}" ] || return 1

    case "${component}" in
        tidb)
            sed -nE 's/.*Connect TiDB:.*--port ([0-9]+).*/\1/p' "${log_file}" | tail -n1
            ;;
        pd)
            sed -nE 's@.*TiDB Dashboard:.*:([0-9]+)/dashboard.*@\1@p' "${log_file}" | tail -n1
            ;;
        *)
            return 1
            ;;
    esac
}

get_effective_port() {
    local base_port=$1
    echo $((base_port + $(get_playground_port_offset)))
}

get_tidb_port() {
    local tidb_port
    tidb_port=$(extract_playground_port_from_log tidb || true)
    if [ -n "${tidb_port}" ]; then
        echo "${tidb_port}"
        return 0
    fi
    get_effective_port "${DEFAULT_TIDB_PORT}"
}

get_pd_port() {
    local pd_port
    pd_port=$(extract_playground_port_from_log pd || true)
    if [ -n "${pd_port}" ]; then
        echo "${pd_port}"
        return 0
    fi
    get_effective_port "${DEFAULT_PD_PORT}"
}

get_cdc_port() {
    get_effective_port "${DEFAULT_CDC_PORT}"
}

# AWS/S3 configuration for MinIO
export AWS_ACCESS_KEY_ID=minioadmin
export AWS_SECRET_ACCESS_KEY=minioadmin
export AWS_REGION=us-east-1

compute_platform_os() {
    uname | tr '[:upper:]' '[:lower:]'
}

compute_platform_arch() {
    case "$(uname -m)" in
        x86_64)
            echo "amd64"
            ;;
        aarch64 | arm64)
            echo "arm64"
            ;;
        *)
            log_error "Unsupported architecture: $(uname -m)"
            return 1
            ;;
    esac
}

prepend_path() {
    local dir=$1

    case ":${PATH}:" in
        *":${dir}:"*)
            ;;
        *)
            export PATH="${dir}:${PATH}"
            ;;
    esac
}

download_file() {
    local url=$1
    local output=$2

    if command -v curl >/dev/null 2>&1; then
        curl -fsSL -o "${output}" "${url}"
        return 0
    fi

    if command -v wget >/dev/null 2>&1; then
        wget -qO "${output}" "${url}"
        return 0
    fi

    log_error "Neither curl nor wget is available to download ${url}"
    return 1
}

ensure_oras() {
    if command -v oras >/dev/null 2>&1; then
        return 0
    fi

    local version="${ORAS_VERSION:-1.3.0}"
    local os
    os=$(compute_platform_os)
    local arch
    arch=$(compute_platform_arch) || return 1
    local install_dir
    if [ -n "${ORAS_INSTALL_DIR:-}" ]; then
        install_dir="${ORAS_INSTALL_DIR}"
    elif [ -n "${HOME:-}" ]; then
        install_dir="${HOME}/.local/bin"
    else
        install_dir="${PROJECT_ROOT}/tests/output/bin"
    fi
    local archive_name="oras_${version}_${os}_${arch}.tar.gz"
    local download_url="https://github.com/oras-project/oras/releases/download/v${version}/${archive_name}"
    local tmp_dir
    tmp_dir=$(mktemp -d)
    local archive_path="${tmp_dir}/${archive_name}"

    if ! command -v tar >/dev/null 2>&1; then
        log_error "tar is required to install ORAS automatically"
        rm -rf "${tmp_dir}"
        return 1
    fi

    log "oras not found in PATH, installing ORAS CLI v${version}"

    if ! mkdir -p "${install_dir}"; then
        log_error "Failed to create ORAS install directory: ${install_dir}"
        rm -rf "${tmp_dir}"
        return 1
    fi

    if ! download_file "${download_url}" "${archive_path}"; then
        log_error "Failed to download ORAS from ${download_url}"
        rm -rf "${tmp_dir}"
        return 1
    fi

    if ! tar -xzf "${archive_path}" -C "${tmp_dir}" oras; then
        log_error "Failed to extract ORAS archive: ${archive_path}"
        rm -rf "${tmp_dir}"
        return 1
    fi

    if ! cp "${tmp_dir}/oras" "${install_dir}/oras"; then
        log_error "Failed to install ORAS to ${install_dir}/oras"
        rm -rf "${tmp_dir}"
        return 1
    fi

    chmod +x "${install_dir}/oras"
    prepend_path "${install_dir}"
    rm -rf "${tmp_dir}"

    if ! command -v oras >/dev/null 2>&1; then
        log_error "ORAS was installed to ${install_dir}, but it is still not available in PATH"
        return 1
    fi

    log "ORAS installed successfully: $(command -v oras)"
    return 0
}

# Create necessary directories
ensure_dirs() {
    [ -d "${TEST_OUTPUT_DIR}" ] || mkdir -p "${TEST_OUTPUT_DIR}"
    [ -d "${TEST_OUTPUT_DIR}/log" ] || mkdir -p "${TEST_OUTPUT_DIR}/log"
    [ -d "${TEST_OUTPUT_DIR}/oss" ] || mkdir -p "${TEST_OUTPUT_DIR}/oss"
}

# Wait for service to be ready
wait_for_service() {
    local service_name=$1
    local check_command=$2
    local max_attempts=${3:-30}
    local interval=${4:-2}
    
    echo "Waiting for $service_name to start..."
    local i=0
    while ! eval "$check_command" >/dev/null 2>&1; do
        echo "Attempt $((i+1))/$max_attempts: Waiting for $service_name..."
        ((i++))
        if [ $i -eq $max_attempts ]; then
            echo "ERROR: Waiting for $service_name timeout after $max_attempts attempts"
            return 1
        fi
        sleep $interval
    done
    echo "$service_name is ready!"
    return 0
}

# Start MinIO server
start_minio() {
    echo "Starting MinIO server..."
    ensure_dirs

    if mc alias set localminio http://127.0.0.1:${DEFAULT_MINIO_PORT} minioadmin minioadmin >/dev/null 2>&1; then
        echo "Reusing existing MinIO on port ${DEFAULT_MINIO_PORT}"
        mc mb --ignore-existing localminio/ticidefaultbucket >/dev/null
        return 0
    fi
    
    # Clean up previous MinIO data
    rm -rf "${TEST_OUTPUT_DIR}/oss"
    mkdir -p "${TEST_OUTPUT_DIR}/oss"
    
    # Start MinIO server
    nohup minio server "${TEST_OUTPUT_DIR}/oss" \
        --address ":${DEFAULT_MINIO_PORT}" \
        --console-address ":${DEFAULT_MINIO_ADMIN_PORT}" \
        > "${TEST_OUTPUT_DIR}/log/minio.log" 2>&1 &

    echo $! > "${TEST_OUTPUT_DIR}/minio.pid"
    log "MinIO process started with PID $(cat "${TEST_OUTPUT_DIR}/minio.pid")"
    
    # Wait for MinIO to be ready and configure it
    # Old command: "mc config host add localminio http://127.0.0.1:${DEFAULT_MINIO_PORT} minioadmin minioadmin"
    if wait_for_service "MinIO" "mc alias set localminio http://127.0.0.1:${DEFAULT_MINIO_PORT} minioadmin minioadmin" 30 2; then
        echo "Configuring MinIO buckets..."
        mc mb --ignore-existing localminio/ticidefaultbucket >/dev/null
        echo "MinIO started and configured successfully"
        return 0
    else
        echo "ERROR: Failed to start MinIO"
        return 1
    fi
}

# Download and prepare dataset
prepare_dataset() {
    echo "Preparing dataset..."
    local dataset_file="${ASSET_DIR}/hdfs-logs-multitenants.json"
    
    if [ -f "$dataset_file" ]; then
        echo "Dataset already exists at $dataset_file"
        return 0
    fi
    
    echo "Dataset $dataset_file not found, downloading..."
    local temp_file=$(mktemp)
    
    if wget -O "$temp_file" "https://quickwit-datasets-public.s3.amazonaws.com/hdfs-logs-multitenants.json.gz"; then
        echo "Download completed, extracting..."
        if gzip -d -c "$temp_file" > "$dataset_file"; then
            echo "Dataset prepared successfully at $dataset_file"
            rm -f "$temp_file"
            return 0
        else
            echo "ERROR: Failed to extract dataset"
            rm -f "$temp_file"
            return 1
        fi
    else
        echo "ERROR: Failed to download dataset"
        rm -f "$temp_file"
        return 1
    fi
}

fts_component_install_version() {
    echo "${FTS_COMPONENT_VERSION:-feature-fts}"
}

fts_tiup_component_dir() {
    local component=$1
    local tiup_home="${TIUP_HOME:-$HOME/.tiup}"
    local install_version
    install_version=$(fts_component_install_version)

    echo "${tiup_home}/components/${component}/${install_version}"
}

fts_tidb_binpath() {
    echo "$(fts_tiup_component_dir tidb)/tidb-server"
}

fts_tikv_binpath() {
    echo "$(fts_tiup_component_dir tikv)/tikv-server"
}

fts_tiflash_binpath() {
    echo "$(fts_tiup_component_dir tiflash)/tiflash/tiflash"
}

prepare_tidb_component_binary() {
    local source_version=$1
    local target_version=$2
    local tiup_home="${TIUP_HOME:-$HOME/.tiup}"
    local source_path="${tiup_home}/components/tidb/${source_version}/tidb-server"
    local target_path="${tiup_home}/components/tidb/${target_version}/tidb-server"

    if [ ! -x "${source_path}" ]; then
        log_error "TiDB binary not found: ${source_path}"
        return 1
    fi

    if [ "${source_path}" = "${target_path}" ]; then
        log "TiDB binary already prepared in target directory: ${target_path}"
        return 0
    fi

    mkdir -p "$(dirname "${target_path}")"
    cp "${source_path}" "${target_path}"
    chmod +x "${target_path}"
    log "TiDB binary prepared: ${target_path} (source version: ${source_version})"
    return 0
}

prepare_tikv_component_binary() {
    local source_version=$1
    local target_version=$2
    local tiup_home="${TIUP_HOME:-$HOME/.tiup}"
    local source_path="${tiup_home}/components/tikv/${source_version}/tikv-server"
    local target_path="${tiup_home}/components/tikv/${target_version}/tikv-server"

    if [ ! -x "${source_path}" ]; then
        log_error "TiKV binary not found: ${source_path}"
        return 1
    fi

    if [ "${source_path}" = "${target_path}" ]; then
        log "TiKV binary already prepared in target directory: ${target_path}"
        return 0
    fi

    mkdir -p "$(dirname "${target_path}")"
    cp "${source_path}" "${target_path}"
    chmod +x "${target_path}"
    log "TiKV binary prepared: ${target_path} (source version: ${source_version})"
    return 0
}

prepare_tiflash_component_binary() {
    local source_version=$1
    local target_version=$2
    local tiup_home="${TIUP_HOME:-$HOME/.tiup}"
    local source_dir="${tiup_home}/components/tiflash/${source_version}/tiflash"
    local target_dir="${tiup_home}/components/tiflash/${target_version}/tiflash"

    if [ ! -x "${source_dir}/tiflash" ]; then
        log_error "TiFlash binary not found: ${source_dir}/tiflash"
        return 1
    fi

    if [ "${source_dir}" = "${target_dir}" ]; then
        log "TiFlash bundle already prepared in target directory: ${target_dir}"
        return 0
    fi

    rm -rf "${target_dir}"
    mkdir -p "$(dirname "${target_dir}")"
    cp -a "${source_dir}" "${target_dir}"
    log "TiFlash bundle prepared: ${target_dir} (source version: ${source_version})"
    return 0
}

install_e2e_tiflash_binary_from_oci() {
    local tiflash_image_tag=$1
    local tiflash_install_version=$2
    local tiup_home="${TIUP_HOME:-$HOME/.tiup}"
    local oci_artifact_host="${OCI_ARTIFACT_HOST:-us-docker.pkg.dev/pingcap-testing-account/hub}"
    local image_repo="${oci_artifact_host}/pingcap/tiflash/image"
    local image_ref="${image_repo}:${tiflash_image_tag}"
    local component_dir="${tiup_home}/components/tiflash/${tiflash_install_version}"
    local bundle_dir="${component_dir}/tiflash"
    local platform_os
    platform_os=$(compute_platform_os)
    local platform_arch
    platform_arch=$(compute_platform_arch) || return 1
    local tmp_dir
    tmp_dir=$(mktemp -d)
    local platform_digest
    local resolved_ref
    local layer_digest
    local layer_tar="${tmp_dir}/layer.tar.gz"

    log "Installing tiflash bundle from OCI image ${image_ref} to ${bundle_dir}"

    if ! platform_digest=$(oras manifest fetch "${image_ref}" | python3 -c '
import json
import sys

data = json.load(sys.stdin)
manifests = data.get("manifests")

if not manifests:
    print("")
    raise SystemExit(0)

platform_os = sys.argv[1]
platform_arch = sys.argv[2]
for manifest in manifests:
    platform = manifest.get("platform", {})
    if platform.get("os") == platform_os and platform.get("architecture") == platform_arch:
        print(manifest["digest"])
        raise SystemExit(0)

raise SystemExit(1)
' "${platform_os}" "${platform_arch}"
    ); then
        log_error "Failed to resolve ${image_ref} for ${platform_os}/${platform_arch}"
        rm -rf "${tmp_dir}"
        return 1
    fi

    resolved_ref="${image_ref}"
    if [ -n "${platform_digest}" ]; then
        resolved_ref="${image_repo}@${platform_digest}"
    fi

    if ! layer_digest=$(oras manifest fetch "${resolved_ref}" | python3 -c '
import json
import sys

data = json.load(sys.stdin)
layers = data.get("layers", [])
if not layers:
    raise SystemExit(1)

print(layers[-1]["digest"])
'
    ); then
        log_error "Failed to locate binary layer from ${resolved_ref}"
        rm -rf "${tmp_dir}"
        return 1
    fi

    if ! oras blob fetch --output "${layer_tar}" "${image_repo}@${layer_digest}" >/dev/null; then
        log_error "Failed to download layer ${layer_digest} from ${image_repo}"
        rm -rf "${tmp_dir}"
        return 1
    fi

    rm -rf "${bundle_dir}"
    mkdir -p "${component_dir}"
    if ! tar -xzf "${layer_tar}" -C "${component_dir}" tiflash; then
        log_error "Failed to extract tiflash bundle from ${image_ref}"
        rm -rf "${tmp_dir}"
        return 1
    fi

    if ! chmod +x "${bundle_dir}/tiflash"; then
        log_error "Failed to chmod TiFlash binary: ${bundle_dir}/tiflash"
        rm -rf "${tmp_dir}"
        return 1
    fi

    rm -rf "${tmp_dir}"
    log "tiflash bundle installed successfully: ${bundle_dir}"
    return 0
}

append_fts_playground_binpath_args() {
    local args=$1
    local tidb_binpath=${DB_BINPATH:-$(fts_tidb_binpath)}
    local tikv_binpath=${KV_BINPATH:-$(fts_tikv_binpath)}
    local tiflash_binpath=${TIFLASH_BINPATH:-$(fts_tiflash_binpath)}

    args+=" --db.binpath ${tidb_binpath}"
    args+=" --kv.binpath ${tikv_binpath}"
    args+=" --tiflash.binpath ${tiflash_binpath}"
    echo "${args}"
}

validate_fts_playground_binpaths() {
    local tidb_binpath=${DB_BINPATH:-$(fts_tidb_binpath)}
    local tikv_binpath=${KV_BINPATH:-$(fts_tikv_binpath)}
    local tiflash_binpath=${TIFLASH_BINPATH:-$(fts_tiflash_binpath)}
    local missing=0

    if [ ! -x "${tidb_binpath}" ]; then
        log_error "TiDB binary not found or not executable: ${tidb_binpath}"
        missing=1
    fi

    if [ ! -x "${tikv_binpath}" ]; then
        log_error "TiKV binary not found or not executable: ${tikv_binpath}"
        missing=1
    fi

    if [ ! -x "${tiflash_binpath}" ]; then
        log_error "TiFlash binary not found or not executable: ${tiflash_binpath}"
        missing=1
    fi

    if [ "${missing}" -eq 1 ]; then
        return 1
    fi

    log "Using FTS component binaries:"
    log "  - TiDB: ${tidb_binpath}"
    log "  - TiKV: ${tikv_binpath}"
    log "  - TiFlash: ${tiflash_binpath}"
    return 0
}

install_e2e_tidb_binary_from_tiup() {
    local tidb_binary_version=$1
    local tidb_install_version=$2

    tiup install "tidb:${tidb_binary_version}"
    prepare_tidb_component_binary "${tidb_binary_version}" "${tidb_install_version}"
}

install_e2e_tiflash_binary_from_tiup() {
    local tiflash_binary_version=$1
    local tiflash_install_version=$2

    tiup install "tiflash:${tiflash_binary_version}"
    prepare_tiflash_component_binary "${tiflash_binary_version}" "${tiflash_install_version}"
}

wait_for_tiflash_registration() {
    local expected_count=${1:-1}
    local max_attempts=${2:-30}
    local interval=${3:-2}
    local i=0
    local tidb_port
    tidb_port=$(get_tidb_port)

    echo "Waiting for TiFlash to register in TiDB..."
    while true; do
        local tiflash_count
        tiflash_count=$(mysql -h 127.0.0.1 -P "${tidb_port}" -u root -Nse \
            "SELECT COUNT(*) FROM information_schema.cluster_info WHERE type='tiflash';" 2>/dev/null || true)

        if [[ "${tiflash_count}" =~ ^[0-9]+$ ]] && [ "${tiflash_count}" -ge "${expected_count}" ]; then
            echo "TiFlash is ready! (${tiflash_count}/${expected_count})"
            return 0
        fi

        if grep -Eq "TiFlash .*failed to start" "${TEST_OUTPUT_DIR}/log/playground.log"; then
            echo "ERROR: TiFlash failed to start"
            grep -E "TiFlash .*failed to start" "${TEST_OUTPUT_DIR}/log/playground.log" || true
            return 1
        fi

        ((i++))
        if [ "${i}" -eq "${max_attempts}" ]; then
            echo "ERROR: Waiting for TiFlash timeout after ${max_attempts} attempts (expected >= ${expected_count}, got '${tiflash_count:-N/A}')"
            return 1
        fi

        echo "Attempt ${i}/${max_attempts}: Waiting for TiFlash..."
        sleep ${interval}
    done
}

# Start TiDB playground
start_playground() {
    local playground_version=$1
    local cluster_version=$2
    local playground_args=$3

    echo "Starting TiDB playground..."
    
    # Check if tiup is available
    if ! command -v tiup >/dev/null 2>&1; then
        echo "ERROR: tiup is not installed or not in PATH"
        return 1
    fi
    
    echo "Starting playground: tiup playground${playground_version} ${cluster_version} --ticdc 1 ${playground_args} > ${TEST_OUTPUT_DIR}/log/playground.log 2>&1 &"

    # Set TiCDC environment
    export TICDC_NEWARCH=true
    
    tiup list --all
    # Start playground
    nohup tiup playground$playground_version $cluster_version \
        --ticdc 1 \
        $playground_args \
        > "${TEST_OUTPUT_DIR}/log/playground.log" 2>&1 &

    # Wait for TiDB to be ready
    if ! wait_for_service "TiDB" "grep -q 'Connect TiDB:' ${TEST_OUTPUT_DIR}/log/playground.log" 60 5; then
        log_error "TiDB is not ready"
        cat "${TEST_OUTPUT_DIR}/log/playground.log"
        return 1
    fi

    local tiflash_count=0
    if [[ "${playground_args}" =~ --tiflash[[:space:]]+([0-9]+) ]]; then
        tiflash_count="${BASH_REMATCH[1]}"
    elif [[ "${playground_args}" =~ --tiflash=([0-9]+) ]]; then
        tiflash_count="${BASH_REMATCH[1]}"
    fi

    if [ "${tiflash_count}" -gt 0 ]; then
        if ! wait_for_tiflash_registration "${tiflash_count}" 60 5; then
            log_error "TiFlash is not ready"
            cat "${TEST_OUTPUT_DIR}/log/playground.log"
            return 1
        fi
    fi
}

# Create TiCDC changefeed
create_changefeed() {
    echo "Creating TiCDC changefeed..."
    local sink_uri="s3://ticidefaultbucket/tici_default_prefix/cdc?protocol=canal-json&endpoint=http://127.0.0.1:${DEFAULT_MINIO_PORT}&enable-tidb-extension=true&output-row-key=true&use-table-id-as-path=true"
    local changefeed_config="${TEST_OUTPUT_DIR}/changefeed-date-none.toml"
    local cdc_port
    cdc_port=$(get_cdc_port)
    mkdir -p "${TEST_OUTPUT_DIR}"
    cat > "${changefeed_config}" <<'EOF'
[sink]
date-separator = "none"
EOF
    
    if wait_for_service "TiCDC" "tiup cdc:nightly cli changefeed list --server=http://127.0.0.1:${cdc_port}" 30 2; then
        tiup cdc:nightly cli changefeed create \
            --server=http://127.0.0.1:${cdc_port} \
            --sink-uri="$sink_uri" \
            --changefeed-id="tici-test-changefeed" \
            --config="${changefeed_config}"
        echo "TiCDC changefeed created successfully"
        return 0
    else
        echo "ERROR: Failed to create TiCDC changefeed"
        return 1
    fi
}

configure_tidb_global_sort_uri() {
    local bucket=${1:-ticidefaultbucket}
    local prefix=${2:-tici/global-sort}
    local endpoint="http://127.0.0.1:${DEFAULT_MINIO_PORT}"
    local sort_uri="s3://${bucket}/${prefix}?endpoint=${endpoint}&access-key=${AWS_ACCESS_KEY_ID}&secret-access-key=${AWS_SECRET_ACCESS_KEY}&provider=minio"
    local tidb_port
    tidb_port=$(get_tidb_port)

    log "Configuring TiDB global sort URI: s3://${bucket}/${prefix}"
    mysql -h 127.0.0.1 -P "${tidb_port}" -u root \
        -e "SET @@global.tidb_cloud_storage_uri='${sort_uri}'"
}

# Insert data into TiDB
insert_data() {
    local rows=${1:-100000}
    local table_name=${2:-hdfs_10w}
    local tidb_port
    tidb_port=$(get_tidb_port)

    echo "Starting insert data..."
    ensure_dirs

    if python3 "${PROJECT_ROOT}/ci/read_hdfs.py" "$table_name" \
        --input "${ASSET_DIR}/hdfs-logs-multitenants.json" \
        --max_rows "$rows" \
        --tidb_host 127.0.0.1 \
        --tidb_port "${tidb_port}"; then
        echo "Data insertion completed"
    else
        echo "ERROR: Data insertion failed"
        return 1
    fi
}

# Cleanup all services
cleanup() {
    local exit_code=${1:-0}
    echo "Cleaning up services..."

    set +e

    # Gracefully stop services first
    pgrep -f "(tiup|minio)" | xargs -r kill -2 || true
    i=0
    while [ $i -lt 15 ]; do
        if pgrep -f "(tiup|minio)" >/dev/null 2>&1; then
            echo "Waiting for services to stop..."
            sleep 2
        else
            break
        fi
        ((i++))
    done
    # Force kill remaining services
    pgrep -f "(tiup|minio)" | xargs -r kill -9 || true

    echo "Cleanup completed"
    return $exit_code
}

# Log function
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

# Error log function
log_error() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2
}

# Check prerequisites
check_prerequisites() {
    local missing_deps=()

    if ! ensure_oras; then
        missing_deps+=("oras")
    fi
    
    # Check required commands
    for cmd in tiup mysql minio mc python3 cargo; do
        if ! command -v $cmd >/dev/null 2>&1; then
            missing_deps+=($cmd)
        fi
    done
    
    # Check Python dependencies
    if ! python3 -c "import mysql.connector" >/dev/null 2>&1; then
        missing_deps+=("python3-mysql.connector")
    fi
    
    if [ ${#missing_deps[@]} -gt 0 ]; then
        log_error "Missing dependencies: ${missing_deps[*]}"
        return 1
    fi

    return 0
}

check_runtime_prerequisites() {
    local missing_deps=()

    for cmd in tiup mysql minio mc python3; do
        if ! command -v $cmd >/dev/null 2>&1; then
            missing_deps+=($cmd)
        fi
    done

    if ! python3 -c "import mysql.connector" >/dev/null 2>&1; then
        missing_deps+=("python3-mysql.connector")
    fi

    if [ ${#missing_deps[@]} -gt 0 ]; then
        log_error "Missing runtime dependencies: ${missing_deps[*]}"
        return 1
    fi

    return 0
}

dump_configs() {
    local tiup_home="${TIUP_HOME:-$HOME/.tiup}"

    if [ -z "${PLAYGROUND_TAG:-}" ]; then
        echo ">>> PLAYGROUND_TAG is empty or unset. Skipping log dump."
        return 0
    fi

    local base_dir="${tiup_home}/data/${PLAYGROUND_TAG}"

    if [ ! -d "$base_dir" ]; then
        echo ">>> Directory not found: $base_dir. Skipping log dump."
        return 0
    fi

    echo ""
    echo "################# DUMPING CONFIGS (${base_dir}) #################"

    shopt -s nullglob

    local config_files=(
        "${base_dir}"/tici-meta-*/*.toml
        "${base_dir}"/tici-worker-*/*.toml
        "${base_dir}"/tiflash-*/*.toml
    )

    shopt -u nullglob

    for config in "${config_files[@]}"; do
        if [ -f "$config" ]; then
            echo "------------------------------------------------"
            echo ">>> FILE: $config"
            echo "------------------------------------------------"
            cat "$config" || true
            echo ""
        fi
    done
    echo "################# END OF CONFIGS #################"
}

dump_logs() {
    local tiup_home="${TIUP_HOME:-$HOME/.tiup}"

    if [ -z "${PLAYGROUND_TAG:-}" ]; then
        echo ">>> PLAYGROUND_TAG is empty or unset. Skipping log dump."
        return 0
    fi

    local base_dir="${tiup_home}/data/${PLAYGROUND_TAG}"
    
    if [ ! -d "$base_dir" ]; then
        echo ">>> Directory not found: $base_dir. Skipping log dump."
        return 0
    fi

    echo ""
    echo "################# DUMPING LOGS (${base_dir}) #################"

    shopt -s nullglob

    local log_files=(
        "${base_dir}"/tici-meta-*/tici-meta.log
        "${base_dir}"/tici-worker-*/tici-worker.log
        "${base_dir}"/tiflash-*/tici_searchlib.log
        "${base_dir}"/tiflash-*/tiflash.log
        "${base_dir}"/tiflash-*/tiflash_error.log
        "${base_dir}"/tiflash-*/tiflash_tikv.log
        "${base_dir}"/tidb-*/tidb.log
        "${base_dir}"/tikv-*/tikv.log
        "${base_dir}"/pd-*/pd.log
    )

    shopt -u nullglob

    for log in "${log_files[@]}"; do
        if [ -f "$log" ]; then
            local line_count
            local byte_size
            local dump_start_seconds

            line_count=$(wc -l < "$log" 2>/dev/null | tr -d '[:space:]') || line_count="unknown"
            byte_size=$(wc -c < "$log" 2>/dev/null | tr -d '[:space:]') || byte_size="unknown"
            dump_start_seconds=${SECONDS}

            echo "------------------------------------------------"
            echo ">>> FILE: $log (lines=${line_count} bytes=${byte_size})"
            echo "------------------------------------------------"
            cat "$log" || true
            echo ">>> FILE TIMING: $log elapsed=$((SECONDS - dump_start_seconds))s"
            echo ""
        fi
    done
    echo "################# END OF LOGS #################"
}
