#!/usr/bin/env bash
#
# Set up a fresh Werkator instance on a new Docker host by converting a legacy
# .werkator env configuration to the new YAML format.
#
# The legacy config comes either from a file passed as the optional third
# argument, or — when omitted — from the embedded snapshot of the hs.hsadmin.ng
# .werkator on vm2176. Either way the script sources it, converts it to YAML
# (mapping the legacy env vars to YAML keys), substitutes the public hostname
# from the command line, and prompts for the Gitea secrets.
#
# Run this ON the new Docker host. The repository must already be cloned at the
# given repo-dir. Builds run in Docker; Werkator itself runs from the jar via
# java, so a JRE and the jar are required (see checks below).
#
# Usage: setup-werkator-instance [--force] <public-hostname> <repo-dir> [legacy-config]
#   <public-hostname>  FQDN this instance is reachable at, e.g. vm2180.hostsharing.net
#                      (used for server.publicBaseUrl)
#   <repo-dir>         path to the cloned git repository to configure,
#                      e.g. ~/hs.hsadmin.ng
#   [legacy-config]    optional path to a legacy .werkator file to convert;
#                      if omitted, the embedded vm2176 snapshot is used
#   --force            overwrite .werkator.yml files that already exist

set -euo pipefail

# ---------------------------------------------------------------- settings --

JAR_PATH="$HOME/bin/werkator.jar"              # where the Werkator jar is expected

die()  { echo "ERROR: $*" >&2; exit 1; }
warn() { echo "WARNING: $*" >&2; }

usage() {
    echo "usage: setup-werkator-instance [--force] <public-hostname> <repo-dir> [legacy-config]" >&2
    echo "example: setup-werkator-instance vm2180.hostsharing.net ~/hs.hsadmin.ng" >&2
    exit 1
}

# --------------------------------------------------------------- arguments --

force=false
positional=()
for arg in "$@"; do
    case "$arg" in
        --force) force=true ;;
        -*) die "unknown option: $arg" ;;
        *) positional+=("$arg") ;;
    esac
done
case ${#positional[@]} in
    2) legacy_file="" ;;
    3) legacy_file="${positional[2]}" ;;
    *) usage ;;
esac

hostname="${positional[0]}"
REPO_DIR="${positional[1]}"
[ -z "$legacy_file" ] || [ -f "$legacy_file" ] || die "legacy config not found: $legacy_file"

# ---------------------------------------------------- legacy config source --
# load_legacy_config populates the werkator_* variables, either by sourcing the
# file passed on the command line, or from the embedded vm2176 snapshot below.
# Secrets (git username/token) are intentionally never read here — they are
# prompted for, so a token in a legacy file is not echoed or reused implicitly.

load_legacy_config() {
    if [ -n "$legacy_file" ]; then
        # shellcheck source=/dev/null
        . "$legacy_file"
        return
    fi
    # embedded, non-secret snapshot of tallyman@vm2176:~/hs.hsadmin.ng/.werkator
    export WERKATOR_BUILD_COMMAND='./gradlew --console=plain --no-daemon --no-build-cache jacocoTestReport check generateDocumentation -x pitest -x dependencyCheckAnalyze'
    export WERKATOR_BUILD_CLEAN_COMMAND='rm -rf build'
    export WERKATOR_BUILD_ARTEFACT_DIRS='build/reports:build/doc'
    export WERKATOR_BUILD_STDOUT_LOG='build.stdout.log'
    export WERKATOR_BUILD_STDERR_LOG='build.stderr.log'
    export WERKATOR_BUILD_DOCKER_IMAGE='hsadmin-ng-build-env:latest'
    export WERKATOR_BUILD_DOCKERFILE='Jenkins/jenkins-agent/Dockerfile'
    export WERKATOR_BUILD_DOCKER_CONTEXT='Jenkins/jenkins-agent'
    export WERKATOR_BUILD_DOCKER_NETWORK='host'
    export WERKATOR_BUILD_DOCKER_ENV='TESTCONTAINERS_RYUK_DISABLED=true'
    # legacy hsadmin-ng-specific JAVA_TOOL_OPTIONS — carried into docker.env so the
    # Testcontainers builds keep finding the docker socket (see migration doc).
    export WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS='-Ddocker.client.strategy=org.testcontainers.dockerclient.UnixSocketClientProviderStrategy -Dtestcontainers.docker.socket.override=/var/run/docker.sock'
    export WERKATOR_ARTIFACT_SERVER_PORT='18080'
    export WERKATOR_ARTIFACT_SERVER_BIND_ADDRESS='0.0.0.0'
    export WERKATOR_ARTIFACT_BUILD_RETENTION_PER_BRANCH='3'
    export WERKATOR_IMPRESSUM_URL='https://michael.hoennig.de/imprint.html'
    export WERKATOR_GITEA_BASE_URL='https://dev.hostsharing.net'
    export WERKATOR_GITEA_OWNER='hostsharing'
    export WERKATOR_GITEA_REPO='hs.hsadmin.ng'
    export WERKATOR_GITEA_STATUS_CONTEXT='werkator'
}

