pipeline {
    agent { label '\u6784\u5efa\u673a1' }

    options {
        timestamps()
        disableConcurrentBuilds()
        skipDefaultCheckout(true)
    }

    environment {
        REGISTRY_URL      = 'reg.nxsir.cn'
        API_IMAGE_NAME    = 'liverecorder/app-api'
        WEB_IMAGE_NAME    = 'liverecorder/app-web'
        IMAGE_TAG         = "${env.BUILD_ID}"
        DOCKER_CREDS      = 'harbor_key'
        TARGET_PLATFORMS  = 'linux/amd64,linux/arm64'
        BUILDER_NAME      = 'liverecorder-buildx'
        WEB_NODE_IMAGE    = 'docker.m.daocloud.io/library/node:22-alpine'
        WEB_NGINX_IMAGE   = 'docker.m.daocloud.io/library/nginx:1.27-alpine'
        HTTP_PROXY_URL    = 'http://192.168.5.200:7890'
        // Tsinghua/daocloud/mcr hosts are reachable directly from the builder — routing them
        // through the 7890 proxy caused intermittent '502 Bad Gateway' during apt-get install.
        NO_PROXY_HOSTS    = '127.0.0.1,localhost,reg.nxsir.cn,gitea.nxsir.cn,mirrors.tuna.tsinghua.edu.cn,.tsinghua.edu.cn,mcr.microsoft.com,docker.m.daocloud.io'

        GIT_REPO_URL = 'https://gitea.nxsir.cn/nanxun/live_recorder.git'
        GIT_BRANCH   = 'main'
        GIT_CREDS    = ''

        API_IMAGE_TAGGED = "${REGISTRY_URL}/${API_IMAGE_NAME}:${IMAGE_TAG}"
        API_IMAGE_LATEST = "${REGISTRY_URL}/${API_IMAGE_NAME}:latest"
        WEB_IMAGE_TAGGED = "${REGISTRY_URL}/${WEB_IMAGE_NAME}:${IMAGE_TAG}"
        WEB_IMAGE_LATEST = "${REGISTRY_URL}/${WEB_IMAGE_NAME}:latest"
        API_CACHE_IMAGE  = "${REGISTRY_URL}/${API_IMAGE_NAME}:buildcache"
        WEB_CACHE_IMAGE  = "${REGISTRY_URL}/${WEB_IMAGE_NAME}:buildcache"
    }

    stages {
        stage('Checkout') {
            steps {
                script {
                    def userRemoteConfig = [url: env.GIT_REPO_URL]
                    if (env.GIT_CREDS?.trim()) {
                        userRemoteConfig.credentialsId = env.GIT_CREDS.trim()
                    }

                    checkout([
                        $class: 'GitSCM',
                        branches: [[name: "*/${env.GIT_BRANCH}"]],
                        userRemoteConfigs: [userRemoteConfig]
                    ])
                }
            }
        }

        stage('Prepare Buildx') {
            steps {
                sh """
                    set -e
                    sudo docker buildx version

                    # Register ARM64 binfmt. Docker Hub is unreachable from this builder,
                    # so try daocloud mirror first; fall back to apt-installed qemu-user-static.
                    if [ ! -e /proc/sys/fs/binfmt_misc/qemu-aarch64 ] && [ ! -e /proc/sys/fs/binfmt_misc/qemu-aarch64-static ]; then
                      echo 'Registering ARM64 emulation (binfmt not present)...'
                      if ! sudo docker run --privileged --rm docker.m.daocloud.io/tonistiigi/binfmt --install arm64 2>/dev/null; then
                        echo 'daocloud mirror failed, falling back to qemu-user-static via apt...'
                        sudo apt-get update -qq
                        sudo apt-get install -y -qq qemu-user-static
                      fi
                    else
                      echo 'ARM64 binfmt already registered, skipping.'
                    fi

                    # Verify emulation works before proceeding
                    sudo docker run --rm --platform linux/arm64 docker.m.daocloud.io/library/alpine:latest uname -m
                """
            }
        }

        stage('Build And Push API Image') {
            steps {
                withCredentials([
                    usernamePassword(
                        credentialsId: "${DOCKER_CREDS}",
                        usernameVariable: 'DOCKER_USERNAME',
                        passwordVariable: 'DOCKER_PASSWORD'
                    )
                ]) {
                sh """
                    set -e

                    run_with_heartbeat() {
                      log_file="\$1"
                      build_label="\$2"
                      shift 2
                      rm -f "\$log_file"
                      : > "\$log_file"
                      "\$@" >"\$log_file" 2>&1 &
                      cmd_pid=\$!
                      cmd_status=0
                      last_size=0
                      while kill -0 "\$cmd_pid" >/dev/null 2>&1; do
                        echo "[heartbeat] \${build_label} still running at \$(date -u +%Y-%m-%dT%H:%M:%SZ)"
                        if [ -f "\$log_file" ]; then
                          current_size=\$(stat -c%s "\$log_file" 2>/dev/null || echo 0)
                          if [ "\$current_size" -gt "\$last_size" ]; then
                            start_byte=\$((last_size + 1))
                            tail -c +"\$start_byte" "\$log_file" || true
                            last_size=\$current_size
                          fi
                        fi
                        sleep 20
                      done
                      wait "\$cmd_pid" || cmd_status=\$?
                      if [ -f "\$log_file" ]; then
                        current_size=\$(stat -c%s "\$log_file" 2>/dev/null || echo 0)
                        if [ "\$current_size" -gt "\$last_size" ]; then
                          start_byte=\$((last_size + 1))
                          tail -c +"\$start_byte" "\$log_file" || true
                        fi
                      fi
                      if [ "\$cmd_status" -ne 0 ]; then
                        echo "[heartbeat] \${build_label} failed with exit code \$cmd_status"
                      fi
                      return "\$cmd_status"
                    }

                    # ── Build each platform separately, push with docker CLI,
                    #     then assemble a multi-arch manifest.
                    #     buildx --push does not forward auth reliably on this
                    #     builder; per-platform --load + docker push + manifest
                    #     bypasses that entirely.
                    for PLATFORM in linux/amd64 linux/arm64; do
                      ARCH_TAG="\${PLATFORM##*/}"       # amd64 / arm64
                      PLAT_REF="${API_IMAGE_TAGGED}-\${ARCH_TAG}"
                      PLAT_LATEST="${API_IMAGE_LATEST}-\${ARCH_TAG}"

                      echo ''
                      echo "=== Building API for \$PLATFORM -> \$PLAT_REF ==="
                      run_with_heartbeat /tmp/live-recorder-api-\${ARCH_TAG}.log "API \${ARCH_TAG} build" \
                        sudo docker buildx build \
                          --platform "\$PLATFORM" \
                          --network host \
                          --progress=plain \
                          --provenance=false \
                          --cache-from type=registry,ref=${API_CACHE_IMAGE} \
                          --build-arg HTTP_PROXY=${HTTP_PROXY_URL} \
                          --build-arg HTTPS_PROXY=${HTTP_PROXY_URL} \
                          --build-arg NO_PROXY=${NO_PROXY_HOSTS} \
                          --build-arg http_proxy=${HTTP_PROXY_URL} \
                          --build-arg https_proxy=${HTTP_PROXY_URL} \
                          --build-arg no_proxy=${NO_PROXY_HOSTS} \
                          -f src/LiveRecorder.WebApi/Dockerfile \
                          -t "\$PLAT_REF" \
                          -t "\$PLAT_LATEST" \
                          --load \
                          .

                      echo "=== Pushing \$PLAT_REF ==="
                      # Write Harbor auth directly to config.json (bypass docker
                      # login entirely) — then push with fresh baked-in credentials.
                      AUTH_B64=\$(printf '%s:%s' "\$DOCKER_USERNAME" "\$DOCKER_PASSWORD" | base64 -w0)
                      printf '{"auths":{"%s":{"auth":"%s"}}}' "${REGISTRY_URL}" "\$AUTH_B64" | sudo tee /root/.docker/config.json >/dev/null
                      echo "DEBUG config:" && sudo cat /root/.docker/config.json
                      sudo docker push "\$PLAT_REF" 2>&1 | tail -5
                      sudo docker push "\$PLAT_LATEST" 2>&1 | tail -5

                      # Free disk space before the next platform build
                      sudo docker rmi "\$PLAT_REF" "\$PLAT_LATEST" 2>/dev/null || true
                    done

                    echo ''
                    echo "=== Creating multi-arch manifest: ${API_IMAGE_TAGGED} ==="
                    AUTH_B64=\$(printf '%s:%s' "\$DOCKER_USERNAME" "\$DOCKER_PASSWORD" | base64 -w0) && printf '{"auths":{"%s":{"auth":"%s"}}}' "${REGISTRY_URL}" "\$AUTH_B64" | sudo tee /root/.docker/config.json >/dev/null && echo "[config written]"
                    sudo docker manifest create "${API_IMAGE_TAGGED}" \
                      "${API_IMAGE_TAGGED}-amd64" \
                      "${API_IMAGE_TAGGED}-arm64"
                    sudo docker manifest create "${API_IMAGE_LATEST}" \
                      "${API_IMAGE_LATEST}-amd64" \
                      "${API_IMAGE_LATEST}-arm64"

                    echo "=== Pushing manifests ==="
                    sudo docker manifest push "${API_IMAGE_TAGGED}" 2>&1 | tail -3
                    sudo docker manifest push "${API_IMAGE_LATEST}" 2>&1 | tail -3

                    # (Optionally clean up per-platform tags from registry, but harmless to leave them)
                    echo ''
                    echo "API multi-arch images pushed successfully:"
                    echo "  ${API_IMAGE_TAGGED}"
                    echo "  ${API_IMAGE_LATEST}"
                """
                }
            }
        }

        stage('Build And Push Web Image') {
            steps {
                withCredentials([
                    usernamePassword(
                        credentialsId: "${DOCKER_CREDS}",
                        usernameVariable: 'DOCKER_USERNAME',
                        passwordVariable: 'DOCKER_PASSWORD'
                    )
                ]) {
                sh """
                    set -e

                    run_with_heartbeat() {
                      log_file="\$1"
                      build_label="\$2"
                      shift 2
                      rm -f "\$log_file"
                      : > "\$log_file"
                      "\$@" >"\$log_file" 2>&1 &
                      cmd_pid=\$!
                      cmd_status=0
                      last_size=0
                      while kill -0 "\$cmd_pid" >/dev/null 2>&1; do
                        echo "[heartbeat] \${build_label} still running at \$(date -u +%Y-%m-%dT%H:%M:%SZ)"
                        if [ -f "\$log_file" ]; then
                          current_size=\$(stat -c%s "\$log_file" 2>/dev/null || echo 0)
                          if [ "\$current_size" -gt "\$last_size" ]; then
                            start_byte=\$((last_size + 1))
                            tail -c +"\$start_byte" "\$log_file" || true
                            last_size=\$current_size
                          fi
                        fi
                        sleep 20
                      done
                      wait "\$cmd_pid" || cmd_status=\$?
                      if [ -f "\$log_file" ]; then
                        current_size=\$(stat -c%s "\$log_file" 2>/dev/null || echo 0)
                        if [ "\$current_size" -gt "\$last_size" ]; then
                          start_byte=\$((last_size + 1))
                          tail -c +"\$start_byte" "\$log_file" || true
                        fi
                      fi
                      if [ "\$cmd_status" -ne 0 ]; then
                        echo "[heartbeat] \${build_label} failed with exit code \$cmd_status"
                      fi
                      return "\$cmd_status"
                    }

                    for PLATFORM in linux/amd64 linux/arm64; do
                      ARCH_TAG="\${PLATFORM##*/}"
                      PLAT_REF="${WEB_IMAGE_TAGGED}-\${ARCH_TAG}"
                      PLAT_LATEST="${WEB_IMAGE_LATEST}-\${ARCH_TAG}"

                      echo ''
                      echo "=== Building Web for \$PLATFORM -> \$PLAT_REF ==="
                      run_with_heartbeat /tmp/live-recorder-web-\${ARCH_TAG}.log "Web \${ARCH_TAG} build" \
                        sudo docker buildx build \
                          --platform "\$PLATFORM" \
                          --network host \
                          --progress=plain \
                          --provenance=false \
                          --cache-from type=registry,ref=${WEB_CACHE_IMAGE} \
                          --build-arg NODE_IMAGE=${WEB_NODE_IMAGE} \
                          --build-arg NGINX_IMAGE=${WEB_NGINX_IMAGE} \
                          --build-arg HTTP_PROXY=${HTTP_PROXY_URL} \
                          --build-arg HTTPS_PROXY=${HTTP_PROXY_URL} \
                          --build-arg NO_PROXY=${NO_PROXY_HOSTS} \
                          --build-arg http_proxy=${HTTP_PROXY_URL} \
                          --build-arg https_proxy=${HTTP_PROXY_URL} \
                          --build-arg no_proxy=${NO_PROXY_HOSTS} \
                          -f frontend/Dockerfile \
                          --build-arg VITE_API_BASE_URL=/api \
                          -t "\$PLAT_REF" \
                          -t "\$PLAT_LATEST" \
                          --load \
                          frontend

                      echo "=== Pushing \$PLAT_REF ==="
                      AUTH_B64=\$(printf '%s:%s' "\$DOCKER_USERNAME" "\$DOCKER_PASSWORD" | base64 -w0) && printf '{"auths":{"%s":{"auth":"%s"}}}' "${REGISTRY_URL}" "\$AUTH_B64" | sudo tee /root/.docker/config.json >/dev/null && echo "[config written]"
                      sudo docker push "\$PLAT_REF" 2>&1 | tail -3
                      sudo docker push "\$PLAT_LATEST" 2>&1 | tail -3
                      sudo docker rmi "\$PLAT_REF" "\$PLAT_LATEST" 2>/dev/null || true
                    done

                    echo ''
                    echo "=== Creating multi-arch manifest: ${WEB_IMAGE_TAGGED} ==="
                    AUTH_B64=\$(printf '%s:%s' "\$DOCKER_USERNAME" "\$DOCKER_PASSWORD" | base64 -w0) && printf '{"auths":{"%s":{"auth":"%s"}}}' "${REGISTRY_URL}" "\$AUTH_B64" | sudo tee /root/.docker/config.json >/dev/null && echo "[config written]"
                    sudo docker manifest create "${WEB_IMAGE_TAGGED}" \
                      "${WEB_IMAGE_TAGGED}-amd64" \
                      "${WEB_IMAGE_TAGGED}-arm64"
                    sudo docker manifest create "${WEB_IMAGE_LATEST}" \
                      "${WEB_IMAGE_LATEST}-amd64" \
                      "${WEB_IMAGE_LATEST}-arm64"

                    echo "=== Pushing manifests ==="
                    sudo docker manifest push "${WEB_IMAGE_TAGGED}" 2>&1 | tail -3
                    sudo docker manifest push "${WEB_IMAGE_LATEST}" 2>&1 | tail -3

                    echo ''
                    echo "Web multi-arch images pushed successfully:"
                    echo "  ${WEB_IMAGE_TAGGED}"
                    echo "  ${WEB_IMAGE_LATEST}"
                """
                }
            }
        }
    }

    post {
        success {
            echo "Pipeline completed successfully."
        }
        failure {
            echo "Pipeline failed. Please check the build log."
        }
        always {
            sh """
                set +e
                sudo docker logout ${REGISTRY_URL} >/dev/null 2>&1 || true
                true
            """
            deleteDir()
        }
    }
}
