#!/usr/bin/env bash
# Th0rn BTX Pool — miner installer (dexbtx-miner v0.4.16 + btx-gbt-solve v0.32.11 static)
#
# Usage:
#   curl -fsSL https://files.th0rn.ru/files/btx/install.sh | bash
#   curl -fsSL https://files.th0rn.ru/files/btx/install.sh | bash -s -- \
#       --address 'btx1zYOUR_ADDRESS' --worker rig1 --pool btx.th0rn.space:3333
#   ... | bash -s -- --address 'btx1z...' --worker rig1 --systemd -y
#
# Based on dexbtx/minebtx install.sh (v0.4.14) — same GPU tuning, Th0rn pool defaults.

# ─── Self-update bootstrap ──────────────────────────────────────────────────
# Marker — keep this exact line + bump on each release. The bootstrap
# downstream parses this string and skips re-exec if it matches.
INSTALL_SH_VERSION="1.0.5-th0rn"

# Th0rn pool: no self-update to minebtx.com — use TH0RN_INSTALL_URL if you host a newer copy.
if [[ "${TH0RN_NO_SELFUPDATE:-1}" != "1" && "${DEXBTX_NO_SELFUPDATE:-0}" != "1" ]]; then
    INSTALL_SH_LATEST_URL="${TH0RN_INSTALL_URL:-https://files.th0rn.ru/files/btx/install.sh}"
    _bootstrap_tmp="$(mktemp /tmp/dexbtx-install-sh.XXXXXX 2>/dev/null || mktemp)"
    if [[ -n "$_bootstrap_tmp" ]] && \
        curl -fsSL --connect-timeout 5 --max-time 30 \
            "$INSTALL_SH_LATEST_URL" -o "$_bootstrap_tmp" 2>/dev/null; then
        _latest_ver="$(grep -E '^INSTALL_SH_VERSION=' "$_bootstrap_tmp" \
            | head -1 | cut -d= -f2 | tr -d '"' | tr -d "'")"
        if [[ -n "$_latest_ver" && "$_latest_ver" != "$INSTALL_SH_VERSION" ]]; then
            echo "[install] self-updating $INSTALL_SH_VERSION -> $_latest_ver"
            chmod +x "$_bootstrap_tmp"
            DEXBTX_NO_SELFUPDATE=1 exec bash "$_bootstrap_tmp" "$@"
        fi
        rm -f "$_bootstrap_tmp"
    else
        # GitHub raw unreachable; warn but continue with the local copy.
        echo "[install] (self-update check skipped: $INSTALL_SH_LATEST_URL unreachable)" >&2
    fi
fi
unset _bootstrap_tmp _latest_ver

set -euo pipefail

# ─── Configurables ──────────────────────────────────────────────────────────
# Pin the prebuilds release tag. install.sh always pulls this version.
# Bump in lockstep with experiments/vast/prebuilds and pyproject.toml.
PREBUILDS_TAG="${PREBUILDS_TAG:-btx-prebuilds-v0.32.11}"
# v0.32.11 x86_64 rebuild (Jun 2026): STATIC libcudart — runs on CUDA-12 and CUDA-13 hosts.
EXPECTED_SHA256="${EXPECTED_SHA256:-3fbc9d71350db2b686a91b5768405a19f81246346a94bf70a151dd0b762c12f3}"
# Darwin arm64 (Apple Silicon + Metal) solver pin. Fill in after the first green
# build-solver-macos-arm64 CI run (the workflow prints the sha256). Until then,
# macOS installs intentionally fail rather than install an unverified binary.
DARWIN_ARM64_SHA256="${DARWIN_ARM64_SHA256:-361abdad3880fe8be4ff470c29238c90303c6bd78dcac3b15643607fc369002c}"
# Linux aarch64 (Grace / GB10 Blackwell etc.) CUDA solver pins. Default CUDA
# toolkit variant is cuda12; set DEXBTX_CUDA=cuda13 for newer-driver hosts.
AARCH64_CUDA12_SHA256="${AARCH64_CUDA12_SHA256:-059478f957433b09ff5f83916aa346538ec26dcc0689e7810b08d6a03f4ebfd0}"
AARCH64_CUDA13_SHA256="${AARCH64_CUDA13_SHA256:-c09a567eee4c97e5b183ffd77d037fb6d1dc161e14779f2de7f480585ca86f5c}"
# Linux x86_64 AMD/ROCm (HIP) solver pin — EXPERIMENTAL. Selected when an AMD
# GPU is present (rocm-smi) or DEXBTX_GPU=rocm. Correctness is enforced by an
# install-time HIP-vs-CPU self-check below (the HIP kernel is unproven off real
# AMD silicon). The reference digest for that self-check:
ROCM_X86_64_SHA256="${ROCM_X86_64_SHA256:-65d178631b4378a7d474d0ecec7609ee01125ccc34770caaf615d898f63ed049}"
SELFCHECK_REF_DIGEST="7db2e9351c8c947293cb12d086ff03435730156265b67e3bce9dab1956074b14"
PREBUILDS_BASE="${PREBUILDS_BASE:-https://github.com/dexbtx/minebtx/releases/download/${PREBUILDS_TAG}}"
# Default asset = Linux x86_64; the Darwin branch below overrides for Apple Silicon.
SOLVER_URL="${PREBUILDS_BASE}/btx-gbt-solve"

# Default pool — Th0rn PPLNS (:3333). Override: --pool or TH0RN_POOL / DEXBTX_POOL env.
DEFAULT_POOL="${TH0RN_POOL:-${DEXBTX_POOL:-btx.th0rn.space:3333}}"