# ----------------------------------------------------------------- helpers --

# quote a scalar for double-quoted YAML
yaml_quote() {
    local s=${1//\\/\\\\}
    s=${s//\"/\\\"}
    printf '"%s"' "$s"
}

# ------------------------------------------------------------ preconditions --

[ -d "$REPO_DIR/.git" ] || die "$REPO_DIR is not a git clone — clone the repository there first"
command -v docker >/dev/null 2>&1 || die "docker not found — this setup targets a Docker host"

have_jar=true
[ -f "$JAR_PATH" ] || { have_jar=false; warn "jar not found at $JAR_PATH — config will be written, but copy the jar there before running init"; }

have_java=true
if ! command -v java >/dev/null 2>&1; then
    have_java=false
    warn "no java in PATH — install a JRE (e.g. Temurin 21 into ~/opt) before running init/server"
fi

# ----------------------------------------------------------- prompt secrets --

# suggest the account from the legacy config's git username, if it sets one
suggested_account=$( set +u; load_legacy_config >/dev/null 2>&1; printf '%s' "${WERKATOR_GITEA_GIT_USERNAME:-jenkins-ci}" )

echo "== Gitea credentials for this instance (used for git HTTPS auth + status API)"
read -r -p "  Gitea git account [$suggested_account]: " git_account
git_account=${git_account:-$suggested_account}
read -r -s -p "  Gitea API token: " git_token; echo
[ -n "$git_token" ] || die "the Gitea token must not be empty"

# -------------------------------------------------------------- convert cfg --

project_yml="$REPO_DIR/.werkator.yml"
machine_yml="$REPO_DIR/.git/werkator/.werkator.yml"
public_base_url="https://$hostname/"

if ! $force; then
    for f in "$project_yml" "$machine_yml"; do
        [ -e "$f" ] && die "$f already exists — re-run with --force to overwrite"
    done
fi

config_source=${legacy_file:-"embedded vm2176 snapshot"}
echo "== writing $project_yml (public host: $hostname, source: $config_source)"

(
    set +u
    load_legacy_config

    # warn about legacy keys that have no counterpart in the new config
    for var in \
        WERKATOR_ARTIFACT_NGINX_SERVER_NAME WERKATOR_ARTIFACT_NGINX_HTTP_PORT \
        WERKATOR_ARTIFACT_NGINX_HTTPS_PORT WERKATOR_ARTIFACT_LETSENCRYPT_EMAIL \
        WERKATOR_ARTIFACT_AUTH_MODE WERKATOR_BUILD_DOCKER_PREFLIGHT_COMMAND \
        WERKATOR_INSTALL_DIR
    do
        [ -n "${!var}" ] && warn "$var is set but has no counterpart in the new config — skipped"
    done

    # retention: strip a legacy age suffix (h/d), only the build count is supported
    retention="$WERKATOR_ARTIFACT_BUILD_RETENTION_PER_BRANCH"
    if [ -n "$retention" ] && [[ ! "$retention" =~ ^[0-9]+$ ]]; then
        warn "retention '$retention' has an age suffix — only the build count is supported, using '${retention%%[!0-9]*}'"
        retention=${retention%%[!0-9]*}
    fi

    emit() { printf '%s\n' "$1" >>"$project_yml"; }

    : >"$project_yml"
    emit "# Werkator configuration, converted from $config_source"
    emit "# by tools/setup-werkator-instance; public host set to $hostname."
    emit "# Omitted keys fall back to the application defaults — see docs/configuration.md."
    emit ""

    emit "server:"
    emit "  publicBaseUrl: $(yaml_quote "$public_base_url")"
    [ -n "$WERKATOR_ARTIFACT_SERVER_PORT" ]         && emit "  port: $WERKATOR_ARTIFACT_SERVER_PORT"
    [ -n "$WERKATOR_ARTIFACT_SERVER_BIND_ADDRESS" ] && emit "  bindAddress: $WERKATOR_ARTIFACT_SERVER_BIND_ADDRESS"
    [ -n "$WERKATOR_IMPRESSUM_URL" ]                && emit "  impressumUrl: $(yaml_quote "$WERKATOR_IMPRESSUM_URL")"
    emit ""

    emit "gitea:"
    [ -n "$WERKATOR_GITEA_BASE_URL" ]       && emit "  baseUrl: $(yaml_quote "$WERKATOR_GITEA_BASE_URL")"
    [ -n "$WERKATOR_GITEA_OWNER" ]          && emit "  owner: $(yaml_quote "$WERKATOR_GITEA_OWNER")"
    [ -n "$WERKATOR_GITEA_REPO" ]           && emit "  repo: $(yaml_quote "$WERKATOR_GITEA_REPO")"
    [ -n "$WERKATOR_GITEA_STATUS_CONTEXT" ] && emit "  statusContext: $(yaml_quote "$WERKATOR_GITEA_STATUS_CONTEXT")"
    emit ""

    [ -n "$retention" ] && { emit "artifacts:"; emit "  retentionPerBranch: $retention"; emit ""; }

    [ -n "$WERKATOR_NEW_BRANCH_COMMIT_MAX_AGE" ] && { emit "watcher:"; emit "  newBranchMaxAge: $WERKATOR_NEW_BRANCH_COMMIT_MAX_AGE"; emit ""; }

    emit "branches:"
    emit "  default:"
    [ -n "$WERKATOR_BUILD_CLEAN_COMMAND" ] && emit "    cleanCommand: $(yaml_quote "$WERKATOR_BUILD_CLEAN_COMMAND")"
    [ -n "$WERKATOR_BUILD_COMMAND" ]       && emit "    buildCommand: $(yaml_quote "$WERKATOR_BUILD_COMMAND")"
    if [ -n "$WERKATOR_BUILD_ARTEFACT_DIRS" ]; then
        emit "    artifactDirs:"
        # legacy separators: ';' (documented) and ':' (as used on vm2176)
        IFS=';:' read -r -a dirs <<<"$WERKATOR_BUILD_ARTEFACT_DIRS"
        for dir in "${dirs[@]}"; do
            [ -n "$dir" ] && emit "      - $(yaml_quote "$dir")"
        done
    fi
    [ -n "$WERKATOR_BUILD_STDOUT_LOG" ] && emit "    stdoutLog: $(yaml_quote "$WERKATOR_BUILD_STDOUT_LOG")"
    [ -n "$WERKATOR_BUILD_STDERR_LOG" ] && emit "    stderrLog: $(yaml_quote "$WERKATOR_BUILD_STDERR_LOG")"

    if [ -n "$WERKATOR_BUILD_DOCKER_IMAGE" ] || [ -n "$WERKATOR_BUILD_DOCKERFILE" ]; then
        emit "    docker:"
        emit "      enabled: true"
        [ -n "$WERKATOR_BUILD_DOCKER_IMAGE" ]    && emit "      image: $(yaml_quote "$WERKATOR_BUILD_DOCKER_IMAGE")"
        [ -n "$WERKATOR_BUILD_DOCKERFILE" ]      && emit "      dockerfile: $(yaml_quote "$WERKATOR_BUILD_DOCKERFILE")"
        [ -n "$WERKATOR_BUILD_DOCKER_CONTEXT" ]  && emit "      context: $(yaml_quote "$WERKATOR_BUILD_DOCKER_CONTEXT")"
        # legacy default network was host; new default is Docker's default
        emit "      network: $(yaml_quote "${WERKATOR_BUILD_DOCKER_NETWORK:-host}")"
        if [ -n "$WERKATOR_BUILD_DOCKER_ENV" ] || [ -n "$WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS" ]; then
            emit "      env:"
            for assignment in $WERKATOR_BUILD_DOCKER_ENV; do
                emit "        ${assignment%%=*}: $(yaml_quote "${assignment#*=}")"
            done
            # hsadmin-ng-specific JAVA_TOOL_OPTIONS carried into docker.env so the
            # Testcontainers builds keep finding the docker socket (see migration doc)
            [ -n "$WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS" ] && \
                emit "        JAVA_TOOL_OPTIONS: $(yaml_quote "$WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS")"
        fi
    fi

    # legacy auto-build: a branch list plus global times -> per-branch autoBuild
    if [ -n "$WERKATOR_AUTO_BUILD_BRANCHES" ]; then
        times_yaml=""
        for t in ${WERKATOR_AUTO_BUILD_TIMES//,/ }; do
            times_yaml+="${times_yaml:+, }$(yaml_quote "$t")"
        done
        for branch in ${WERKATOR_AUTO_BUILD_BRANCHES//,/ }; do
            emit "  $(yaml_quote "$branch"):"
            emit "    autoBuild:"
            emit "      enabled: true"
            [ -n "$times_yaml" ] && emit "      times: [$times_yaml]"
        done
    fi
)

# -------------------------------------------------------- machine + secrets --

echo "== writing $machine_yml (secrets, mode 600)"
mkdir -p "$(dirname "$machine_yml")"
# the umask in the subshell makes the file mode 600 at creation, so the token is
# never world-readable — not even between the redirect and a follow-up chmod;
# an existing file is removed first, because the redirect would keep its mode
rm -f "$machine_yml"
(
    umask 077
    {
        echo "# Machine-specific overrides and secrets. Keys here win over .werkator.yml."
        echo "git:"
        echo "  account: $(yaml_quote "$git_account")"
        echo "  token: $(yaml_quote "$git_token")"
    } >"$machine_yml"
)

echo "== converted project config:"
sed 's/^/    /' "$project_yml"

# ------------------------------------------------------------------- finish --

if $have_jar && $have_java; then
    echo "== running init (keeps the config files just written)"
    ( cd "$REPO_DIR" && java -jar "$JAR_PATH" init )
fi

cat <<EOF

Done. Werkator config for $hostname is in place.

Verify and start on this host:

    cd $REPO_DIR
    java -jar $JAR_PATH config:print --full     # check the effective config (git.token masked)
    java -jar $JAR_PATH config:print --full --show-secrets   # ... including git.token in clear text
    java -jar $JAR_PATH server                  # or: java -jar $JAR_PATH init --systemd

Note: Werkator binds to $hostname:${WERKATOR_ARTIFACT_SERVER_PORT:-18080} over plain HTTP.
publicBaseUrl is https://$hostname/ — terminate TLS in front of it with the host's
reverse proxy (managed nginx/TLS is ADR 0005 / docs/plan/13-nginx-tls.md, not yet built).
EOF
