#!/usr/bin/env bash
# Migrate per-customer Valkey storage from /home/<user>/valkey to
# /docker/users/<user>/valkey AND repair any existing-enabled customers whose
# valkey container is dead because of the original path bug.
#
# The bug: ValkeyMgr::host_dir() returned /home/<user>/valkey, which the WHP
# safety-net open_basedir blocks. enable() failed to write valkey.conf to
# /home/<user>/valkey/valkey.conf (suppressed warning, unchecked return),
# Docker auto-created the bind source as the image's USER, the container
# started "successfully" via the API and immediately crashed with "can't
# open /data/valkey.conf", and the WHP customer_valkey row was set to
# enabled=1 anyway. So we have a class of customers with enabled=1 but a
# dead container.
#
# This script:
#   1. Lists every customer_valkey row currently marked enabled=1.
#   2. For each, force-removes the (likely-dead) Valkey container.
#   3. Cleans up the (almost certainly empty) /home/<user>/valkey directory.
#      Only removes if empty or contains only the auto-created Docker source —
#      never touches real customer files.
#   4. Calls ValkeyMgr::recreate() via PHP heredoc, which uses the NEW
#      /docker/users/<user>/valkey path and starts a fresh, healthy container.
#
# Idempotent: customers whose containers ARE healthy on the new path are
# left untouched (the inspect Running check covers that case).

set -euo pipefail

LOG_PREFIX="[migrate-valkey-host-dir]"
log() { echo "$LOG_PREFIX $*"; }

if ! command -v mysql >/dev/null 2>&1; then
    log "mysql CLI not found; skipping."
    exit 0
fi

if ! mysql whp -BN -e "SHOW TABLES LIKE 'customer_valkey'" 2>/dev/null | grep -q customer_valkey; then
    log "customer_valkey table not present; skipping."
    exit 0
fi

mapfile -t USERS < <(mysql whp -BN -e "SELECT username FROM customer_valkey WHERE enabled = 1" 2>/dev/null)

if [[ ${#USERS[@]} -eq 0 ]]; then
    log "no enabled customer_valkey rows; nothing to migrate."
    exit 0
fi

log "evaluating ${#USERS[@]} enabled customer(s)"

migrated=0
already_ok=0
failed=0

for user in "${USERS[@]}"; do
    container="valkey-cust-${user}"
    old_dir="/home/${user}/valkey"
    new_dir="/docker/users/${user}/valkey"

    # If the container is alive AND the new bind-mount path is in use, nothing to do.
    bind=$(docker inspect "$container" -f '{{range .HostConfig.Binds}}{{.}}{{end}}' 2>/dev/null || true)
    state=$(docker inspect "$container" -f '{{.State.Status}}' 2>/dev/null || true)
    if [[ "$state" == "running" && "$bind" == "${new_dir}:/data" ]]; then
        log "$user: already healthy on new path; skip"
        already_ok=$((already_ok + 1))
        continue
    fi

    log "$user: state=$state bind=$bind — migrating"

    # 1. Tear down whatever container exists (likely dead, but force anyway).
    if [[ -n "$state" ]]; then
        docker rm -f "$container" >/dev/null 2>&1 || true
    fi

    # 2. Clean up old empty dir. Safety: only delete if no valkey.conf and no
    # data/ subdir with content. This protects against any case where the
    # customer somehow had a real /home/<user>/valkey/ with data.
    if [[ -d "$old_dir" ]]; then
        # Count entries beyond . and ..
        entries=$(find "$old_dir" -mindepth 1 -maxdepth 1 2>/dev/null | wc -l)
        has_conf=0
        [[ -f "$old_dir/valkey.conf" ]] && has_conf=1
        data_has_files=0
        if [[ -d "$old_dir/data" ]]; then
            data_files=$(find "$old_dir/data" -type f 2>/dev/null | wc -l)
            (( data_files > 0 )) && data_has_files=1
        fi
        if (( has_conf == 0 && data_has_files == 0 )); then
            log "$user: removing empty $old_dir"
            rm -rf "$old_dir" 2>/dev/null || log "$user: WARN: couldn't remove $old_dir"
        else
            log "$user: WARN: $old_dir has real content (conf=$has_conf, data_files=$data_has_files); leaving it"
        fi
    fi

    # 3. Recreate via the corrected ValkeyMgr code path. recreate() uses
    # current customer_valkey settings (memory_mb, mode, eviction_policy)
    # so we don't have to re-enter them.
    #
    # CRITICAL: update.sh runs per-version scripts BEFORE rsyncing web-files/,
    # so /docker/whp/web/libs/valkey-mgmt.php is the OLD pre-2026.06.22
    # version when this script first fires during deploy. The old version has
    # host_dir() returning /home/<u>/valkey — exactly the bug we're trying to
    # migrate away from. Loading it here would re-create at the old path.
    #
    # Resolve the NEW valkey-mgmt.php from the tarball via $BASH_SOURCE-relative
    # path: tarball extract root has scripts/updates/2026.06.20/<this> AND
    # web-files/libs/valkey-mgmt.php. Fall back to /docker/whp/web/libs/ only
    # when running standalone (already-deployed re-run), where the on-disk
    # copy IS the new version.
    script_dir=$(dirname "$(readlink -f "$BASH_SOURCE")")
    tarball_valkey="$script_dir/../../../web-files/libs/valkey-mgmt.php"
    if [[ -f "$tarball_valkey" ]]; then
        valkey_mgmt_path=$(readlink -f "$tarball_valkey")
        log "$user: loading tarball valkey-mgmt.php from $valkey_mgmt_path"
    else
        valkey_mgmt_path="/docker/whp/web/libs/valkey-mgmt.php"
        log "$user: tarball valkey-mgmt.php not at $tarball_valkey (re-run mode); using deployed copy"
    fi

    # Run PHP from a temp file rather than heredoc — heredoc + set -e + a
    # PHP fatal error produced a silent script-exit during initial testing
    # (the migration script printed the heredoc body but skipped the
    # success/fail branch). A temp file with explicit `|| true` makes the
    # exit code handling unambiguous.
    php_script=$(mktemp /tmp/whp-valkey-recreate.XXXXXX.php)
    trap "rm -f '$php_script'" EXIT
    cat > "$php_script" <<PHP
<?php
require_once('/docker/whp/web/configs/config.php');
require_once('${valkey_mgmt_path}');
\$username = \$argv[1] ?? '';
if (\$username === '') { fwrite(STDERR, "no username arg\n"); exit(2); }
// config.php exposes MYSQL_PASS but no global \$db. Construct our own
// PDO matching the pattern used by other top-level CLI scripts.
\$db = new PDO(
    'mysql:host=127.0.0.1;dbname=whp;charset=utf8mb4',
    'root',
    trim(MYSQL_PASS),
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
\$mgr = new ValkeyMgr(\$db);
// recreate(username, app_containers): pass [] because the per-customer
// network already has the app containers attached from the original
// enable() — we're swapping the valkey container in place, not the net.
\$res = \$mgr->recreate(\$username, []);
if (!empty(\$res['success'])) {
    fwrite(STDOUT, "ok\n");
    exit(0);
}
fwrite(STDERR, "recreate failed: " . (\$res['error'] ?? 'unknown') . "\n");
exit(1);
PHP

    if php "$php_script" "$user"; then
        log "$user: recreated on new path"
        migrated=$((migrated + 1))
    else
        log "$user: recreate failed"
        failed=$((failed + 1))
    fi
done

log "done: migrated=$migrated, already_ok=$already_ok, failed=$failed"