# Install paths (systemd → /var/lib/btx-miner; interactive/fleet → ~/.dexbtx-miner).
INSTALL_DIR="${TH0RN_INSTALL_DIR:-${HOME}/.dexbtx-miner}"
SOLVER_PATH="${INSTALL_DIR}/bin/btx-gbt-solve"
CONFIG_PATH="${INSTALL_DIR}/config.yaml"

# ─── Parse CLI ──────────────────────────────────────────────────────────────
ADDRESS=""
WORKER=""
POOL="${DEFAULT_POOL}"
ASSUME_YES=0
SKIP_PROMPT=0
LOCAL_SOLVER=""
SKIP_PIP=0
SYSTEMD=0

while [[ $# -gt 0 ]]; do
    case "$1" in
        --address) ADDRESS="$2"; shift 2 ;;
        --worker)  WORKER="$2";  shift 2 ;;
        --pool)    POOL="$2";    shift 2 ;;
        --yes|-y)  ASSUME_YES=1; SKIP_PROMPT=1; shift ;;
        --skip-prompt) SKIP_PROMPT=1; shift ;;
        --local-solver) LOCAL_SOLVER="$2"; shift 2 ;;
        --skip-pip)    SKIP_PIP=1; shift ;;
        --systemd) SYSTEMD=1; ASSUME_YES=1; SKIP_PROMPT=1; shift ;;
        --help|-h)
            sed -n '2,12p' "$0"
            exit 0
            ;;
        *) echo "unknown arg: $1"; exit 1 ;;
    esac
done

if [[ "$SYSTEMD" -eq 1 ]]; then
    INSTALL_DIR="${TH0RN_INSTALL_DIR:-/var/lib/btx-miner}"
    SOLVER_PATH="${INSTALL_DIR}/bin/btx-gbt-solve"
    CONFIG_PATH="${INSTALL_DIR}/config.yaml"
fi

# ─── Helpers ────────────────────────────────────────────────────────────────
log()  { echo -e "\033[1;36m[th0rn-btx]\033[0m $*"; }
warn() { echo -e "\033[1;33m[warn]\033[0m $*" >&2; }
err()  { echo -e "\033[1;31m[error]\033[0m $*" >&2; exit 1; }

# Домашний риг: обычный пользователь, БЕЗ sudo.
# Аренда (vast/runpod/docker): часто root в контейнере — разрешаем автоматически.
IN_CONTAINER=0
[[ -f /.dockerenv || -n "${VAST_CONTAINER_LABEL:-}" || -n "${RUNPOD_POD_ID:-}" ]] && IN_CONTAINER=1
[[ "${DEXBTX_ALLOW_ROOT:-0}" == "1" ]] && IN_CONTAINER=1

if [[ "$(id -u)" -eq 0 && "$SYSTEMD" -eq 0 && "$IN_CONTAINER" -eq 0 ]]; then
    err "Не запускай install от root/sudo на домашнем ПК.
  Войди как обычный пользователь (whoami) и повтори БЕЗ sudo.
  Арендный контейнер (vast/runpod): тот же install, root там нормален — просто запусти снова."
fi
if [[ "$(id -u)" -eq 0 && "$IN_CONTAINER" -eq 1 ]]; then
    warn "контейнер/аренда: install от root → ${HOME}/.dexbtx-miner (это ок для vast/runpod)"
fi

need() {
    command -v "$1" >/dev/null 2>&1 || err "missing required tool: $1"
}

confirm() {
    [[ "$ASSUME_YES" -eq 1 ]] && return 0
    read -rp "$1 [y/N] " ans
    [[ "$ans" =~ ^[Yy]$ ]]
}

# libcudart часто не в дефолтном LD_LIBRARY_PATH (driver есть, toolkit — нет).
setup_cuda_library_path() {
    local d
    for d in \
        /usr/local/cuda/lib64 \
        /usr/local/cuda/targets/x86_64-linux/lib \
        /usr/local/cuda/targets/aarch64-linux/lib \
        /usr/local/cuda-13/lib64 \
        /usr/local/cuda-12/lib64 \
        /usr/local/cuda-11.7/targets/x86_64-linux/lib \
        /usr/lib/wsl/lib \
        /usr/lib/nvidia-cuda-toolkit/lib64; do
        [[ -d "$d" ]] || continue
        case ":${LD_LIBRARY_PATH:-}:" in
            *":$d:"*) ;;
            *) export LD_LIBRARY_PATH="${d}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" ;;
        esac
    done
}

# --help может не вывести текст, если бинарник не стартует (нет libcudart на dynamic builds).
# SHA256 уже сверен с официальным релизом — этого достаточно; help только для лога.
verify_solver_share_target() {
    local help=""
    setup_cuda_library_path
    help="$("$SOLVER_PATH" --help 2>&1 || true)"
    if echo "$help" | grep -q 'share-target'; then
        log "solver OK (--share-target в --help)"
        return 0
    fi
    if grep -a -q 'share-target' "$SOLVER_PATH" 2>/dev/null; then
        if ldd "$SOLVER_PATH" 2>/dev/null | grep -q 'libcudart'; then
            warn "solver --help не запустился (часто нет libcudart — драйвер есть, CUDA runtime нет)."
        else
            warn "solver --help не запустился (x86 static build — обычно достаточно NVIDIA driver)."
        fi
        [[ -n "$help" ]] && warn "  $(echo "$help" | head -1 | cut -c1-120)"
        warn "бинарник проверен по SHA256 — продолжаем установку."
        return 0
    fi
    [[ -n "$help" ]] && echo "$help" | head -10 >&2
    err "solver не тот (нет share-target в бинарнике). Сообщи оператору пула."
}

# aarch64: cuda12 vs cuda13 (GB10/Spark, driver 580+). Override: DEXBTX_CUDA=cuda13
detect_aarch64_cuda_variant() {
    if [[ -n "${DEXBTX_CUDA:-}" ]]; then
        echo "${DEXBTX_CUDA}"
        return 0
    fi
    if ldconfig -p 2>/dev/null | grep -q 'libcudart.so.13' && \
       ! ldconfig -p 2>/dev/null | grep -q 'libcudart.so.12'; then
        echo cuda13
        return 0
    fi
    if [[ -d /usr/local/cuda-13/lib64 ]] && \
       { [[ ! -d /usr/local/cuda-12/lib64 ]] || [[ ! -e /usr/local/cuda/lib64/libcudart.so.12 ]]; }; then
        echo cuda13
        return 0
    fi
    if command -v nvidia-smi >/dev/null 2>&1; then
        local cv
        cv="$(nvidia-smi 2>/dev/null | sed -n 's/.*CUDA Version: \([0-9][0-9]*\).*/\1/p' | head -1)"
        [[ "$cv" == "13" ]] && { echo cuda13; return 0; }
    fi
    echo cuda12
}

write_solver_platform_env() {
    local key=""
    case "$ARCH" in
        aarch64|arm64)
            local variant
            variant="$(detect_aarch64_cuda_variant)"
            if [[ "$variant" == "cuda13" ]]; then
                key="aarch64-linux-cuda13"
            else
                key="aarch64-linux"
            fi
            ;;
    esac
    [[ -z "$key" ]] && return 0
    cat > "${INSTALL_DIR}/solver-platform.env" <<ENV
# auto-generated by install.sh — solver auto-update channel
export DEXBTX_PLATFORM_KEY=${key}
ENV
    chmod 600 "${INSTALL_DIR}/solver-platform.env"
    log "solver platform key → ${key} (${INSTALL_DIR}/solver-platform.env)"
}

# ─── Detection ──────────────────────────────────────────────────────────────
log "Th0rn BTX miner installer v${INSTALL_SH_VERSION} — dexbtx-miner ${PREBUILDS_TAG}"
if [[ "$(id -u)" -ne 0 ]]; then
    log "режим: пользователь $(whoami) → ${INSTALL_DIR}"
else
    log "режим: systemd → ${INSTALL_DIR}"
fi

OS="$(uname -s)"
ARCH="$(uname -m)"
IS_ROCM=0
case "$OS" in
    Linux)
        # x86_64: default CUDA/CPU asset, unless an AMD GPU is present (ROCm/HIP,
        # experimental). aarch64 (Grace/GB10) needs its own ARM64 CUDA build, or
        # install.sh would hand an ARM host an x86_64 binary that can't exec.
        case "$ARCH" in
            x86_64|amd64)
                if [[ "${DEXBTX_GPU:-}" == "rocm" ]]; then
                    err "AMD/ROCm has no prebuilt solver for the v0.32.10 MatMul-V3 release yet. \
Options: (1) force the NVIDIA/CPU x86_64 build with DEXBTX_GPU=none, or (2) build btx-gbt-solve from source against btxchain/btx v0.32.10. ROCm support will return in a later point release."
                elif [[ "${DEXBTX_GPU:-}" != "none" ]] && command -v rocm-smi >/dev/null 2>&1 && \
                     ! { command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L &>/dev/null 2>&1; }; then
                    err "AMD/ROCm has no prebuilt solver for the v0.32.10 MatMul-V3 release yet. \
Options: (1) force the NVIDIA/CPU x86_64 build with DEXBTX_GPU=none, or (2) build btx-gbt-solve from source against btxchain/btx v0.32.10. ROCm support will return in a later point release."
                fi
                ;;
            aarch64|arm64)
                CUDA_VARIANT="$(detect_aarch64_cuda_variant)"
                log "Linux aarch64 detected — CUDA variant ${CUDA_VARIANT} (override: DEXBTX_CUDA=cuda12|cuda13)"
                SOLVER_URL="${PREBUILDS_BASE}/btx-gbt-solve-aarch64-linux-gnu-${CUDA_VARIANT}"
                if [[ "$CUDA_VARIANT" == "cuda13" ]]; then
                    EXPECTED_SHA256="${AARCH64_CUDA13_SHA256}"
                else
                    EXPECTED_SHA256="${AARCH64_CUDA12_SHA256}"
                fi
                ;;
            *) err "unsupported Linux architecture: $ARCH (published builds: x86_64, aarch64)" ;;
        esac
        ;;
    Darwin)
        # Apple Silicon only — the published Mac build is arm64 + Metal. There is
        # no Intel (x86_64) macOS solver.
        if [[ "$ARCH" != "arm64" ]]; then
            err "macOS Intel (x86_64) is not supported — Apple Silicon (arm64) only."
        fi
        log "macOS Apple Silicon detected — using the Metal solver build (no NVIDIA path)."
        SOLVER_URL="${PREBUILDS_BASE}/btx-gbt-solve-darwin-arm64"
        EXPECTED_SHA256="${DARWIN_ARM64_SHA256}"
        if [[ "$EXPECTED_SHA256" == "REPLACE_AFTER_FIRST_MACOS_BUILD" ]]; then
            err "macOS solver SHA pin not set yet. Run the build-solver-macos-arm64 workflow, publish the asset, then set DARWIN_ARM64_SHA256 (in install.sh or via env)."
        fi
        ;;
    *)      err "unsupported OS: $OS" ;;
esac

# GPU detection
HAS_NVIDIA=0
GPU_NAME=""
if [[ "$OS" == "Darwin" ]]; then
    # Apple Silicon uses the Metal backend. The NVIDIA / CPU-fallback check
    # below is Linux-only — running it on macOS would wrongly warn "CPU only"
    # for what is actually a Metal-accelerated build.
    GPU_NAME="Apple Silicon (Metal)"
elif [[ "$IS_ROCM" -eq 1 ]]; then
    # AMD/ROCm — the HIP backend presents as "cuda"; skip the NVIDIA/CPU check.
    GPU_NAME="AMD GPU (ROCm/HIP, experimental)"
else
    if command -v nvidia-smi >/dev/null 2>&1; then
        if nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 | grep -q "."; then
            HAS_NVIDIA=1
            GPU_NAME="$(nvidia-smi --query-gpu=name --format=csv,noheader | head -1)"
            log "detected NVIDIA GPU: ${GPU_NAME}"
        fi
    fi
    if [[ "$HAS_NVIDIA" -eq 0 ]]; then
        warn "no NVIDIA GPU detected — solver will run on CPU only (much slower)"
    fi
fi

GPU_COUNT=0
if [[ "$HAS_NVIDIA" -eq 1 ]] && command -v nvidia-smi >/dev/null 2>&1; then
    GPU_COUNT=$(nvidia-smi -L 2>/dev/null | wc -l)
    GPU_COUNT=$((GPU_COUNT + 0))
fi
# Один dexbtx-процесс = одна GPU (gpu_inputs: 1). Multi-GPU → start-btx-multigpu-screen.sh.
GPU_INPUTS=1
[[ "$HAS_NVIDIA" -eq 1 ]] && log "GPU count: ${GPU_COUNT} (gpu_inputs=${GPU_INPUTS} per process)"

# Python
need curl
# SHA-256 helper — Linux has sha256sum; macOS ships shasum instead.
if command -v sha256sum >/dev/null 2>&1; then
    sha256_of() { sha256sum "$1" | awk '{print $1}'; }
elif command -v shasum >/dev/null 2>&1; then
    sha256_of() { shasum -a 256 "$1" | awk '{print $1}'; }
else
    err "no SHA-256 tool found (need sha256sum or shasum)"
fi

PYTHON=""
for cand in python3.11 python3.10 python3; do
    if command -v "$cand" >/dev/null 2>&1; then
        PYTHON="$cand"
        if "$cand" -c 'import sys; sys.exit(0 if sys.version_info >= (3,10) else 1)'; then
            break
        fi
        PYTHON=""
    fi
done

if [[ -z "$PYTHON" ]]; then
    if [[ "$OS" == "Darwin" ]]; then
        command -v brew >/dev/null 2>&1 || err "no python3.10+ found and Homebrew missing — install Python 3.10+ (see https://brew.sh, then 'brew install python') and re-run"
        log "installing python@3.11 via brew..."
        brew install python@3.11 || err "brew install python@3.11 failed"
        PYTHON="$(brew --prefix)/opt/python@3.11/bin/python3.11"
        command -v "$PYTHON" >/dev/null 2>&1 || PYTHON=python3.11
    elif command -v apt-get >/dev/null 2>&1; then
        log "installing python3.11 via apt..."
        sudo apt-get update -qq
        sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3.11 python3.11-venv python3-pip
        PYTHON=python3.11
    else
        err "no python3.10+ found and no supported package manager (apt/brew) — install Python 3.10+ manually then re-run"
    fi
fi
log "using Python: $($PYTHON --version 2>&1)"
# Where `pip install --user` drops console scripts (Linux: ~/.local/bin;
# macOS: ~/Library/Python/X.Y/bin). Used for the PATH hint + launch command.
USER_BIN="$("$PYTHON" -m site --user-base 2>/dev/null)/bin"

# ─── Install pip + runtime deps + dexbtx-miner ──────────────────────────────
# Many vast.ai CUDA images ship without pip — install it via apt if missing.
if ! "$PYTHON" -m pip --version >/dev/null 2>&1; then
    if [[ "$OS" == "Darwin" ]]; then
        log "bootstrapping pip via ensurepip..."
        "$PYTHON" -m ensurepip --upgrade 2>/dev/null || err "pip missing — try 'brew install python@3.11' then re-run"
    elif command -v apt-get >/dev/null 2>&1; then
        log "python pip not present; installing via apt..."
        sudo apt-get update -qq
        sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3-pip
    else
        err "python pip missing and apt-get not available; install pip manually then re-run"
    fi
fi

# pip install wrapper — retries with --break-system-packages on PEP-668
# ("externally-managed-environment"), which Homebrew/system Python on macOS
# (and newer Debian) raise for --user installs.
pip_install() {
    local elog; elog="$(mktemp)"
    if "$PYTHON" -m pip install --user "$@" 2>"$elog"; then
        rm -f "$elog"; return 0
    fi
    if grep -q "externally-managed-environment" "$elog"; then
        warn "PEP-668 externally-managed env — retrying with --break-system-packages"
        rm -f "$elog"
        "$PYTHON" -m pip install --user --break-system-packages "$@"
    else
        cat "$elog" >&2; rm -f "$elog"; return 1
    fi
}

# Runtime deps (pyyaml for --config). Install regardless of --skip-pip
# because --skip-pip only skips the dexbtx-miner package itself (useful for
# source-tree dev), not its transitive deps.
log "installing runtime deps (pyyaml)..."
pip_install --quiet --upgrade pyyaml

if [[ "$SKIP_PIP" -eq 1 ]]; then
    log "skipping dexbtx-miner pip install (--skip-pip); assuming source tree is on PYTHONPATH"
else
    # Install the Python package directly from the GitHub source tarball
    # for the v0.3 release tag. We do NOT publish to PyPI — fetching from
    # GitHub keeps the install pinned to a specific release commit and
    # avoids a third-party package surface. Override DEXBTX_MINER_PKG_URL
    # to install from a fork or a different ref.
    DEXBTX_MINER_PKG_URL="${DEXBTX_MINER_PKG_URL:-https://github.com/dexbtx/minebtx/archive/refs/tags/v0.4.16.tar.gz}"
    log "installing dexbtx-miner from ${DEXBTX_MINER_PKG_URL} (pip --user)..."
    pip_install --upgrade "$DEXBTX_MINER_PKG_URL"

    # Make sure the pip --user bin dir is on PATH for the next session.
    case ":$PATH:" in
        *":$USER_BIN:"*) : ;;
        *) warn "dexbtx-miner was installed to $USER_BIN (not on PATH). Add to your shell rc: export PATH=\"$USER_BIN:\$PATH\"" ;;
    esac
fi

# ─── Fetch + verify solver ──────────────────────────────────────────────────
mkdir -p "${INSTALL_DIR}/bin"
TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT

if [[ -n "$LOCAL_SOLVER" ]]; then
    log "using local solver at ${LOCAL_SOLVER} (skipping download)"
    cp "$LOCAL_SOLVER" "$TMP"
else
    log "downloading patched btx-gbt-solve from ${SOLVER_URL}..."
    curl -fsSL "$SOLVER_URL" -o "$TMP"
fi

ACTUAL_SHA="$(sha256_of "$TMP")"
if [[ "$ACTUAL_SHA" != "$EXPECTED_SHA256" ]]; then
    err "solver SHA256 mismatch — expected $EXPECTED_SHA256, got $ACTUAL_SHA. Aborting (refusing to install untrusted binary)."
fi
log "solver SHA256 verified ($EXPECTED_SHA256)"

install -m 0755 "$TMP" "$SOLVER_PATH"
log "solver installed → $SOLVER_PATH"
if [[ "$ARCH" == "x86_64" || "$ARCH" == "amd64" ]]; then
    if ! ldd "$SOLVER_PATH" 2>/dev/null | grep -q 'libcudart'; then
        log "x86_64 solver: static CUDA runtime (driver CUDA 12/13, libcudart.so не нужен)"
    fi
fi

verify_solver_share_target
write_solver_platform_env

# ─── Config ─────────────────────────────────────────────────────────────────
if [[ -z "$ADDRESS" && "$SKIP_PROMPT" -eq 0 ]]; then
    echo
    echo "Enter your BTX payout address (format: btx1z...):"
    read -rp "  address: " ADDRESS
fi

if [[ -n "$ADDRESS" ]]; then
    if [[ ! "$ADDRESS" =~ ^btx1z[0-9a-zA-Z]{50,}$ ]]; then
        warn "address does not match expected btx1z... format — proceeding anyway, but double-check"
    fi
fi

if [[ -z "$WORKER" ]]; then
    WORKER="$(hostname -s 2>/dev/null || echo default)"
fi

# Solver backend + thread defaults from platform.
#   Apple Silicon: MLX (Apple's tuned array lib) edges the bespoke Metal
#   kernels by ~3-4% and is digest-equivalent (valid work either way). The
#   solver is CPU-prep-bound, so default ALL cores — on an M4, threads=4 left
#   ~50% of throughput on the table vs threads=ncpu (measured ~70 kN/s ->
#   ~133 kN/s). Fall back to "metal" in the config if MLX ever misbehaves.
#   NVIDIA: CUDA, threads=4 (prep-workers are the lever there, not threads).
#   Otherwise: CPU on all cores.
if [[ "$OS" == "Darwin" ]]; then
    SOLVER_BACKEND="mlx"
    SOLVER_THREADS="$(sysctl -n hw.ncpu 2>/dev/null || echo 4)"
elif [[ "$IS_ROCM" -eq 1 ]]; then
    # HIP masquerades as backend "cuda"; verified/possibly-downgraded by the
    # self-check below. Threads=4 (the GPU does the work).
    SOLVER_BACKEND="cuda"
    SOLVER_THREADS=4
elif [[ "$HAS_NVIDIA" -eq 1 ]]; then
    SOLVER_BACKEND="cuda"
    # v0.4.x — GPU-class-aware defaults from the 48h / 550-worker pool analysis.
    # The canonical winner across most cards (3060-4090, 5070/5080) is
    # SOLVER_THREADS=8 / PREPARE_WORKERS=16. We do NOT scale threads to nproc
    # anymore: over-threading STARVES fast cards (a 4090 drops 95%->68% at
    # THREADS=24; THREADS=24 + big batch is the single most common
    # underutilization trap on the network). Exceptions: slower cards want a
    # heavier CPU feed (16); the RTX 5090 wants 12/24 AND a dedicated, high-clock
    # CPU host (a 6-core Ryzen feeds it to 89%; a shared/oversubscribed cloud
    # EPYC stalls it ~70% regardless of config — that's a host choice, not tuning).
    NPROC="$(nproc 2>/dev/null || echo 8)"
    _gpu_uc="$(printf '%s' "$GPU_NAME" | tr '[:lower:]' '[:upper:]')"
    if printf '%s' "$_gpu_uc" | grep -qE '5090|PRO 6000'; then
        SOLVER_THREADS=12; GPU_WORKERS=24
    elif printf '%s' "$_gpu_uc" | grep -qE '5060|4060|3060|3070|1060|1070|1080|LAPTOP'; then
        SOLVER_THREADS=16; GPU_WORKERS=24   # slower cards: heavier CPU feed helps
    else
        SOLVER_THREADS=8; GPU_WORKERS=16    # canonical winner (4090/4080/5080/5070/4070/3090/3080/...)
    fi
    # Floor to the host's thread budget on small machines.
    _budget=$(( NPROC - 2 < 4 ? 4 : NPROC - 2 ))
    [ "$SOLVER_THREADS" -gt "$_budget" ] && SOLVER_THREADS="$_budget"
else
    SOLVER_BACKEND="cpu"
    SOLVER_THREADS="$(nproc 2>/dev/null || echo 4)"
fi

# Write config (preserve existing fields if file exists)
if [[ -f "$CONFIG_PATH" ]]; then
    log "config exists at $CONFIG_PATH — preserving (override with --yes to regenerate)"
    if [[ "$ASSUME_YES" -eq 1 ]]; then
        cp "$CONFIG_PATH" "${CONFIG_PATH}.bak.$(date +%s)"
        log "backed up old config"
    fi
fi

if [[ ! -f "$CONFIG_PATH" || "$ASSUME_YES" -eq 1 ]]; then
    # v0.4.4 — empirically-found optimal across the GPU-config sweep on
    # home-1070 (GTX 1070, Pascal sm_61). Pre-v0.4.4 we set Pascal-specific
    # batch=32/prefetch=4/workers=4 thinking it was the "Pascal sweet spot,"
    # but the sweep showed batch=128/prefetch=8/workers=16 works on Pascal
    # too — the actual lever is solver_threads (handled above, auto-scaled
    # from nproc). batch=128/workers=16/prefetch=8 + threads=24 hit
    # 108 kN/s on the 1070 (was 30 kN/s at the old "Pascal sweet spot").
    # Modern cards retain the same shape and benefit from the same
    # parallelism — sweep across hardware classes hasn't shown a card that
    # PREFERS batch<128, so we set one default that works fleet-wide.
    GPU_BATCH=128
    GPU_PREFETCH=8
    GPU_WORKERS="${GPU_WORKERS:-16}"   # GPU-class-aware value set above (24 for slow cards / 5090); 16 default
    cat > "$CONFIG_PATH" <<YAML
# Th0rn BTX Pool — dexbtx-miner config (install.sh v${INSTALL_SH_VERSION})
pool_host: "${POOL%:*}"
pool_port: ${POOL##*:}
pool_tls: false

payout_address: "${ADDRESS}"
worker_name: "${WORKER}"

gbt_solve_path: "${SOLVER_PATH}"

solver_backend: "${SOLVER_BACKEND}"
solver_threads: ${SOLVER_THREADS}
solver_batch_size: ${GPU_BATCH}
solver_prefetch_depth: ${GPU_PREFETCH}
solver_prepare_workers: ${GPU_WORKERS}
solver_pipeline_async: 1
gpu_inputs: ${GPU_INPUTS}

nonces_per_slice: 20000000
solver_max_seconds_per_slice: 5.0
reconnect_initial_s: 1.0
reconnect_max_s: 60.0

log_level: "INFO"
YAML
    log "config written → $CONFIG_PATH (profile: batch=${GPU_BATCH} prefetch=${GPU_PREFETCH} workers=${GPU_WORKERS})"
else
    # Multi-GPU: всегда gpu_inputs=1 + cuda (отдельный процесс на карту через start-btx-multigpu-screen.sh).
    if [[ "$HAS_NVIDIA" -eq 1 && "$GPU_COUNT" -gt 1 && -f "$CONFIG_PATH" ]]; then
        _cur=$(grep -E '^gpu_inputs:' "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' || echo "")
        _backend=$(grep -E '^solver_backend:' "$CONFIG_PATH" 2>/dev/null | sed 's/.*: *//' | tr -d '"' || echo "")
        if [[ "$_cur" != "1" || "$_backend" != "cuda" ]]; then
            cp "$CONFIG_PATH" "${CONFIG_PATH}.bak.multigpu.$(date +%s)"
            sed -i 's/^solver_backend:.*/solver_backend: "cuda"/' "$CONFIG_PATH"
            sed -i 's/^gpu_inputs:.*/gpu_inputs: 1/' "$CONFIG_PATH"
            log "updated multi-GPU config (backend=${_backend:-?}→cuda, gpu_inputs=${_cur:-?}→1)"
        fi
    fi
fi

install_launch_scripts() {
    mkdir -p "$INSTALL_DIR"
    local base="${TH0RN_FILES_BASE:-$(echo "${TH0RN_INSTALL_URL:-https://files.th0rn.ru/files/btx/install.sh}" | sed 's#/install\.sh$##')}"
    local self=""
    self="$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || true)"
    if [[ -n "$self" && -f "$(dirname "$self")/start-btx-screen.sh" ]]; then
        cp "$(dirname "$self")/start-btx-screen.sh" "${INSTALL_DIR}/start-screen.sh"
        cp "$(dirname "$self")/start-btx-multigpu-screen.sh" "${INSTALL_DIR}/start-btx-multigpu-screen.sh"
        chmod +x "${INSTALL_DIR}/start-screen.sh" "${INSTALL_DIR}/start-btx-multigpu-screen.sh"
        log "launch scripts (local) → ${INSTALL_DIR}/start-screen.sh"
        return 0
    fi
    if curl -fsSL --connect-timeout 10 "${base}/start-btx-screen.sh" -o "${INSTALL_DIR}/start-screen.sh" \
       && curl -fsSL --connect-timeout 10 "${base}/start-btx-multigpu-screen.sh" -o "${INSTALL_DIR}/start-btx-multigpu-screen.sh"; then
        chmod +x "${INSTALL_DIR}/start-screen.sh" "${INSTALL_DIR}/start-btx-multigpu-screen.sh"
        log "launch scripts (files) → ${INSTALL_DIR}/start-screen.sh"
        return 0
    fi
    warn "не удалось скачать start-screen.sh — скопируй вручную из репозитория btx-pool/scripts/"
    return 1
}

install_launch_scripts || true

# ─── AMD/ROCm HIP correctness self-check ─────────────────────────────────────
# The HIP kernel is EXPERIMENTAL and cannot be verified without real AMD
# hardware (CI has no AMD GPU). So verify it HERE, on the user's actual GPU:
# run the deterministic post-125000 V2 reference vector through both the HIP
# backend ("cuda") and the CPU backend and require both to equal the known
# reference digest. On mismatch we downgrade the config to CPU mining rather
# than let it submit shares the pool will reject.
if [[ "$IS_ROCM" -eq 1 ]]; then
    log "running AMD HIP-vs-CPU correctness self-check on your GPU (experimental backend)..."
    SC_VEC='{"version":536870912,"prev_hash":"00000000000000000000000000000000000000000000000000000000000000ab","merkle_root":"00000000000000000000000000000000000000000000000000000000000000cd","time":1780000000,"bits":"207fffff","seed_a":"1111111111111111111111111111111111111111111111111111111111111111","seed_b":"2222222222222222222222222222222222222222222222222222222222222222","block_height":130000,"nonce_start":0,"max_tries":256,"max_seconds":60,"share_target":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}'
    _digest() { echo "$SC_VEC" | BTX_MATMUL_BACKEND="$1" "$SOLVER_PATH" --daemon --backend "$1" --solver-threads 1 2>/dev/null \
        | grep -oE '"matmul_digest"[^0-9a-f]*[0-9a-f]{64}' | grep -oE '[0-9a-f]{64}' | head -1; }
    SC_CPU="$(_digest cpu)"
    SC_HIP="$(_digest cuda)"   # "cuda" == HIP in this build
    if [[ "$SC_HIP" == "$SELFCHECK_REF_DIGEST" && "$SC_CPU" == "$SELFCHECK_REF_DIGEST" ]]; then
        log "self-check PASS ✓ HIP digest == CPU == reference — AMD backend is consensus-correct on this GPU."
    else
        warn "self-check FAILED — the experimental HIP/ROCm kernel is NOT consensus-correct on this GPU."
        warn "  cpu=${SC_CPU:-<none>}  hip=${SC_HIP:-<none>}  ref=${SELFCHECK_REF_DIGEST}"
        warn "  Downgrading to CPU mining (solver_backend: cpu) so you don't submit rejected shares."
        warn "  Please report your GPU model + gfx arch so the HIP kernel can be fixed (likely the"
        warn "  wavefront-size / __shfl_down_sync path). GPU mining stays disabled until then."
        if [[ -f "$CONFIG_PATH" ]]; then
            sed -i.bak 's/^solver_backend:.*/solver_backend: "cpu"   # auto-downgraded: HIP self-check failed/' "$CONFIG_PATH" 2>/dev/null \
              || sed -i '' 's/^solver_backend:.*/solver_backend: "cpu"/' "$CONFIG_PATH" 2>/dev/null || true
        fi
    fi
fi

# ─── Hard GPU acceleration smoke test ───────────────────────────────────────
# Runs the solver with --backend cuda against a deterministic input. If the
# CUDA backend doesn't engage (driver missing, sm not supported, CUDA OOM,
# etc.) the solver falls back to CPU which is ~100× slower — the alpha
# cohort would never know. Fail HARD so the operator notices at install time.
if [[ "$HAS_NVIDIA" -eq 1 ]]; then
    setup_cuda_library_path
    log "running GPU acceleration smoke test (5-10 seconds)..."
    SMOKE_OUT="$("$SOLVER_PATH" \
        --version 536870912 \
        --prev-hash 0ab38fdff2ef667dcddac7f50c3696080c26697615f7b6b9af5c3a1ba0a5fb7e \
        --merkle-root d906f02ed11d8936770423263b56c5ffe1ea1b15c8a2867afb161adb6fd76eb7 \
        --time 1779672814 --bits 0x1d17c609 \
        --share-target 00ffffff00000000000000000000000000000000000000000000000000000000 \
        --seed-a 8460daf3ff446cc55a7115de88ee24c8a2bf182eedde43abb9cf4cc94cc209bf \
        --seed-b 7f2e377616feb92d2e9857cab390595b7d6b8d24373a2da394f8d97197b5f437 \
        --block-height 110806 --nonce-start 1 \
        --max-tries 200000 --max-seconds 30 \
        --backend cuda --solver-threads 4 --batch-size 32 2>&1 || true)"
    SMOKE_LAST_LINE="$(echo "$SMOKE_OUT" | grep -E '^\{.*\}$' | tail -1)"
    if [[ -z "$SMOKE_LAST_LINE" ]]; then
        echo "$SMOKE_OUT" | tail -10 >&2
        err "GPU smoke test: solver produced no JSON output. CUDA backend likely failed to initialize. Aborting."
    fi
    if echo "$SMOKE_LAST_LINE" | grep -q '"found":true'; then
        ELAPSED="$(echo "$SMOKE_LAST_LINE" | sed -E 's/.*"elapsed_s":([0-9.e+-]+).*/\1/')"
        log "GPU smoke test: PASS (found a share in ${ELAPSED}s)"
    else
        echo "$SMOKE_LAST_LINE" >&2
        warn "GPU smoke test: solver ran but didn't find a share — could be hard luck OR CPU fallback. Continuing, but watch first share latency."
    fi
fi

# ─── systemd (fleet rigs) ───────────────────────────────────────────────────
if [[ "$SYSTEMD" -eq 1 ]]; then
    SUDO=""
    [[ $(id -u) -ne 0 ]] && command -v sudo >/dev/null 2>&1 && SUDO="sudo"
    [[ $(id -u) -ne 0 && -z "$SUDO" ]] && err "--systemd requires root or sudo"

    MINER_BIN=""
    for cand in dexbtx-miner "${USER_BIN}/dexbtx-miner" /usr/local/bin/dexbtx-miner; do
        if command -v "$cand" >/dev/null 2>&1; then MINER_BIN="$(command -v "$cand")"; break; fi
        [[ -x "$cand" ]] && MINER_BIN="$cand" && break
    done
    [[ -n "$MINER_BIN" ]] || err "dexbtx-miner not found after pip install"

    $SUDO install -d -m 755 /usr/local/bin "${INSTALL_DIR}/bin"
    # config + solver already written under ${INSTALL_DIR} when --systemd

    LAUNCHER="/usr/local/bin/btx-miner"
    $SUDO tee "$LAUNCHER" >/dev/null <<LAUNCH
#!/usr/bin/env bash
exec "${MINER_BIN}" --config "${INSTALL_DIR}/config.yaml" "\$@"
LAUNCH
    $SUDO chmod 755 "$LAUNCHER"

    $SUDO tee /etc/systemd/system/btx-miner.service >/dev/null <<UNIT
[Unit]
Description=Th0rn BTX miner (dexbtx-miner)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=${INSTALL_DIR}
Environment=PATH=${USER_BIN}:/usr/local/bin:/usr/bin:/bin
ExecStart=${LAUNCHER}
Restart=on-failure
RestartSec=15
TimeoutStopSec=30
StandardOutput=journal
StandardError=journal
SyslogIdentifier=btx-miner

[Install]
WantedBy=multi-user.target
UNIT

    $SUDO systemctl daemon-reload
    $SUDO systemctl enable btx-miner.service
    { $SUDO systemctl stop btx-miner.service 2>/dev/null || true; } 2>/dev/null
    pkill -9 -f th0rn-miner 2>/dev/null || true
    pkill -9 -f 'btx-gbt-solve' 2>/dev/null || true
    sleep 2
    $SUDO systemctl restart btx-miner.service
    log "systemd unit installed → btx-miner.service (restarted)"
fi

# ─── Summary ────────────────────────────────────────────────────────────────
# Resolve the launch command: bare name if it's on PATH, otherwise the full
# pip --user path so the printed command actually works (esp. on macOS where
# ~/Library/Python/X.Y/bin is rarely on PATH).
if command -v dexbtx-miner >/dev/null 2>&1; then
    MINER_CMD="dexbtx-miner"
else
    MINER_CMD="${USER_BIN}/dexbtx-miner"
fi

echo
log "✓ Th0rn BTX miner installed (dexbtx-miner v0.4.16 + ${PREBUILDS_TAG})."
echo
echo "  Pool:     ${POOL} (PPLNS)"
echo "  Address:  ${ADDRESS:-<edit ${CONFIG_PATH}>}"
echo "  Worker:   ${WORKER}"
echo "  GPU:      ${GPU_NAME:-CPU only} × ${GPU_COUNT:-0} (gpu_inputs=${GPU_INPUTS})"
echo "  Backend:  ${SOLVER_BACKEND}"
echo "  Config:   ${CONFIG_PATH}"
echo "  Dashboard: https://btx.th0rn.space/"
echo
if [[ "$SYSTEMD" -eq 1 ]]; then
    echo "  systemctl restart btx-miner && journalctl -u btx-miner -f"
    echo
else
    echo "Launch (screen, рекомендуется):"
    echo "  ${INSTALL_DIR}/start-screen.sh"
    if [[ "$GPU_COUNT" -gt 1 ]]; then
        echo "  # multi-GPU (${GPU_COUNT} cards): воркеры ${WORKER}-g0 … ${WORKER}-g$((GPU_COUNT - 1)), screen btx-g0 …"
    else
        echo "  # 1 GPU: screen -r btx"
    fi
    echo
    echo "Launch (вручную):"
    echo "  ${MINER_CMD} --config ${CONFIG_PATH}"
    echo
fi
echo "Tune GPU: ${MINER_CMD} benchmark --write-config"
echo "Check load: watch -n1 nvidia-smi"
echo
